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..89a945b53 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,43 @@ 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) { + $enumClassNameParts = explode('\\', get_class($defaultProperties[$name])); + $enumClasses[] = $enumClassNameParts[0]; + } + } + + return implode( + "\n", + array_map( + static function ($className) { + return 'use ' . $className . ';'; + }, + array_unique($enumClasses) + ) + ) . "\n"; + } + /** * Generates the original class name. * diff --git a/phpstan.neon.dist b/phpstan.neon.dist index d5070c54e..56040fe49 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,5 +1,5 @@ parameters: - phpVersion: 70100 + phpVersion: 80100 level: 3 paths: - lib @@ -19,6 +19,7 @@ parameters: - tests/Doctrine/Tests/Common/Proxy/ProxyLogicTypedPropertiesTest.php - tests/Doctrine/Tests/Common/Proxy/SerializedClass.php - tests/Doctrine/Tests/Common/Proxy/VariadicTypeHintClass.php + - tests/Doctrine/Tests/Common/Proxy/Php71NullableDefaultedNonOptionalHintClass.php - tests/Doctrine/Tests/Common/Proxy/generated ignoreErrors: - '#Access to an undefined property Doctrine\\Common\\Proxy\\Proxy::\$publicField#' @@ -49,6 +50,10 @@ parameters: message: '#^Access to an undefined property Doctrine\\Tests\\Common\\Proxy\\MagicGetByRefClass\:\:\$nonExisting\.$#' path: 'tests/Doctrine/Tests/Common/Proxy/ProxyMagicMethodsTest.php' + - + message: "#^Class Doctrine\\\\Tests\\\\Common\\\\Proxy\\\\MagicIssetClassWithInteger not found\\.$#" + count: 1 + path: tests/Doctrine/Tests/Common/Proxy/ProxyMagicMethodsTest.php includes: - vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-phpunit/rules.neon 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->method('hasField')->will($this->returnValue(true)); + + $proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy'); + $this->generateAndRequire($proxyGenerator, $metadata); + } + + $this->assertStringContainsString( + 'use Doctrine;', + file_get_contents(__DIR__ . '/generated/__CG__DoctrineTestsCommonProxyPhp81EnumPublicPropertyType.php') + ); + + $object = new \Doctrine\Tests\Common\ProxyProxy\__CG__\Doctrine\Tests\Common\Proxy\Php81EnumPublicPropertyType(); + $object = unserialize(serialize($object)); + + $this->assertSame($object->isEnum, \Doctrine\Tests\Common\Proxy\YesOrNo::YES); + } + /** * @param string $className * @param mixed[] $ids