From 894abe127d4b74f7e7d55dcf16a8ec13c51e868b Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 28 Jan 2022 11:35:28 +0100 Subject: [PATCH] Bugfix: Enum default values require a use statement for correct serialization. --- .github/workflows/static-analysis.yml | 2 +- composer.json | 2 +- lib/Doctrine/Common/Proxy/ProxyGenerator.php | 42 ++++++++++++++++++- .../Proxy/Php81EnumPublicPropertyType.php | 14 +++++++ .../Tests/Common/Proxy/ProxyGeneratorTest.php | 22 ++++++++++ 5 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 tests/Doctrine/Tests/Common/Proxy/Php81EnumPublicPropertyType.php diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index e196a5286..9a911787d 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -14,4 +14,4 @@ jobs: name: "Static Analysis" uses: "doctrine/.github/.github/workflows/static-analysis.yml@1.4.0" with: - php-version: "7.4" + php-version: "8.1" diff --git a/composer.json b/composer.json index fd9868eeb..34a67fc54 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "doctrine/persistence": "^2.0" }, "require-dev": { - "phpstan/phpstan": "^1.2.0", + "phpstan/phpstan": "^1.4.1", "phpstan/phpstan-phpunit": "^1", "phpunit/phpunit": "^7.5.20 || ^8.5 || ^9.0", "doctrine/coding-standard": "^9.0", diff --git a/lib/Doctrine/Common/Proxy/ProxyGenerator.php b/lib/Doctrine/Common/Proxy/ProxyGenerator.php index f71cb9159..7aff701c0 100644 --- a/lib/Doctrine/Common/Proxy/ProxyGenerator.php +++ b/lib/Doctrine/Common/Proxy/ProxyGenerator.php @@ -2,6 +2,7 @@ namespace Doctrine\Common\Proxy; +use BackedEnum; use Doctrine\Common\Proxy\Exception\InvalidArgumentException; use Doctrine\Common\Proxy\Exception\UnexpectedValueException; use Doctrine\Common\Util\ClassUtils; @@ -19,6 +20,7 @@ use function array_key_exists; use function array_map; use function array_slice; +use function array_unique; use function assert; use function call_user_func; use function chmod; @@ -27,6 +29,7 @@ use function explode; use function file; use function file_put_contents; +use function get_class; use function implode; use function in_array; use function interface_exists; @@ -53,6 +56,7 @@ use function var_export; use const DIRECTORY_SEPARATOR; +use const PHP_VERSION_ID; /** * This factory is used to generate proxy classes. @@ -98,7 +102,7 @@ class ProxyGenerator protected $proxyClassTemplate = '; - + /** * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE\'S PROXY GENERATOR */ @@ -381,6 +385,42 @@ private function generateNamespace(ClassMetadata $class) return strrev($parts[1]); } + /** + * Enums must have a use statement when used as public property defaults. + */ + public function generateEnumUseStatements(ClassMetadata $class): string + { + if (PHP_VERSION_ID < 80100) { + return "\n"; + } + + $defaultProperties = $class->getReflectionClass()->getDefaultProperties(); + $lazyLoadedPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class); + $enumClasses = []; + + foreach ($class->getReflectionClass()->getProperties(ReflectionProperty::IS_PUBLIC) as $property) { + $name = $property->getName(); + + if (! in_array($name, $lazyLoadedPublicProperties, true)) { + continue; + } + + if (array_key_exists($name, $defaultProperties) && $defaultProperties[$name] instanceof BackedEnum) { + $enumClasses[] = get_class($defaultProperties[$name]); + } + } + + return implode( + "\n", + array_map( + static function ($className) { + return 'use ' . $className . ';'; + }, + array_unique($enumClasses) + ) + ) . "\n"; + } + /** * Generates the original class name. * diff --git a/tests/Doctrine/Tests/Common/Proxy/Php81EnumPublicPropertyType.php b/tests/Doctrine/Tests/Common/Proxy/Php81EnumPublicPropertyType.php new file mode 100644 index 000000000..27bdfeb30 --- /dev/null +++ b/tests/Doctrine/Tests/Common/Proxy/Php81EnumPublicPropertyType.php @@ -0,0 +1,14 @@ += 8.1.0 + */ + public function testEnumDefaultInPublicProperty() : void + { + $className = Php81EnumPublicPropertyType::class; + + if ( ! class_exists('Doctrine\Tests\Common\ProxyProxy\__CG__\Php81EnumPublicPropertyType', false)) { + $metadata = $this->createClassMetadata($className, ['id']); + + $metadata->expects($this->any())->method('hasField')->will($this->returnValue(true)); + + $proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy'); + $this->generateAndRequire($proxyGenerator, $metadata); + } + + $this->assertStringContainsString( + 'use Doctrine\Tests\Common\Proxy\YesOrNo;', + file_get_contents(__DIR__ . '/generated/__CG__DoctrineTestsCommonProxyPhp81EnumPublicPropertyType.php') + ); + } + /** * @param string $className * @param mixed[] $ids