From 0d06dc4241e08531a5f4fe26127607879fdf3b71 Mon Sep 17 00:00:00 2001 From: Sascha Egerer Date: Wed, 22 May 2024 15:16:09 +0200 Subject: [PATCH] Fix return type of QueryInterface::execute() The execute mehtod has a boolean argument that defines if a raw result should be returned. This means that no object mapping is done but an array containing the result values is returned. It is now also supported to not just pass constant boolean values but a variable containing a boolean to the execute function. In this case it's not clear if a raw result or objects should be returned so we do now return a union type containing both. --- ...eryInterfaceDynamicReturnTypeExtension.php | 20 ++++++++++++----- stubs/QueryInterface.stub | 2 +- tests/Unit/Type/data/custom-query-type.php | 22 ++++++++++++++----- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/Type/QueryInterfaceDynamicReturnTypeExtension.php b/src/Type/QueryInterfaceDynamicReturnTypeExtension.php index e18ab79..2d90fe1 100644 --- a/src/Type/QueryInterfaceDynamicReturnTypeExtension.php +++ b/src/Type/QueryInterfaceDynamicReturnTypeExtension.php @@ -6,6 +6,7 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; use PHPStan\Type\ArrayType; +use PHPStan\Type\BooleanType; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\ErrorType; @@ -13,7 +14,9 @@ use PHPStan\Type\IntegerType; use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; +use PHPStan\Type\StringType; use PHPStan\Type\Type; +use PHPStan\Type\TypeCombinator; use SaschaEgerer\PhpstanTypo3\Helpers\Typo3ClassNamingUtilityTrait; use TYPO3\CMS\Extbase\Persistence\Generic\QueryResult; use TYPO3\CMS\Extbase\Persistence\QueryInterface; @@ -45,7 +48,6 @@ public function getTypeFromMethodCall( $argument = $methodCall->getArgs()[0] ?? null; $classReflection = $scope->getClassReflection(); - $queryType = $scope->getType($methodCall->var); if ($queryType instanceof GenericObjectType) { $modelType = $queryType->getTypes(); @@ -64,15 +66,23 @@ public function getTypeFromMethodCall( } } + $returnType = new GenericObjectType(QueryResult::class, $modelType); + $rawReturnType = new ArrayType(new IntegerType(), new ArrayType(new StringType(), new MixedType())); + if ($argument !== null) { $argType = $scope->getType($argument->value); - if ($classReflection !== null && $argType instanceof ConstantBooleanType && $argType->getValue() === true) { - return new ArrayType(new IntegerType(), $modelType[0]); + if ($argType instanceof ConstantBooleanType) { + if ($argType->getValue() === true) { + // A static boolean value with "true" has been given + return $rawReturnType; + } + } elseif ($argType instanceof BooleanType) { + // A variable with a boolean value has been given but we don't know it's value + return TypeCombinator::union($rawReturnType, $returnType); } } - - return new GenericObjectType(QueryResult::class, $modelType); + return $returnType; } } diff --git a/stubs/QueryInterface.stub b/stubs/QueryInterface.stub index 96621ea..db615a9 100644 --- a/stubs/QueryInterface.stub +++ b/stubs/QueryInterface.stub @@ -8,7 +8,7 @@ interface QueryInterface { /** * @param bool $returnRawQueryResult - * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array + * @phpstan-return ($returnRawQueryResult is false ? QueryResultInterface : list>) */ public function execute($returnRawQueryResult = false); diff --git a/tests/Unit/Type/data/custom-query-type.php b/tests/Unit/Type/data/custom-query-type.php index 2131cd2..0ab58c5 100644 --- a/tests/Unit/Type/data/custom-query-type.php +++ b/tests/Unit/Type/data/custom-query-type.php @@ -18,7 +18,6 @@ class SomeOtherModel extends AbstractEntity namespace CustomQueryType\My\Test\Extension\Domain\Repository; use CustomQueryType\My\Test\Extension\Domain\Model\SomeOtherModel; -use TYPO3\CMS\Extbase\Persistence\QueryInterface; use TYPO3\CMS\Extbase\Persistence\Repository; use function PHPStan\Testing\assertType; @@ -28,19 +27,30 @@ class SomeOtherModel extends AbstractEntity class MyModelRepository extends Repository { - public function findBySomething(): void + public function findBySomething(bool $booleanParameter = false): void { - /** @var QueryInterface $query */ $query = $this->persistenceManager->createQueryForType(SomeOtherModel::class); $result = $query->execute(); assertType( - 'TYPO3\CMS\Extbase\Persistence\QueryInterface', - $query + 'TYPO3\CMS\Extbase\Persistence\Generic\QueryResult', + $result + ); + + $result = $query->execute(false); + assertType( + 'TYPO3\CMS\Extbase\Persistence\Generic\QueryResult', + $result + ); + + $result = $query->execute($booleanParameter); + assertType( + 'array>|TYPO3\CMS\Extbase\Persistence\Generic\QueryResult', + $result ); $rawResult = $query->execute(true); - assertType('array', $rawResult); + assertType('array>', $rawResult); $array = $result->toArray(); assertType('array', $array);