diff --git a/src/Type/Doctrine/QueryBuilder/Expr/BaseExpressionDynamicReturnTypeExtension.php b/src/Type/Doctrine/QueryBuilder/Expr/BaseExpressionDynamicReturnTypeExtension.php index 7dd9729e..cbac5d6f 100644 --- a/src/Type/Doctrine/QueryBuilder/Expr/BaseExpressionDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/QueryBuilder/Expr/BaseExpressionDynamicReturnTypeExtension.php @@ -9,6 +9,7 @@ use PHPStan\Type\Doctrine\ArgumentsProcessor; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Type; +use Throwable; use function get_class; use function in_array; use function is_object; @@ -55,7 +56,12 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return null; } - $exprValue = $expr->{$methodReflection->getName()}(...$args); + try { + $exprValue = $expr->{$methodReflection->getName()}(...$args); + } catch (Throwable $e) { + return null; + } + if (is_object($exprValue)) { return new ExprType(get_class($exprValue), $exprValue); } diff --git a/src/Type/Doctrine/QueryBuilder/Expr/ExpressionBuilderDynamicReturnTypeExtension.php b/src/Type/Doctrine/QueryBuilder/Expr/ExpressionBuilderDynamicReturnTypeExtension.php index a6a6896a..b05cf90c 100644 --- a/src/Type/Doctrine/QueryBuilder/Expr/ExpressionBuilderDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/QueryBuilder/Expr/ExpressionBuilderDynamicReturnTypeExtension.php @@ -11,6 +11,7 @@ use PHPStan\Type\Doctrine\ObjectMetadataResolver; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Type; +use Throwable; use function get_class; use function is_object; use function method_exists; @@ -74,7 +75,12 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return null; } - $exprValue = $expr->{$methodReflection->getName()}(...$args); + try { + $exprValue = $expr->{$methodReflection->getName()}(...$args); + } catch (Throwable $e) { + return null; + } + if (is_object($exprValue)) { return new ExprType(get_class($exprValue), $exprValue); } diff --git a/tests/Rules/Doctrine/ORM/QueryBuilderDqlFirstClassCallableTest.php b/tests/Rules/Doctrine/ORM/QueryBuilderDqlFirstClassCallableTest.php new file mode 100644 index 00000000..4b456ab2 --- /dev/null +++ b/tests/Rules/Doctrine/ORM/QueryBuilderDqlFirstClassCallableTest.php @@ -0,0 +1,36 @@ + + */ +class QueryBuilderDqlFirstClassCallableTest extends RuleTestCase +{ + + protected function getRule(): Rule + { + return new QueryBuilderDqlRule( + new ObjectMetadataResolver(__DIR__ . '/entity-manager.php', __DIR__ . '/../../../../tmp'), + true, + ); + } + + public function testFirstClassCallableInMatchArm(): void + { + $this->analyse([__DIR__ . '/data/query-builder-first-class-callable.php'], []); + } + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../../../extension.neon', + __DIR__ . '/entity-manager.neon', + ]; + } + +} diff --git a/tests/Rules/Doctrine/ORM/data/query-builder-first-class-callable.php b/tests/Rules/Doctrine/ORM/data/query-builder-first-class-callable.php new file mode 100644 index 00000000..bdaa8749 --- /dev/null +++ b/tests/Rules/Doctrine/ORM/data/query-builder-first-class-callable.php @@ -0,0 +1,47 @@ += 8.1 + +namespace PHPStan\Rules\Doctrine\ORM; + +use Doctrine\ORM\EntityManager; + +class TestFirstClassCallableExpr +{ + + /** @var EntityManager */ + private $entityManager; + + public function __construct(EntityManager $entityManager) + { + $this->entityManager = $entityManager; + } + + public function matchArmWithFirstClassCallable(): void + { + $queryBuilder = $this->entityManager->createQueryBuilder(); + $expr = $queryBuilder->expr(); + + $comparator = match (random_int(0, 1)) { + 0 => $expr->in(...), + 1 => $expr->notIn(...), + }; + + $queryBuilder->select('e') + ->from(MyEntity::class, 'e') + ->andWhere($comparator('e.id', [1, 2, 3])); + $queryBuilder->getQuery(); + } + + public function variableWithFirstClassCallable(): void + { + $queryBuilder = $this->entityManager->createQueryBuilder(); + $expr = $queryBuilder->expr(); + + $fn = $expr->eq(...); + + $queryBuilder->select('e') + ->from(MyEntity::class, 'e') + ->andWhere($fn('e.id', '1')); + $queryBuilder->getQuery(); + } + +} diff --git a/tests/Type/Doctrine/data/QueryResult/baseExpressionAndOr.php b/tests/Type/Doctrine/data/QueryResult/baseExpressionAndOr.php index 9fcf71a3..8e5ec066 100644 --- a/tests/Type/Doctrine/data/QueryResult/baseExpressionAndOr.php +++ b/tests/Type/Doctrine/data/QueryResult/baseExpressionAndOr.php @@ -14,3 +14,6 @@ $string = $and->__toString(); assertType("string", $string); + +$invalidAdd = $and->add(123); +assertType("Doctrine\ORM\Query\Expr\Andx", $invalidAdd);