diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index cebcd4e2..a07bef20 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,325 +1,331 @@ parameters: ignoreErrors: - - message: '#^Call to internal method Doctrine\\DBAL\\Connection\:\:getParams\(\) from outside its root namespace Doctrine\.$#' + rawMessage: 'Call to internal method Doctrine\DBAL\Connection::getParams() from outside its root namespace Doctrine.' identifier: method.internal count: 2 path: src/Doctrine/Driver/DriverDetector.php - - message: ''' - #^Access to constant on deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Access to constant on deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: classConstant.deprecatedClass count: 1 path: src/Doctrine/Mapping/ClassMetadataFactory.php - - message: ''' - #^Call to method __construct\(\) of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Call to method __construct() of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: method.deprecatedClass count: 1 path: src/Doctrine/Mapping/ClassMetadataFactory.php - - message: ''' - #^Instantiation of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Instantiation of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: new.deprecatedClass count: 1 path: src/Doctrine/Mapping/ClassMetadataFactory.php - - message: '#^Calling PHPStan\\Type\\TypehintHelper\:\:decideType\(\) is not covered by backward compatibility promise\. The method might change in a minor PHPStan version\.$#' + rawMessage: 'Calling PHPStan\Type\TypehintHelper::decideType() is not covered by backward compatibility promise. The method might change in a minor PHPStan version.' identifier: phpstanApi.method count: 1 path: src/Rules/Doctrine/ORM/EntityColumnRule.php - - message: '#^Calling PHPStan\\Type\\TypehintHelper\:\:decideType\(\) is not covered by backward compatibility promise\. The method might change in a minor PHPStan version\.$#' + rawMessage: 'Calling PHPStan\Type\TypehintHelper::decideType() is not covered by backward compatibility promise. The method might change in a minor PHPStan version.' identifier: phpstanApi.method count: 1 path: src/Rules/Doctrine/ORM/EntityRelationRule.php - - message: '#^PHPDoc tag @var with type class\-string is not subtype of native type ''Doctrine\\\\Bundle…''\.$#' - identifier: varTag.nativeType - count: 1 - path: src/Stubs/Doctrine/StubFilesExtensionLoader.php - - - - message: ''' - #^Catching deprecated class Doctrine\\Common\\CommonException\: - The doctrine/common package is deprecated, please use specific packages and their exceptions instead\.$# + rawMessage: ''' + Catching deprecated class Doctrine\Common\CommonException: + The doctrine/common package is deprecated, please use specific packages and their exceptions instead. ''' identifier: catch.deprecatedClass count: 1 path: src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php - - message: ''' - #^Catching deprecated class Doctrine\\ORM\\ORMException\: - Use Doctrine\\ORM\\Exception\\ORMException for catch and instanceof$# + rawMessage: ''' + Catching deprecated class Doctrine\ORM\ORMException: + Use Doctrine\ORM\Exception\ORMException for catch and instanceof ''' identifier: catch.deprecatedClass count: 1 path: src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php - - message: ''' - #^Access to constant on deprecated class Doctrine\\DBAL\\Types\\ArrayType\: - Use \{@link JsonType\} instead\.$# + rawMessage: ''' + Access to constant on deprecated class Doctrine\DBAL\Types\ArrayType: + Use {@link JsonType} instead. ''' identifier: classConstant.deprecatedClass count: 1 path: src/Type/Doctrine/Descriptors/ArrayType.php - - message: ''' - #^Access to constant on deprecated class Doctrine\\DBAL\\Types\\ObjectType\: - Use \{@link JsonType\} instead\.$# + rawMessage: ''' + Access to constant on deprecated class Doctrine\DBAL\Types\ObjectType: + Use {@link JsonType} instead. ''' identifier: classConstant.deprecatedClass count: 1 path: src/Type/Doctrine/Descriptors/ObjectType.php - - message: '#^Parameter \$condExpr of method PHPStan\\Type\\Doctrine\\Query\\QueryResultTypeWalker\:\:walkConditionalExpression\(\) has typehint with internal interface Doctrine\\ORM\\Query\\AST\\Phase2OptimizableConditional\.$#' + rawMessage: 'Parameter $condExpr of method PHPStan\Type\Doctrine\Query\QueryResultTypeWalker::walkConditionalExpression() has typehint with internal interface Doctrine\ORM\Query\AST\Phase2OptimizableConditional.' identifier: parameter.internalInterface count: 1 path: src/Type/Doctrine/Query/QueryResultTypeWalker.php - - message: '#^Parameter \$condExpr of method PHPStan\\Type\\Doctrine\\Query\\QueryResultTypeWalker\:\:walkJoinAssociationDeclaration\(\) has typehint with internal interface Doctrine\\ORM\\Query\\AST\\Phase2OptimizableConditional\.$#' + rawMessage: 'Parameter $condExpr of method PHPStan\Type\Doctrine\Query\QueryResultTypeWalker::walkJoinAssociationDeclaration() has typehint with internal interface Doctrine\ORM\Query\AST\Phase2OptimizableConditional.' identifier: parameter.internalInterface count: 1 path: src/Type/Doctrine/Query/QueryResultTypeWalker.php - - message: ''' - #^Catching deprecated class Doctrine\\Common\\CommonException\: - The doctrine/common package is deprecated, please use specific packages and their exceptions instead\.$# + rawMessage: ''' + Catching deprecated class Doctrine\Common\CommonException: + The doctrine/common package is deprecated, please use specific packages and their exceptions instead. ''' identifier: catch.deprecatedClass count: 1 path: src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php - - message: ''' - #^Catching deprecated class Doctrine\\ORM\\ORMException\: - Use Doctrine\\ORM\\Exception\\ORMException for catch and instanceof$# + rawMessage: ''' + Catching deprecated class Doctrine\ORM\ORMException: + Use Doctrine\ORM\Exception\ORMException for catch and instanceof ''' identifier: catch.deprecatedClass count: 1 path: src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php - - message: '#^Accessing PHPStan\\Rules\\Classes\\InstantiationRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' + rawMessage: Accessing PHPStan\Analyser\ResultCache\ResultCacheManagerFactory::class is not covered by backward compatibility promise. The class might change in a minor PHPStan version. + identifier: phpstanApi.classConstant + count: 1 + path: tests/Analyser/ResultCacheNoReflectionTest.php + + - + rawMessage: 'Calling PHPStan\Analyser\ResultCache\ResultCacheManager::restore() is not covered by backward compatibility promise. The method might change in a minor PHPStan version.' + identifier: phpstanApi.method + count: 1 + path: tests/Analyser/ResultCacheNoReflectionTest.php + + - + rawMessage: 'Calling PHPStan\Analyser\ResultCache\ResultCacheManagerFactory::create() is not covered by backward compatibility promise. The method might change in a minor PHPStan version.' + identifier: phpstanApi.method + count: 1 + path: tests/Analyser/ResultCacheNoReflectionTest.php + + - + rawMessage: Accessing PHPStan\Rules\Classes\InstantiationRule::class is not covered by backward compatibility promise. The class might change in a minor PHPStan version. identifier: phpstanApi.classConstant count: 1 path: tests/Classes/DoctrineProxyForbiddenClassNamesExtensionTest.php - - message: ''' - #^Call to method __construct\(\) of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Call to method __construct() of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: method.deprecatedClass count: 1 path: tests/Classes/entity-manager.php - - message: ''' - #^Instantiation of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Instantiation of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: new.deprecatedClass count: 1 path: tests/Classes/entity-manager.php - - message: ''' - #^Call to method __construct\(\) of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Call to method __construct() of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: method.deprecatedClass count: 1 path: tests/DoctrineIntegration/ORM/entity-manager.php - - message: ''' - #^Instantiation of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Instantiation of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: new.deprecatedClass count: 1 path: tests/DoctrineIntegration/ORM/entity-manager.php - - message: '#^Call to internal method PHPUnit\\Framework\\TestCase\:\:dataName\(\) from outside its root namespace PHPUnit\.$#' + rawMessage: 'Call to internal method PHPUnit\Framework\TestCase::dataName() from outside its root namespace PHPUnit.' identifier: method.internal - count: 14 + count: 13 path: tests/Platform/QueryResultTypeWalkerFetchTypeMatrixTest.php - - message: ''' - #^Call to method __construct\(\) of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Call to method __construct() of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: method.deprecatedClass count: 1 path: tests/Platform/QueryResultTypeWalkerFetchTypeMatrixTest.php - - message: ''' - #^Instantiation of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Instantiation of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: new.deprecatedClass count: 1 path: tests/Platform/QueryResultTypeWalkerFetchTypeMatrixTest.php - - message: '#^Call to internal method Doctrine\\DBAL\\Driver\\PDO\\Connection\:\:__construct\(\) from outside its root namespace Doctrine\.$#' + rawMessage: 'Call to internal method Doctrine\DBAL\Driver\PDO\Connection::__construct() from outside its root namespace Doctrine.' identifier: method.internal count: 1 path: tests/Platform/UnknownDriver.php - - message: '#^Accessing PHPStan\\Rules\\DeadCode\\UnusedPrivatePropertyRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' + rawMessage: Accessing PHPStan\Rules\DeadCode\UnusedPrivatePropertyRule::class is not covered by backward compatibility promise. The class might change in a minor PHPStan version. identifier: phpstanApi.classConstant count: 1 path: tests/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php - - message: ''' - #^Call to method __construct\(\) of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Call to method __construct() of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: method.deprecatedClass count: 1 path: tests/Rules/DeadCode/entity-manager.php - - message: ''' - #^Instantiation of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Instantiation of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: new.deprecatedClass count: 1 path: tests/Rules/DeadCode/entity-manager.php - - message: ''' - #^Access to constant on deprecated class Doctrine\\DBAL\\Types\\ArrayType\: - Use \{@link JsonType\} instead\.$# + rawMessage: ''' + Access to constant on deprecated class Doctrine\DBAL\Types\ArrayType: + Use {@link JsonType} instead. ''' identifier: classConstant.deprecatedClass count: 1 path: tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php - - message: '#^Accessing PHPStan\\Rules\\Methods\\CallMethodsRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' + rawMessage: Accessing PHPStan\Rules\Methods\CallMethodsRule::class is not covered by backward compatibility promise. The class might change in a minor PHPStan version. identifier: phpstanApi.classConstant count: 1 path: tests/Rules/Doctrine/ORM/MagicRepositoryMethodCallRuleTest.php - - message: '#^Accessing PHPStan\\Rules\\Exceptions\\CatchWithUnthrownExceptionRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' + rawMessage: Accessing PHPStan\Rules\Exceptions\CatchWithUnthrownExceptionRule::class is not covered by backward compatibility promise. The class might change in a minor PHPStan version. identifier: phpstanApi.classConstant count: 1 path: tests/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php - - message: '#^Accessing PHPStan\\Rules\\Exceptions\\TooWideMethodThrowTypeRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' + rawMessage: Accessing PHPStan\Rules\Exceptions\TooWideMethodThrowTypeRule::class is not covered by backward compatibility promise. The class might change in a minor PHPStan version. identifier: phpstanApi.classConstant count: 1 path: tests/Rules/Exceptions/TooWideMethodThrowTypeRuleTest.php - - message: '#^Accessing PHPStan\\Rules\\DeadCode\\UnusedPrivatePropertyRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' + rawMessage: Accessing PHPStan\Rules\DeadCode\UnusedPrivatePropertyRule::class is not covered by backward compatibility promise. The class might change in a minor PHPStan version. identifier: phpstanApi.classConstant count: 1 path: tests/Rules/Properties/MissingGedmoByPhpDocPropertyAssignRuleTest.php - - message: '#^Accessing PHPStan\\Rules\\DeadCode\\UnusedPrivatePropertyRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' + rawMessage: Accessing PHPStan\Rules\DeadCode\UnusedPrivatePropertyRule::class is not covered by backward compatibility promise. The class might change in a minor PHPStan version. identifier: phpstanApi.classConstant count: 1 path: tests/Rules/Properties/MissingGedmoPropertyAssignRuleTest.php - - message: '#^Accessing PHPStan\\Rules\\Properties\\MissingReadOnlyByPhpDocPropertyAssignRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' + rawMessage: Accessing PHPStan\Rules\Properties\MissingReadOnlyByPhpDocPropertyAssignRule::class is not covered by backward compatibility promise. The class might change in a minor PHPStan version. identifier: phpstanApi.classConstant count: 1 path: tests/Rules/Properties/MissingReadOnlyByPhpDocPropertyAssignRuleTest.php - - message: '#^Accessing PHPStan\\Rules\\Properties\\MissingReadOnlyPropertyAssignRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' + rawMessage: Accessing PHPStan\Rules\Properties\MissingReadOnlyPropertyAssignRule::class is not covered by backward compatibility promise. The class might change in a minor PHPStan version. identifier: phpstanApi.classConstant count: 1 path: tests/Rules/Properties/MissingReadOnlyPropertyAssignRuleTest.php - - message: ''' - #^Call to method __construct\(\) of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Call to method __construct() of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: method.deprecatedClass count: 1 path: tests/Rules/Properties/entity-manager.php - - message: ''' - #^Instantiation of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Instantiation of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: new.deprecatedClass count: 1 path: tests/Rules/Properties/entity-manager.php - - message: ''' - #^Call to method __construct\(\) of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Call to method __construct() of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: method.deprecatedClass count: 1 path: tests/Type/Doctrine/DBAL/mysqli.php - - message: ''' - #^Instantiation of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Instantiation of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: new.deprecatedClass count: 1 path: tests/Type/Doctrine/DBAL/mysqli.php - - message: ''' - #^Call to method __construct\(\) of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Call to method __construct() of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: method.deprecatedClass count: 1 path: tests/Type/Doctrine/DBAL/pdo.php - - message: ''' - #^Instantiation of deprecated class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\: - This class will be removed in 3\.0 without replacement\.$# + rawMessage: ''' + Instantiation of deprecated class Doctrine\ORM\Mapping\Driver\AnnotationDriver: + This class will be removed in 3.0 without replacement. ''' identifier: new.deprecatedClass count: 1 path: tests/Type/Doctrine/DBAL/pdo.php - - - - message: '#^Parameter references internal interface Doctrine\\ORM\\Query\\AST\\Phase2OptimizableConditional in its type\.$#' - identifier: parameter.internalInterface - count: 2 - path: src/Type/Doctrine/Query/QueryResultTypeWalker.php diff --git a/src/Stubs/Doctrine/StubFilesExtensionLoader.php b/src/Stubs/Doctrine/StubFilesExtensionLoader.php index fc4b48aa..f8dcc37d 100644 --- a/src/Stubs/Doctrine/StubFilesExtensionLoader.php +++ b/src/Stubs/Doctrine/StubFilesExtensionLoader.php @@ -4,25 +4,15 @@ use Composer\InstalledVersions; use OutOfBoundsException; -use PHPStan\BetterReflection\Reflector\Exception\IdentifierNotFound; -use PHPStan\BetterReflection\Reflector\Reflector; use PHPStan\PhpDoc\StubFilesExtension; use function class_exists; use function dirname; use function strpos; +use function version_compare; class StubFilesExtensionLoader implements StubFilesExtension { - private Reflector $reflector; - - public function __construct( - Reflector $reflector - ) - { - $this->reflector = $reflector; - } - public function getFiles(): array { $stubsDir = dirname(dirname(dirname(__DIR__))) . '/stubs'; @@ -36,20 +26,7 @@ public function getFiles(): array $files[] = $stubsDir . '/DBAL/Connection.stub'; } - $hasLazyServiceEntityRepositoryAsParent = false; - - try { - $serviceEntityRepository = $this->reflector->reflectClass('Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository'); - if ($serviceEntityRepository->getParentClass() !== null) { - /** @var class-string $lazyServiceEntityRepositoryName */ - $lazyServiceEntityRepositoryName = 'Doctrine\Bundle\DoctrineBundle\Repository\LazyServiceEntityRepository'; - $hasLazyServiceEntityRepositoryAsParent = $serviceEntityRepository->getParentClass()->getName() === $lazyServiceEntityRepositoryName; - } - } catch (IdentifierNotFound $e) { - // pass - } - - if ($hasLazyServiceEntityRepositoryAsParent) { + if ($this->hasLazyServiceEntityRepository()) { $files[] = $stubsDir . '/LazyServiceEntityRepository.stub'; } else { $files[] = $stubsDir . '/ServiceEntityRepository.stub'; @@ -73,6 +50,25 @@ public function getFiles(): array return $files; } + private function hasLazyServiceEntityRepository(): bool + { + if (!class_exists(InstalledVersions::class)) { + return false; + } + + try { + $bundleVersion = InstalledVersions::getVersion('doctrine/doctrine-bundle'); + } catch (OutOfBoundsException $e) { + return false; + } + + if ($bundleVersion === null) { + return false; + } + + return version_compare($bundleVersion, '2.8.1', '>=') && version_compare($bundleVersion, '3.0.0', '<'); + } + private function isInstalledVersion(string $package, int $majorVersion): bool { if (!class_exists(InstalledVersions::class)) { diff --git a/tests/Analyser/ResultCacheNoReflectionTest.php b/tests/Analyser/ResultCacheNoReflectionTest.php new file mode 100644 index 00000000..49181757 --- /dev/null +++ b/tests/Analyser/ResultCacheNoReflectionTest.php @@ -0,0 +1,36 @@ +getByType(ResultCacheManagerFactory::class)->create([]); + $manager->restore([], true, false, null, $this->createMock(Output::class)); + + // Sanity check: verify ThrowingSourceLocator is actually wired up. + // If restore() had triggered the source locator, the test would have already failed above. + $this->expectException(LogicException::class); + $this->expectExceptionMessage('must not be called during result cache construction'); + $container->getByType(Reflector::class)->reflectClass(self::class); + } + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + __DIR__ . '/result-cache-no-reflection-test.neon', + ]; + } + +} diff --git a/tests/Analyser/ThrowingSourceLocator.php b/tests/Analyser/ThrowingSourceLocator.php new file mode 100644 index 00000000..ff839f72 --- /dev/null +++ b/tests/Analyser/ThrowingSourceLocator.php @@ -0,0 +1,25 @@ +extension = new DoctrineSelectableDynamicReturnTypeExtension(); + $this->extension = self::getContainer()->getByType(DoctrineSelectableDynamicReturnTypeExtension::class); } /**