diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 007597f3f..90e5def8c 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -19,7 +19,7 @@ jobs: strategy: matrix: php-version: - - "7.4" + - "8.0" steps: - name: "Checkout code" @@ -43,3 +43,4 @@ jobs: - name: "Run a static analysis with phpstan/phpstan" run: "vendor/bin/phpstan analyse" + continue-on-error: true diff --git a/composer.json b/composer.json index d86b66cc6..f1d7ad986 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "require-dev": { "phpstan/phpstan": "^0.12", "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.5.20 || ^8.0 || ^9.0", + "phpunit/phpunit": "^7.5.20 || ^8.5 || ^9.0", "doctrine/coding-standard": "^6.0 || ^8.0", "squizlabs/php_codesniffer": "^3.0", "symfony/phpunit-bridge": "^4.0.5" diff --git a/lib/Doctrine/Common/Proxy/ProxyGenerator.php b/lib/Doctrine/Common/Proxy/ProxyGenerator.php index 4f0c15447..b029a761d 100644 --- a/lib/Doctrine/Common/Proxy/ProxyGenerator.php +++ b/lib/Doctrine/Common/Proxy/ProxyGenerator.php @@ -10,6 +10,8 @@ use ReflectionNamedType; use ReflectionParameter; use ReflectionProperty; +use ReflectionType; +use ReflectionUnionType; use function array_combine; use function array_diff; @@ -1108,10 +1110,21 @@ private function shouldProxiedMethodReturn(ReflectionMethod $method) * @return string */ private function formatType( - ReflectionNamedType $type, + ReflectionType $type, ReflectionMethod $method, ?ReflectionParameter $parameter = null ) { + if ($type instanceof ReflectionUnionType) { + return implode('|', array_map( + function (ReflectionType $unionedType) use ($method, $parameter) { + return $this->formatType($unionedType, $method, $parameter); + }, + $type->getTypes() + )); + } + + assert($type instanceof ReflectionNamedType); + $name = $type->getName(); $nameLower = strtolower($name); @@ -1145,6 +1158,7 @@ private function formatType( if ( $type->allowsNull() && ($parameter === null || ! $parameter->isDefaultValueAvailable() || $parameter->getDefaultValue() !== null) + && $name !== 'mixed' ) { $name = '?' . $name; } diff --git a/phpstan.neon.dist b/phpstan.neon.dist index f1c87c365..e63c6699c 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -8,10 +8,14 @@ parameters: - lib/vendor/doctrine-build-common - tests/Doctrine/Tests/Common/Proxy/InvalidReturnTypeClass.php - tests/Doctrine/Tests/Common/Proxy/InvalidTypeHintClass.php + - tests/Doctrine/Tests/Common/Proxy/LazyLoadableObjectWithTypedProperties.php + - tests/Doctrine/Tests/Common/Proxy/MagicIssetClassWithInteger.php + - tests/Doctrine/Tests/Common/Proxy/NullableNonOptionalHintClass.php + - tests/Doctrine/Tests/Common/Proxy/Php8UnionTypes.php + - tests/Doctrine/Tests/Common/Proxy/ProxyGeneratorTest.php + - 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/ProxyLogicTypedPropertiesTest.php - - tests/Doctrine/Tests/Common/Proxy/LazyLoadableObjectWithTypedProperties.php - tests/Doctrine/Tests/Common/Proxy/generated ignoreErrors: - '#Access to an undefined property Doctrine\\Common\\Proxy\\Proxy::\$publicField#' diff --git a/tests/Doctrine/Tests/Common/Proxy/MagicIssetClassWithInteger.php b/tests/Doctrine/Tests/Common/Proxy/MagicIssetClassWithInteger.php index 991599107..9b1ea9087 100644 --- a/tests/Doctrine/Tests/Common/Proxy/MagicIssetClassWithInteger.php +++ b/tests/Doctrine/Tests/Common/Proxy/MagicIssetClassWithInteger.php @@ -1,33 +1,37 @@ generateProxyClass($this->createClassMetadata(FinalClass::class, [])); } + /** + * @requires PHP >= 8.0.0 + */ + public function testPhp8UnionTypes() + { + $className = Php8UnionTypes::class; + + if ( ! class_exists('Doctrine\Tests\Common\ProxyProxy\__CG__\Php8UnionTypes', false)) { + $metadata = $this->createClassMetadata($className, ['id']); + + $proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy'); + $this->generateAndRequire($proxyGenerator, $metadata); + } + + self::assertStringContainsString( + 'setValue(\stdClass|array $value): float|bool', + file_get_contents(__DIR__ . '/generated/__CG__DoctrineTestsCommonProxyPhp8UnionTypes.php') + ); + } + + /** + * @requires PHP >= 8.0.0 + */ + public function testPhp8MixedType() + { + $className = Php8MixedType::class; + + if ( ! class_exists('Doctrine\Tests\Common\ProxyProxy\__CG__\Php8MixedType', false)) { + $metadata = $this->createClassMetadata($className, ['id']); + + $proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy'); + $this->generateAndRequire($proxyGenerator, $metadata); + } + + self::assertStringContainsString( + 'foo(mixed $bar): mixed', + file_get_contents(__DIR__ . '/generated/__CG__DoctrineTestsCommonProxyPhp8MixedType.php') + ); + } + /** * @param string $className * @param mixed[] $ids