Skip to content

Commit

Permalink
Fix handling of custom Expr objects
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed May 21, 2019
1 parent b6a0076 commit 9fac3a7
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 2 deletions.
7 changes: 7 additions & 0 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,10 @@ services:
arguments:
class: Doctrine\ORM\Query\Expr\OrderBy
argumentsProcessor: @doctrineQueryBuilderArgumentsProcessor
-
class: PHPStan\Type\Doctrine\QueryBuilder\Expr\NewExprDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
arguments:
class: Doctrine\ORM\Query\Expr
argumentsProcessor: @doctrineQueryBuilderArgumentsProcessor
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ parameters:
message: '~^Variable method call on Doctrine\\ORM\\QueryBuilder~'
path: */src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php
-
message: '~^Variable method call on Doctrine\\ORM\\Query\\Expr\.$~'
message: '~^Variable method call on object\.$~'
path: */src/Type/Doctrine/QueryBuilder/Expr/ExpressionBuilderDynamicReturnTypeExtension.php
-
message: '~^Variable property access on PhpParser\\Node\\Stmt\\Declare_\|PhpParser\\Node\\Stmt\\Namespace_\.$~'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,18 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
return $defaultReturnType;
}

$exprValue = $queryBuilder->expr()->{$methodReflection->getName()}(...$args);
$calledOnType = $scope->getType($methodCall->var);
if ($calledOnType instanceof ExprType) {
$expr = $calledOnType->getExprObject();
} else {
$expr = $queryBuilder->expr();
}

if (!method_exists($expr, $methodReflection->getName())) {
return $defaultReturnType;
}

$exprValue = $expr->{$methodReflection->getName()}(...$args);
if (is_object($exprValue)) {
return new ExprType(get_class($exprValue), $exprValue);
}
Expand Down
6 changes: 6 additions & 0 deletions tests/Rules/Doctrine/ORM/QueryBuilderDqlRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace PHPStan\Rules\Doctrine\ORM;

use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\Query\Expr\Base;
use Doctrine\ORM\Query\Expr\OrderBy;
use PHPStan\DependencyInjection\Container;
Expand Down Expand Up @@ -102,6 +103,10 @@ public function testRule(): void
'QueryBuilder: [Semantical Error] line 0, col 60 near \'nonexistent =\': Error: Class PHPStan\Rules\Doctrine\ORM\MyEntity has no field or association named nonexistent',
251,
],
[
"QueryBuilder: [Syntax Error] line 0, col -1: Error: Expected =, <, <=, <>, >, >=, !=, got end of string.\nDQL: SELECT e FROM PHPStan\Rules\Doctrine\ORM\MyEntity e WHERE foo",
281,
],
]);
}

Expand Down Expand Up @@ -210,6 +215,7 @@ public function getDynamicStaticMethodReturnTypeExtensions(): array
return [
new NewExprDynamicReturnTypeExtension($argumentsProcessor, OrderBy::class),
new NewExprDynamicReturnTypeExtension($argumentsProcessor, Base::class),
new NewExprDynamicReturnTypeExtension($argumentsProcessor, Expr::class),
];
}

Expand Down
40 changes: 40 additions & 0 deletions tests/Rules/Doctrine/ORM/data/query-builder-dql.php
Original file line number Diff line number Diff line change
Expand Up @@ -261,4 +261,44 @@ public function anotherWeirdTypeSpecifyingExtensionProblem(): void
$queryBuilder->getQuery();
}

public function qbCustomExprMethod(): void
{
$expr = new CustomExpr();
$queryBuilder = $this->entityManager->createQueryBuilder();
$queryBuilder->select('e')
->from(MyEntity::class, 'e')
->andWhere($expr->correct());
$queryBuilder->getQuery();
}

public function qbCustomExprMethodSyntaxError(): void
{
$expr = new CustomExpr();
$queryBuilder = $this->entityManager->createQueryBuilder();
$queryBuilder->select('e')
->from(MyEntity::class, 'e')
->andWhere($expr->syntaxError());
$queryBuilder->getQuery();
}

}

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

public function __construct()
{
// necessary so that NewExprDynamicReturnTypeExtension works
}

public function syntaxError(): string
{
return 'foo';
}

public function correct(): string
{
return 'e.id = 1';
}

}

0 comments on commit 9fac3a7

Please # to comment.