Skip to content

Commit

Permalink
Fix return type of QueryInterface::execute()
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sascha-egerer committed May 22, 2024
1 parent a7549f5 commit 0d06dc4
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 12 deletions.
20 changes: 15 additions & 5 deletions src/Type/QueryInterfaceDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@
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;
use PHPStan\Type\Generic\GenericObjectType;
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;
Expand Down Expand Up @@ -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();
Expand All @@ -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;
}

}
2 changes: 1 addition & 1 deletion stubs/QueryInterface.stub
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface QueryInterface
{
/**
* @param bool $returnRawQueryResult
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface<ModelType>|array<string, mixed>
* @phpstan-return ($returnRawQueryResult is false ? QueryResultInterface<ModelType> : list<array<string,mixed>>)
*/
public function execute($returnRawQueryResult = false);

Expand Down
22 changes: 16 additions & 6 deletions tests/Unit/Type/data/custom-query-type.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<SomeOtherModel> $query */
$query = $this->persistenceManager->createQueryForType(SomeOtherModel::class);

$result = $query->execute();
assertType(
'TYPO3\CMS\Extbase\Persistence\QueryInterface<CustomQueryType\My\Test\Extension\Domain\Model\SomeOtherModel>',
$query
'TYPO3\CMS\Extbase\Persistence\Generic\QueryResult<CustomQueryType\My\Test\Extension\Domain\Model\SomeOtherModel>',
$result
);

$result = $query->execute(false);
assertType(
'TYPO3\CMS\Extbase\Persistence\Generic\QueryResult<CustomQueryType\My\Test\Extension\Domain\Model\SomeOtherModel>',
$result
);

$result = $query->execute($booleanParameter);
assertType(
'array<int, array<string, mixed>>|TYPO3\CMS\Extbase\Persistence\Generic\QueryResult<CustomQueryType\My\Test\Extension\Domain\Model\SomeOtherModel>',
$result
);

$rawResult = $query->execute(true);
assertType('array<int, CustomQueryType\My\Test\Extension\Domain\Model\SomeOtherModel>', $rawResult);
assertType('array<int, array<string, mixed>>', $rawResult);

$array = $result->toArray();
assertType('array<int, CustomQueryType\My\Test\Extension\Domain\Model\SomeOtherModel>', $array);
Expand Down

0 comments on commit 0d06dc4

Please sign in to comment.