Skip to content

Commit 4bd03bf

Browse files
committed
Merge remote-tracking branch 'origin/1.3.x' into 1.4.x
2 parents 4ec70ff + 5c0ba09 commit 4bd03bf

File tree

6 files changed

+140
-1
lines changed

6 files changed

+140
-1
lines changed

extension.neon

+6-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,12 @@ services:
150150
argumentsProcessor: @doctrineQueryBuilderArgumentsProcessor
151151
tags:
152152
- phpstan.broker.dynamicMethodReturnTypeExtension
153-
153+
-
154+
class: PHPStan\Type\Doctrine\QueryBuilder\Expr\BaseExpressionDynamicReturnTypeExtension
155+
arguments:
156+
argumentsProcessor: @doctrineQueryBuilderArgumentsProcessor
157+
tags:
158+
- phpstan.broker.dynamicMethodReturnTypeExtension
154159
-
155160
class: PHPStan\Rules\Doctrine\ORM\PropertiesExtension
156161
tags:

phpstan.neon

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ parameters:
2828
-
2929
message: '~^Variable method call on object\.$~'
3030
path: src/Type/Doctrine/QueryBuilder/Expr/ExpressionBuilderDynamicReturnTypeExtension.php
31+
-
32+
message: '~^Variable method call on object\.$~'
33+
path: src/Type/Doctrine/QueryBuilder/Expr/BaseExpressionDynamicReturnTypeExtension.php
3134
-
3235
message: '~^Variable property access on PhpParser\\Node\\Stmt\\Declare_\|PhpParser\\Node\\Stmt\\Namespace_\.$~'
3336
path: src/Type/Doctrine/QueryBuilder/OtherMethodQueryBuilderParser.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Doctrine\QueryBuilder\Expr;
4+
5+
use PhpParser\Node\Expr\MethodCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\MethodReflection;
8+
use PHPStan\Reflection\ParametersAcceptorSelector;
9+
use PHPStan\Rules\Doctrine\ORM\DynamicQueryBuilderArgumentException;
10+
use PHPStan\Type\Doctrine\ArgumentsProcessor;
11+
use PHPStan\Type\DynamicMethodReturnTypeExtension;
12+
use PHPStan\Type\Type;
13+
use function get_class;
14+
use function is_object;
15+
use function method_exists;
16+
17+
class BaseExpressionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
18+
{
19+
20+
/** @var ArgumentsProcessor */
21+
private $argumentsProcessor;
22+
23+
public function __construct(
24+
ArgumentsProcessor $argumentsProcessor
25+
)
26+
{
27+
$this->argumentsProcessor = $argumentsProcessor;
28+
}
29+
30+
public function getClass(): string
31+
{
32+
return 'Doctrine\ORM\Query\Expr\Base';
33+
}
34+
35+
public function isMethodSupported(MethodReflection $methodReflection): bool
36+
{
37+
return true;
38+
}
39+
40+
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
41+
{
42+
$defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType();
43+
44+
try {
45+
$args = $this->argumentsProcessor->processArgs($scope, $methodReflection->getName(), $methodCall->getArgs());
46+
} catch (DynamicQueryBuilderArgumentException $e) {
47+
return $defaultReturnType;
48+
}
49+
50+
$calledOnType = $scope->getType($methodCall->var);
51+
if (!$calledOnType instanceof ExprType) {
52+
return $defaultReturnType;
53+
}
54+
55+
$expr = $calledOnType->getExprObject();
56+
57+
if (!method_exists($expr, $methodReflection->getName())) {
58+
return $defaultReturnType;
59+
}
60+
61+
$exprValue = $expr->{$methodReflection->getName()}(...$args);
62+
if (is_object($exprValue)) {
63+
return new ExprType(get_class($exprValue), $exprValue);
64+
}
65+
66+
return $scope->getTypeFromValue($exprValue);
67+
}
68+
69+
}

tests/Rules/Doctrine/ORM/QueryBuilderDqlRuleTest.php

+5
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ public function testRuleBranches(): void
124124
$this->analyse([__DIR__ . '/data/query-builder-branches-dql.php'], $errors);
125125
}
126126

127+
public function testAndOrVariants(): void
128+
{
129+
$this->analyse([__DIR__ . '/data/query-builder-dql-and-or-variants.php'], []);
130+
}
131+
127132
public function testBranchingPerformance(): void
128133
{
129134
$this->analyse([__DIR__ . '/data/query-builder-branches-performance.php'], [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Doctrine\ORM;
4+
5+
use Doctrine\ORM\EntityManager;
6+
7+
class TestAndxWithConsequentMethodCall
8+
{
9+
10+
/** @var EntityManager */
11+
private $entityManager;
12+
13+
public function __construct(EntityManager $entityManager)
14+
{
15+
$this->entityManager = $entityManager;
16+
}
17+
18+
public function emptyConstructorConsequentCall(): void
19+
{
20+
$expr = (new \Doctrine\ORM\Query\Expr\Andx())->add('1 = 1');
21+
$queryBuilder = $this->entityManager->createQueryBuilder();
22+
$queryBuilder->select('e')
23+
->from(MyEntity::class, 'e')
24+
->andWhere($expr);
25+
$queryBuilder->getQuery();
26+
}
27+
28+
public function usedConstructor(): void
29+
{
30+
$expr = new \Doctrine\ORM\Query\Expr\Andx('1 = 1');
31+
$queryBuilder = $this->entityManager->createQueryBuilder();
32+
$queryBuilder->select('e')
33+
->from(MyEntity::class, 'e')
34+
->andWhere($expr);
35+
$queryBuilder->getQuery();
36+
}
37+
38+
public function createdFromQueryBuilder(): void
39+
{
40+
$queryBuilder = $this->entityManager->createQueryBuilder();
41+
$queryBuilder->select('e')
42+
->from(MyEntity::class, 'e')
43+
->andWhere($queryBuilder->expr()->andX('1 = 1'));
44+
$queryBuilder->getQuery();
45+
}
46+
47+
}

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

+10
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,16 @@ public function qbCustomExprMethodSyntaxError(): void
281281
$queryBuilder->getQuery();
282282
}
283283

284+
public function qbExprMethod(): void
285+
{
286+
$expr = (new \Doctrine\ORM\Query\Expr\Andx())->add('1 = 1');
287+
$queryBuilder = $this->entityManager->createQueryBuilder();
288+
$queryBuilder->select('e')
289+
->from(MyEntity::class, 'e')
290+
->andWhere($expr);
291+
$queryBuilder->getQuery();
292+
}
293+
284294
}
285295

286296
class CustomExpr extends \Doctrine\ORM\Query\Expr

0 commit comments

Comments
 (0)