Skip to content

Commit b3e7d2d

Browse files
committed
Revert "Infer QueryBuilderType for any method returning QueryBuilder"
This reverts commit a9d0aaf.
1 parent 0edf5b0 commit b3e7d2d

13 files changed

+119
-266
lines changed

Diff for: extension.neon

-5
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,6 @@ services:
169169
descendIntoOtherMethods: %doctrine.searchOtherMethodsForQueryBuilderBeginning%
170170
parser: @defaultAnalysisParser
171171

172-
-
173-
class: PHPStan\Type\Doctrine\QueryBuilder\ReturnQueryBuilderExpressionTypeResolverExtension
174-
tags:
175-
- phpstan.broker.expressionTypeResolverExtension
176-
177172
-
178173
class: PHPStan\Stubs\Doctrine\StubFilesExtensionLoader
179174
tags:

Diff for: rules.neon

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ services:
3535
class: PHPStan\Rules\Doctrine\ORM\QueryBuilderDqlRule
3636
arguments:
3737
reportDynamicQueryBuilders: %doctrine.reportDynamicQueryBuilders%
38+
searchOtherMethodsForQueryBuilderBeginning: %doctrine.searchOtherMethodsForQueryBuilderBeginning%
3839
tags:
3940
- phpstan.rules.rule
4041
-

Diff for: src/Rules/Doctrine/ORM/QueryBuilderDqlRule.php

+20-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use PHPStan\Rules\RuleErrorBuilder;
1313
use PHPStan\Type\Doctrine\DoctrineTypeUtils;
1414
use PHPStan\Type\Doctrine\ObjectMetadataResolver;
15+
use PHPStan\Type\Doctrine\QueryBuilder\OtherMethodQueryBuilderParser;
1516
use PHPStan\Type\ObjectType;
1617
use PHPStan\Type\TypeUtils;
1718
use Throwable;
@@ -31,13 +32,23 @@ class QueryBuilderDqlRule implements Rule
3132
/** @var bool */
3233
private $reportDynamicQueryBuilders;
3334

35+
/** @var OtherMethodQueryBuilderParser */
36+
private $otherMethodQueryBuilderParser;
37+
38+
/** @var bool */
39+
private $searchOtherMethodsForQueryBuilderBeginning;
40+
3441
public function __construct(
3542
ObjectMetadataResolver $objectMetadataResolver,
36-
bool $reportDynamicQueryBuilders
43+
OtherMethodQueryBuilderParser $otherMethodQueryBuilderParser,
44+
bool $reportDynamicQueryBuilders,
45+
bool $searchOtherMethodsForQueryBuilderBeginning
3746
)
3847
{
3948
$this->objectMetadataResolver = $objectMetadataResolver;
49+
$this->otherMethodQueryBuilderParser = $otherMethodQueryBuilderParser;
4050
$this->reportDynamicQueryBuilders = $reportDynamicQueryBuilders;
51+
$this->searchOtherMethodsForQueryBuilderBeginning = $searchOtherMethodsForQueryBuilderBeginning;
4152
}
4253

