From a93497a1a63da391eeaee405f742ee02f37cce7a Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 23 Sep 2026 13:32:02 +0200 Subject: [PATCH] [doctrine] Scope RequireQueryBuilderOnRepository to repository classes only --- .../RequireQueryBuilderOnRepositoryRule.php | 20 +++++++++++++++++++ .../Fixture/ReportOnDocumentManager.php | 3 ++- .../Fixture/ReportOnEntityManager.php | 4 ++-- .../Fixture/SkipNonRepositoryClass.php | 15 ++++++++++++++ ...equireQueryBuilderOnRepositoryRuleTest.php | 3 ++- 5 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/Fixture/SkipNonRepositoryClass.php diff --git a/src/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule.php b/src/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule.php index 7b2b80eda..6cd44c45d 100644 --- a/src/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule.php +++ b/src/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule.php @@ -7,6 +7,7 @@ use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; +use PHPStan\Reflection\ClassReflection; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\ObjectType; @@ -38,6 +39,11 @@ public function processNode(Node $node, Scope $scope): array return []; } + // only relevant inside repository classes, where $this->createQueryBuilder() is available as a safe swap + if (! $this->isInsideRepositoryClass($scope)) { + return []; + } + $callerType = $scope->getType($node->var); if ($this->isValidRepositoryObjectType($callerType)) { return []; @@ -50,6 +56,20 @@ public function processNode(Node $node, Scope $scope): array return [$identifierRuleError]; } + private function isInsideRepositoryClass(Scope $scope): bool + { + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + if ($classReflection->isSubclassOf(DoctrineClass::ENTITY_REPOSITORY)) { + return true; + } + + return $classReflection->isSubclassOf(DoctrineClass::DOCUMENT_REPOSITORY); + } + private function isValidRepositoryObjectType(Type $type): bool { if ($type instanceof UnionType) { diff --git a/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/Fixture/ReportOnDocumentManager.php b/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/Fixture/ReportOnDocumentManager.php index b9bedd307..3586066ad 100644 --- a/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/Fixture/ReportOnDocumentManager.php +++ b/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/Fixture/ReportOnDocumentManager.php @@ -5,8 +5,9 @@ namespace Symplify\PHPStanRules\Tests\Rules\Doctrine\RequireQueryBuilderOnRepositoryRule\Fixture; use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Repository\DocumentRepository; -final class ReportOnDocumentManager +final class ReportOnDocumentManager extends DocumentRepository { public function process(DocumentManager $documentManager) { diff --git a/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/Fixture/ReportOnEntityManager.php b/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/Fixture/ReportOnEntityManager.php index 55352a7ca..4cc87b02e 100644 --- a/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/Fixture/ReportOnEntityManager.php +++ b/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/Fixture/ReportOnEntityManager.php @@ -5,9 +5,9 @@ namespace Symplify\PHPStanRules\Tests\Rules\Doctrine\RequireQueryBuilderOnRepositoryRule\Fixture; use Doctrine\ORM\EntityManagerInterface; -use Symplify\PHPStanRules\Tests\Rules\Doctrine\RequireQueryBuilderOnRepositoryRule\Source\RandomEntity; +use Doctrine\ORM\EntityRepository; -final class ReportOnEntityManager +final class ReportOnEntityManager extends EntityRepository { public function process(EntityManagerInterface $entityManager) { diff --git a/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/Fixture/SkipNonRepositoryClass.php b/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/Fixture/SkipNonRepositoryClass.php new file mode 100644 index 000000000..9c95adde8 --- /dev/null +++ b/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/Fixture/SkipNonRepositoryClass.php @@ -0,0 +1,15 @@ +createQueryBuilder(); + } +} diff --git a/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/RequireQueryBuilderOnRepositoryRuleTest.php b/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/RequireQueryBuilderOnRepositoryRuleTest.php index a54007481..ae9c38a79 100644 --- a/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/RequireQueryBuilderOnRepositoryRuleTest.php +++ b/tests/Rules/Doctrine/RequireQueryBuilderOnRepositoryRule/RequireQueryBuilderOnRepositoryRuleTest.php @@ -29,13 +29,14 @@ public static function provideData(): Iterator yield [__DIR__ . '/Fixture/SkipCreateQueryBuilderOnRepository.php', []]; yield [__DIR__ . '/Fixture/SkipDocumentRepository.php', []]; yield [__DIR__ . '/Fixture/SkipConnection.php', []]; + yield [__DIR__ . '/Fixture/SkipNonRepositoryClass.php', []]; yield [__DIR__ . '/Fixture/ReportOnEntityManager.php', [ [RequireQueryBuilderOnRepositoryRule::ERROR_MESSAGE, 14], ]]; yield [__DIR__ . '/Fixture/ReportOnDocumentManager.php', [ - [RequireQueryBuilderOnRepositoryRule::ERROR_MESSAGE, 13], + [RequireQueryBuilderOnRepositoryRule::ERROR_MESSAGE, 14], ]]; }