From 6baba53f1623e61ed21b277766c57023de37b0c9 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Tue, 21 Nov 2023 10:58:05 +0100 Subject: [PATCH] QueryBuilderGetQueryDynamicReturnTypeExtension: handle regular set usages --- src/Type/Doctrine/ArgumentsProcessor.php | 2 +- ...lderGetQueryDynamicReturnTypeExtension.php | 13 ++++++ .../Doctrine/ORM/QueryBuilderDqlRuleTest.php | 10 +++++ .../ORM/data/query-builder-dql-update.php | 40 +++++++++++++++++++ 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 tests/Rules/Doctrine/ORM/data/query-builder-dql-update.php diff --git a/src/Type/Doctrine/ArgumentsProcessor.php b/src/Type/Doctrine/ArgumentsProcessor.php index c6660524..4a5b316d 100644 --- a/src/Type/Doctrine/ArgumentsProcessor.php +++ b/src/Type/Doctrine/ArgumentsProcessor.php @@ -15,7 +15,7 @@ class ArgumentsProcessor /** * @param Arg[] $methodCallArgs - * @return mixed[] + * @return list * @throws DynamicQueryBuilderArgumentException */ public function processArgs( diff --git a/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php b/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php index db2df49d..6f3efcb5 100644 --- a/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php @@ -24,6 +24,7 @@ use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use Throwable; +use function array_slice; use function count; use function in_array; use function method_exists; @@ -152,6 +153,18 @@ public function getTypeFromMethodCall( continue; } + if ($lowerMethodName === 'set') { + try { + $args = $this->argumentsProcessor->processArgs($scope, $methodName, array_slice($calledMethodCall->getArgs(), 0, 1)); + } catch (DynamicQueryBuilderArgumentException $e) { + return $defaultReturnType; + } + if (count($args) === 1) { + $queryBuilder->set($args[0], $args[0]); + continue; + } + } + if (!method_exists($queryBuilder, $methodName)) { continue; } diff --git a/tests/Rules/Doctrine/ORM/QueryBuilderDqlRuleTest.php b/tests/Rules/Doctrine/ORM/QueryBuilderDqlRuleTest.php index c216a9e4..67b94c8f 100644 --- a/tests/Rules/Doctrine/ORM/QueryBuilderDqlRuleTest.php +++ b/tests/Rules/Doctrine/ORM/QueryBuilderDqlRuleTest.php @@ -129,6 +129,16 @@ public function testAndOrVariants(): void $this->analyse([__DIR__ . '/data/query-builder-dql-and-or-variants.php'], []); } + public function testUpdateIssue(): void + { + $this->analyse([__DIR__ . '/data/query-builder-dql-update.php'], [ + [ + 'Could not analyse QueryBuilder with dynamic arguments.', + 31, + ], + ]); + } + public function testBranchingPerformance(): void { $this->analyse([__DIR__ . '/data/query-builder-branches-performance.php'], [ diff --git a/tests/Rules/Doctrine/ORM/data/query-builder-dql-update.php b/tests/Rules/Doctrine/ORM/data/query-builder-dql-update.php new file mode 100644 index 00000000..6e1db62c --- /dev/null +++ b/tests/Rules/Doctrine/ORM/data/query-builder-dql-update.php @@ -0,0 +1,40 @@ +entityManager = $entityManager; + } + + public function nonDynamicSet(int $int, string $title): void + { + $this->entityManager->createQueryBuilder() + ->update(MyEntity::class, 'e') + ->set('e.title', $title) + ->andWhere('e.id = :int') + ->setParameter('int', $int) + ->getQuery() + ->execute(); + } + + public function dynamicSet(int $int, string $field, string $title): void + { + $this->entityManager->createQueryBuilder() + ->update(MyEntity::class, 'e') + ->set('e.' . $field, $title) + ->andWhere('e.id = :int') + ->setParameter('int', $int) + ->getQuery() + ->execute(); + } + +}