4354
public function getNodeType(): string
@@ -58,6 +69,14 @@ public function processNode(Node $node, Scope $scope): array
5869
$calledOnType = $scope->getType($node->var);
5970
$queryBuilderTypes = DoctrineTypeUtils::getQueryBuilderTypes($calledOnType);
6071
if (count($queryBuilderTypes) === 0) {
72+
73+
if ($this->searchOtherMethodsForQueryBuilderBeginning) {
74+
$queryBuilderTypes = $this->otherMethodQueryBuilderParser->getQueryBuilderTypes($scope, $node);
75+
if (count($queryBuilderTypes) !== 0) {
76+
return [];
77+
}
78+
}
79+
6180
if (
6281
$this->reportDynamicQueryBuilders
6382
&& (new ObjectType('Doctrine\ORM\QueryBuilder'))->isSuperTypeOf($calledOnType)->yes()

Diff for: src/Type/Doctrine/QueryBuilder/OtherMethodQueryBuilderParser.php

+41-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace PHPStan\Type\Doctrine\QueryBuilder;
44

55
use PhpParser\Node;
6+
use PhpParser\Node\Expr\MethodCall;
7+
use PhpParser\Node\Identifier;
68
use PhpParser\Node\Stmt;
79
use PhpParser\Node\Stmt\Class_;
810
use PhpParser\Node\Stmt\ClassMethod;
@@ -15,12 +17,13 @@
1517
use PHPStan\Analyser\ScopeFactory;
1618
use PHPStan\DependencyInjection\Container;
1719
use PHPStan\Parser\Parser;
18-
use PHPStan\Reflection\MethodReflection;
20+
use PHPStan\Reflection\ReflectionProvider;
1921
use PHPStan\Type\Generic\TemplateTypeMap;
2022
use PHPStan\Type\IntersectionType;
2123
use PHPStan\Type\Type;
2224
use PHPStan\Type\TypeTraverser;
2325
use PHPStan\Type\UnionType;
26+
use function count;
2427
use function is_array;
2528

2629
class OtherMethodQueryBuilderParser
@@ -29,28 +32,61 @@ class OtherMethodQueryBuilderParser
2932
/** @var bool */
3033
private $descendIntoOtherMethods;
3134

35+
/** @var ReflectionProvider */
36+
private $reflectionProvider;
37+
3238
/** @var Parser */
3339
private $parser;
3440

3541
/** @var Container */
3642
private $container;
3743

38-
public function __construct(bool $descendIntoOtherMethods, Parser $parser, Container $container)
44+
public function __construct(bool $descendIntoOtherMethods, ReflectionProvider $reflectionProvider, Parser $parser, Container $container)
3945
{
4046
$this->descendIntoOtherMethods = $descendIntoOtherMethods;
47+
$this->reflectionProvider = $reflectionProvider;
4148
$this->parser = $parser;
4249
$this->container = $container;
4350
}
4451

4552
/**
46-
* @return list<QueryBuilderType>
53+
* @return QueryBuilderType[]
4754
*/
48-
public function findQueryBuilderTypesInCalledMethod(Scope $scope, MethodReflection $methodReflection): array
55+
public function getQueryBuilderTypes(Scope $scope, MethodCall $methodCall): array
4956
{
50-
if (!$this->descendIntoOtherMethods) {
57+
if (!$this->descendIntoOtherMethods || !$methodCall->var instanceof MethodCall) {
58+
return [];
59+
}
60+
61+
return $this->findQueryBuilderTypesInCalledMethod($scope, $methodCall->var);
62+
}
63+
/**
64+
* @return QueryBuilderType[]
65+
*/
66+
private function findQueryBuilderTypesInCalledMethod(Scope $scope, MethodCall $methodCall): array
67+
{
68+
$methodCalledOnType = $scope->getType($methodCall->var);
69+
if (!$methodCall->name instanceof Identifier) {
70+
return [];
71+
}
72+
73+
$methodCalledOnTypeClassNames = $methodCalledOnType->getObjectClassNames();
74+
75+
if (count($methodCalledOnTypeClassNames) !== 1) {
76+
return [];
77+
}
78+
79+
if (!$this->reflectionProvider->hasClass($methodCalledOnTypeClassNames[0])) {
80+
return [];
81+
}
82+
83+
$classReflection = $this->reflectionProvider->getClass($methodCalledOnTypeClassNames[0]);
84+
$methodName = $methodCall->name->toString();
85+
if (!$classReflection->hasNativeMethod($methodName)) {
5186
return [];
5287
}
5388

89+
$methodReflection = $classReflection->getNativeMethod($methodName);
5490
$fileName = $methodReflection->getDeclaringClass()->getFileName();
5591
if ($fileName === null) {
5692
return [];

Diff for: src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php

+10-2
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,22 @@ class QueryBuilderGetQueryDynamicReturnTypeExtension implements DynamicMethodRet
6666
/** @var DescriptorRegistry */
6767
private $descriptorRegistry;
6868

69+
/** @var OtherMethodQueryBuilderParser */
70+
private $otherMethodQueryBuilderParser;
71+
6972
public function __construct(
7073
ObjectMetadataResolver $objectMetadataResolver,
7174
ArgumentsProcessor $argumentsProcessor,
7275
?string $queryBuilderClass,
73-
DescriptorRegistry $descriptorRegistry
76+
DescriptorRegistry $descriptorRegistry,
77+
OtherMethodQueryBuilderParser $otherMethodQueryBuilderParser
7478
)
7579
{
7680
$this->objectMetadataResolver = $objectMetadataResolver;
7781
$this->argumentsProcessor = $argumentsProcessor;
7882
$this->queryBuilderClass = $queryBuilderClass;
7983
$this->descriptorRegistry = $descriptorRegistry;
84+
$this->otherMethodQueryBuilderParser = $otherMethodQueryBuilderParser;
8085
}
8186

8287
public function getClass(): string
@@ -103,7 +108,10 @@ public function getTypeFromMethodCall(
103108
)->getReturnType();
104109
$queryBuilderTypes = DoctrineTypeUtils::getQueryBuilderTypes($calledOnType);
105110
if (count($queryBuilderTypes) === 0) {
106-
return $defaultReturnType;
111+
$queryBuilderTypes = $this->otherMethodQueryBuilderParser->getQueryBuilderTypes($scope, $methodCall);
112+
if (count($queryBuilderTypes) === 0) {
113+
return $defaultReturnType;
114+
}
107115
}
108116

109117
$objectManager = $this->objectMetadataResolver->getObjectManager();

Diff for: src/Type/Doctrine/QueryBuilder/QueryBuilderMethodDynamicReturnTypeExtension.php

+10-2
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,16 @@ class QueryBuilderMethodDynamicReturnTypeExtension implements DynamicMethodRetur
2626
/** @var string|null */
2727
private $queryBuilderClass;
2828

29+
/** @var OtherMethodQueryBuilderParser */
30+
private $otherMethodQueryBuilderParser;
31+
2932
public function __construct(
30-
?string $queryBuilderClass
33+
?string $queryBuilderClass,
34+
OtherMethodQueryBuilderParser $otherMethodQueryBuilderParser
3135
)
3236
{
3337
$this->queryBuilderClass = $queryBuilderClass;
38+
$this->otherMethodQueryBuilderParser = $otherMethodQueryBuilderParser;
3439
}
3540

3641
public function getClass(): string
@@ -69,7 +74,10 @@ public function getTypeFromMethodCall(
6974

7075
$queryBuilderTypes = DoctrineTypeUtils::getQueryBuilderTypes($calledOnType);
7176
if (count($queryBuilderTypes) === 0) {
72-
return $calledOnType;
77+
$queryBuilderTypes = $this->otherMethodQueryBuilderParser->getQueryBuilderTypes($scope, $methodCall);
78+
if (count($queryBuilderTypes) === 0) {
79+
return $calledOnType;
80+
}
7381
}
7482

7583
if (count($queryBuilderTypes) > self::MAX_COMBINATIONS) {

Diff for: src/Type/Doctrine/QueryBuilder/ReturnQueryBuilderExpressionTypeResolverExtension.php

-103
This file was deleted.

Diff for: tests/Rules/Doctrine/ORM/QueryBuilderDqlRuleSlowTest.php

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use PHPStan\Rules\Rule;
66
use PHPStan\Testing\RuleTestCase;
77
use PHPStan\Type\Doctrine\ObjectMetadataResolver;
8+
use PHPStan\Type\Doctrine\QueryBuilder\OtherMethodQueryBuilderParser;
89

910
/**
1011
* @extends RuleTestCase<QueryBuilderDqlRule>
@@ -16,6 +17,8 @@ protected function getRule(): Rule
1617
{
1718
return new QueryBuilderDqlRule(
1819
new ObjectMetadataResolver(__DIR__ . '/entity-manager.php', __DIR__ . '/../../../../tmp'),
20+
self::getContainer()->getByType(OtherMethodQueryBuilderParser::class),
21+
true,
1922
true
2023
);
2124
}

Diff for: tests/Rules/Doctrine/ORM/QueryBuilderDqlRuleTest.php

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use PHPStan\Rules\Rule;
66
use PHPStan\Testing\RuleTestCase;
77
use PHPStan\Type\Doctrine\ObjectMetadataResolver;
8+
use PHPStan\Type\Doctrine\QueryBuilder\OtherMethodQueryBuilderParser;
89

910
/**
1011
* @extends RuleTestCase<QueryBuilderDqlRule>
@@ -16,6 +17,8 @@ protected function getRule(): Rule
1617
{
1718
return new QueryBuilderDqlRule(
1819
new ObjectMetadataResolver(__DIR__ . '/entity-manager.php', __DIR__ . '/../../../../tmp'),
20+
self::getContainer()->getByType(OtherMethodQueryBuilderParser::class),
21+
true,
1922
true
2023
);
2124
}

Diff for: tests/Rules/Doctrine/ORM/data/query-builder-dql.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,14 @@ public function selectArray(): void
8484
->getQuery();
8585
}
8686

87-
public function analyseQueryBuilderOtherMethodBeginning(): void
87+
public function analyseQueryBuilderUnknownBeginning(): void
8888
{
8989
$this->createQb()->getQuery();
9090
}
9191

9292
private function createQb(): \Doctrine\ORM\QueryBuilder
9393
{
94-
return $this->entityManager->createQueryBuilder()->select('e')->from(MyEntity::class, 'e');
94+
return $this->entityManager->createQueryBuilder();
9595
}
9696

9797
public function analyseQueryBuilderDynamicArgs(string $entity): void

0 commit comments

Comments
 (0)