From 31f74fec9710ff4d3105617aa56b7afac3f79fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Sun, 8 Jan 2023 14:08:50 +0100 Subject: [PATCH] Revert "Improve QueryResultDynamicReturnTypeExtension" This reverts commit 5a3bbc1cf62c735b84f12212adeea1b24e3594b5. --- .../QueryResultDynamicReturnTypeExtension.php | 141 +------ .../Doctrine/data/QueryResult/queryResult.php | 366 +----------------- 2 files changed, 31 insertions(+), 476 deletions(-) diff --git a/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php b/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php index c9ae9593..1bd41a55 100644 --- a/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php @@ -10,7 +10,6 @@ use PHPStan\ShouldNotHappenException; use PHPStan\Type\Accessory\AccessoryArrayListType; use PHPStan\Type\ArrayType; -use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\GenericTypeVariableResolver; @@ -18,13 +17,10 @@ use PHPStan\Type\IterableType; use PHPStan\Type\MixedType; use PHPStan\Type\NullType; -use PHPStan\Type\ObjectWithoutClassType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; -use PHPStan\Type\TypeTraverser; use PHPStan\Type\TypeWithClassName; use PHPStan\Type\VoidType; -use function count; final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { @@ -39,13 +35,6 @@ final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturn 'getSingleResult' => 0, ]; - private const METHOD_HYDRATION_MODE = [ - 'getArrayResult' => AbstractQuery::HYDRATE_ARRAY, - 'getScalarResult' => AbstractQuery::HYDRATE_SCALAR, - 'getSingleColumnResult' => AbstractQuery::HYDRATE_SCALAR_COLUMN, - 'getSingleScalarResult' => AbstractQuery::HYDRATE_SINGLE_SCALAR, - ]; - public function getClass(): string { return AbstractQuery::class; @@ -53,8 +42,7 @@ public function getClass(): string public function isMethodSupported(MethodReflection $methodReflection): bool { - return isset(self::METHOD_HYDRATION_MODE_ARG[$methodReflection->getName()]) - || isset(self::METHOD_HYDRATION_MODE[$methodReflection->getName()]); + return isset(self::METHOD_HYDRATION_MODE_ARG[$methodReflection->getName()]); } public function getTypeFromMethodCall( @@ -65,23 +53,21 @@ public function getTypeFromMethodCall( { $methodName = $methodReflection->getName(); - if (isset(self::METHOD_HYDRATION_MODE[$methodName])) { - $hydrationMode = new ConstantIntegerType(self::METHOD_HYDRATION_MODE[$methodName]); - } elseif (isset(self::METHOD_HYDRATION_MODE_ARG[$methodName])) { - $argIndex = self::METHOD_HYDRATION_MODE_ARG[$methodName]; - $args = $methodCall->getArgs(); + if (!isset(self::METHOD_HYDRATION_MODE_ARG[$methodName])) { + throw new ShouldNotHappenException(); + } + + $argIndex = self::METHOD_HYDRATION_MODE_ARG[$methodName]; + $args = $methodCall->getArgs(); - if (isset($args[$argIndex])) { - $hydrationMode = $scope->getType($args[$argIndex]->value); - } else { - $parametersAcceptor = ParametersAcceptorSelector::selectSingle( - $methodReflection->getVariants() - ); - $parameter = $parametersAcceptor->getParameters()[$argIndex]; - $hydrationMode = $parameter->getDefaultValue() ?? new NullType(); - } + if (isset($args[$argIndex])) { + $hydrationMode = $scope->getType($args[$argIndex]->value); } else { - throw new ShouldNotHappenException(); + $parametersAcceptor = ParametersAcceptorSelector::selectSingle( + $methodReflection->getVariants() + ); + $parameter = $parametersAcceptor->getParameters()[$argIndex]; + $hydrationMode = $parameter->getDefaultValue() ?? new NullType(); } $queryType = $scope->getType($methodCall->var); @@ -145,32 +131,12 @@ private function getMethodReturnTypeForHydrationMode( return $this->originalReturnType($methodReflection); } - if (!$hydrationMode instanceof ConstantIntegerType) { + if (!$this->isObjectHydrationMode($hydrationMode)) { + // We support only HYDRATE_OBJECT. For other hydration modes, we + // return the declared return type of the method. return $this->originalReturnType($methodReflection); } - switch ($hydrationMode->getValue()) { - case AbstractQuery::HYDRATE_OBJECT: - break; - case AbstractQuery::HYDRATE_ARRAY: - $queryResultType = $this->getArrayHydratedReturnType($queryResultType); - break; - case AbstractQuery::HYDRATE_SCALAR: - $queryResultType = $this->getScalarHydratedReturnType($queryResultType); - break; - case AbstractQuery::HYDRATE_SINGLE_SCALAR: - $queryResultType = $this->getSingleScalarHydratedReturnType($queryResultType); - break; - case AbstractQuery::HYDRATE_SIMPLEOBJECT: - $queryResultType = $this->getSimpleObjectHydratedReturnType($queryResultType); - break; - case AbstractQuery::HYDRATE_SCALAR_COLUMN: - $queryResultType = $this->getScalarColumnHydratedReturnType($queryResultType); - break; - default: - return $this->originalReturnType($methodReflection); - } - switch ($methodReflection->getName()) { case 'getSingleResult': return $queryResultType; @@ -195,78 +161,13 @@ private function getMethodReturnTypeForHydrationMode( } } - private function getArrayHydratedReturnType(Type $queryResultType): Type + private function isObjectHydrationMode(Type $type): bool { - return TypeTraverser::map( - $queryResultType, - static function (Type $type, callable $traverse): Type { - $isObject = (new ObjectWithoutClassType())->isSuperTypeOf($type); - if ($isObject->yes()) { - return new ArrayType(new MixedType(), new MixedType()); - } - if ($isObject->maybe()) { - return new MixedType(); - } - - return $traverse($type); - } - ); - } - - private function getScalarHydratedReturnType(Type $queryResultType): Type - { - if (!$queryResultType instanceof ArrayType) { - return new ArrayType(new MixedType(), new MixedType()); - } - - $itemType = $queryResultType->getItemType(); - $hasNoObject = (new ObjectWithoutClassType())->isSuperTypeOf($itemType)->no(); - $hasNoArray = $itemType->isArray()->no(); - - if ($hasNoArray && $hasNoObject) { - return $queryResultType; - } - - return new ArrayType(new MixedType(), new MixedType()); - } - - private function getSimpleObjectHydratedReturnType(Type $queryResultType): Type - { - if ((new ObjectWithoutClassType())->isSuperTypeOf($queryResultType)->yes()) { - return $queryResultType; - } - - return new MixedType(); - } - - private function getSingleScalarHydratedReturnType(Type $queryResultType): Type - { - $queryResultType = $this->getScalarHydratedReturnType($queryResultType); - if (!$queryResultType instanceof ConstantArrayType) { - return new ArrayType(new MixedType(), new MixedType()); - } - - $values = $queryResultType->getValueTypes(); - if (count($values) !== 1) { - return new ArrayType(new MixedType(), new MixedType()); - } - - return $queryResultType; - } - - private function getScalarColumnHydratedReturnType(Type $queryResultType): Type - { - $queryResultType = $this->getScalarHydratedReturnType($queryResultType); - if (!$queryResultType instanceof ConstantArrayType) { - return new MixedType(); - } - - $values = $queryResultType->getValueTypes(); - if (count($values) !== 1) { - return new MixedType(); + if (!$type instanceof ConstantIntegerType) { + return false; } - return $queryResultType->getFirstIterableValueType(); + return $type->getValue() === AbstractQuery::HYDRATE_OBJECT; } private function originalReturnType(MethodReflection $methodReflection): Type diff --git a/tests/Type/Doctrine/data/QueryResult/queryResult.php b/tests/Type/Doctrine/data/QueryResult/queryResult.php index 6c095311..18a1faa9 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryResult.php +++ b/tests/Type/Doctrine/data/QueryResult/queryResult.php @@ -143,11 +143,11 @@ public function testReturnTypeOfQueryMethodsWithExplicitObjectHydrationMode(Enti } /** - * Test that we properly infer the return type of Query methods with explicit hydration mode of HYDRATE_ARRAY + * Test that we properly infer the return type of Query methods with explicit hydration mode that is not HYDRATE_OBJECT * - * We can infer the return type by changing every object by an array + * We are never able to infer the return type here */ - public function testReturnTypeOfQueryMethodsWithExplicitArrayHydrationMode(EntityManagerInterface $em): void + public function testReturnTypeOfQueryMethodsWithExplicitNonObjectHydrationMode(EntityManagerInterface $em): void { $query = $em->createQuery(' SELECT m @@ -155,378 +155,32 @@ public function testReturnTypeOfQueryMethodsWithExplicitArrayHydrationMode(Entit '); assertType( - 'list', + 'mixed', $query->getResult(AbstractQuery::HYDRATE_ARRAY) ); assertType( - 'list', - $query->getArrayResult() - ); - assertType( - 'iterable', + 'iterable', $query->toIterable([], AbstractQuery::HYDRATE_ARRAY) ); assertType( - 'list', - $query->execute(null, AbstractQuery::HYDRATE_ARRAY) - ); - assertType( - 'list', - $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_ARRAY) - ); - assertType( - 'list', - $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_ARRAY) - ); - assertType( - 'array', - $query->getSingleResult(AbstractQuery::HYDRATE_ARRAY) - ); - assertType( - 'array|null', - $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY) - ); - - $query = $em->createQuery(' - SELECT m.intColumn, m.stringNullColumn - FROM QueryResult\Entities\Many m - '); - - assertType( - 'list', - $query->getResult(AbstractQuery::HYDRATE_ARRAY) - ); - assertType( - 'list', - $query->getArrayResult() - ); - assertType( - 'list', + 'mixed', $query->execute(null, AbstractQuery::HYDRATE_ARRAY) ); - assertType( - 'list', - $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_ARRAY) - ); - assertType( - 'list', - $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_ARRAY) - ); - assertType( - 'array{intColumn: int, stringNullColumn: string|null}', - $query->getSingleResult(AbstractQuery::HYDRATE_ARRAY) - ); - assertType( - 'array{intColumn: int, stringNullColumn: string|null}|null', - $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY) - ); - } - - /** - * Test that we properly infer the return type of Query methods with explicit hydration mode of HYDRATE_SCALAR - */ - public function testReturnTypeOfQueryMethodsWithExplicitScalarHydrationMode(EntityManagerInterface $em): void - { - $query = $em->createQuery(' - SELECT m - FROM QueryResult\Entities\Many m - '); - - assertType( - 'list', - $query->getResult(AbstractQuery::HYDRATE_SCALAR) - ); - assertType( - 'list', - $query->getScalarResult() - ); - assertType( - 'iterable', - $query->toIterable([], AbstractQuery::HYDRATE_SCALAR) - ); - assertType( - 'list', - $query->execute(null, AbstractQuery::HYDRATE_SCALAR) - ); - assertType( - 'list', - $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SCALAR) - ); - assertType( - 'list', - $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SCALAR) - ); - assertType( - 'array', - $query->getSingleResult(AbstractQuery::HYDRATE_SCALAR) - ); - assertType( - 'array|null', - $query->getOneOrNullResult(AbstractQuery::HYDRATE_SCALAR) - ); - - $query = $em->createQuery(' - SELECT m.intColumn, m.stringNullColumn - FROM QueryResult\Entities\Many m - '); - - assertType( - 'list', - $query->getResult(AbstractQuery::HYDRATE_SCALAR) - ); - assertType( - 'list', - $query->getScalarResult() - ); - assertType( - 'list', - $query->execute(null, AbstractQuery::HYDRATE_SCALAR) - ); - assertType( - 'list', - $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SCALAR) - ); - assertType( - 'list', - $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SCALAR) - ); - assertType( - 'array{intColumn: int, stringNullColumn: string|null}', - $query->getSingleResult(AbstractQuery::HYDRATE_SCALAR) - ); - assertType( - 'array{intColumn: int, stringNullColumn: string|null}|null', - $query->getOneOrNullResult(AbstractQuery::HYDRATE_SCALAR) - ); - } - - /** - * Test that we properly infer the return type of Query methods with explicit hydration mode of HYDRATE_SCALAR - */ - public function testReturnTypeOfQueryMethodsWithExplicitSingleScalarHydrationMode(EntityManagerInterface $em): void - { - $query = $em->createQuery(' - SELECT m - FROM QueryResult\Entities\Many m - '); - - assertType( - 'list', - $query->getResult(AbstractQuery::HYDRATE_SINGLE_SCALAR) - ); - assertType( - 'list', - $query->getSingleScalarResult() - ); - assertType( - 'iterable', - $query->toIterable([], AbstractQuery::HYDRATE_SINGLE_SCALAR) - ); - assertType( - 'list', - $query->execute(null, AbstractQuery::HYDRATE_SINGLE_SCALAR) - ); - assertType( - 'list', - $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SINGLE_SCALAR) - ); - assertType( - 'list', - $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SINGLE_SCALAR) - ); - assertType( - 'array', - $query->getSingleResult(AbstractQuery::HYDRATE_SINGLE_SCALAR) - ); - assertType( - 'array|null', - $query->getOneOrNullResult(AbstractQuery::HYDRATE_SINGLE_SCALAR) - ); - - $query = $em->createQuery(' - SELECT m.intColumn - FROM QueryResult\Entities\Many m - '); - - assertType( - 'list', - $query->getResult(AbstractQuery::HYDRATE_SINGLE_SCALAR) - ); - assertType( - 'list', - $query->getSingleScalarResult() - ); - assertType( - 'list', - $query->execute(null, AbstractQuery::HYDRATE_SINGLE_SCALAR) - ); - assertType( - 'list', - $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SINGLE_SCALAR) - ); - assertType( - 'list', - $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SINGLE_SCALAR) - ); - assertType( - 'array{intColumn: int}', - $query->getSingleResult(AbstractQuery::HYDRATE_SINGLE_SCALAR) - ); - assertType( - 'array{intColumn: int}|null', - $query->getOneOrNullResult(AbstractQuery::HYDRATE_SINGLE_SCALAR) - ); - } - - /** - * Test that we properly infer the return type of Query methods with explicit hydration mode of HYDRATE_SIMPLEOBJECT - * - * We are never able to infer the return type here - */ - public function testReturnTypeOfQueryMethodsWithExplicitSimpleObjectHydrationMode(EntityManagerInterface $em): void - { - $query = $em->createQuery(' - SELECT m - FROM QueryResult\Entities\Many m - '); - - assertType( - 'list', - $query->getResult(AbstractQuery::HYDRATE_SIMPLEOBJECT) - ); - assertType( - 'iterable', - $query->toIterable([], AbstractQuery::HYDRATE_SIMPLEOBJECT) - ); - assertType( - 'list', - $query->execute(null, AbstractQuery::HYDRATE_SIMPLEOBJECT) - ); - assertType( - 'list', - $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SIMPLEOBJECT) - ); - assertType( - 'list', - $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SIMPLEOBJECT) - ); - assertType( - 'QueryResult\Entities\Many', - $query->getSingleResult(AbstractQuery::HYDRATE_SIMPLEOBJECT) - ); - assertType( - 'QueryResult\Entities\Many|null', - $query->getOneOrNullResult(AbstractQuery::HYDRATE_SIMPLEOBJECT) - ); - - $query = $em->createQuery(' - SELECT m.intColumn, m.stringNullColumn - FROM QueryResult\Entities\Many m - '); - - assertType( - 'list', - $query->getResult(AbstractQuery::HYDRATE_SIMPLEOBJECT) - ); - assertType( - 'list', - $query->execute(null, AbstractQuery::HYDRATE_SIMPLEOBJECT) - ); - assertType( - 'list', - $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SIMPLEOBJECT) - ); - assertType( - 'list', - $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SIMPLEOBJECT) - ); assertType( 'mixed', - $query->getSingleResult(AbstractQuery::HYDRATE_SIMPLEOBJECT) + $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_ARRAY) ); assertType( 'mixed', - $query->getOneOrNullResult(AbstractQuery::HYDRATE_SIMPLEOBJECT) - ); - } - - /** - * Test that we properly infer the return type of Query methods with explicit hydration mode of HYDRATE_SCALAR_COLUMN - * - * We are never able to infer the return type here - */ - public function testReturnTypeOfQueryMethodsWithExplicitScalarColumnHydrationMode(EntityManagerInterface $em): void - { - $query = $em->createQuery(' - SELECT m - FROM QueryResult\Entities\Many m - '); - - assertType( - 'list', - $query->getResult(AbstractQuery::HYDRATE_SCALAR_COLUMN) - ); - assertType( - 'list', - $query->getSingleColumnResult() - ); - assertType( - 'iterable', - $query->toIterable([], AbstractQuery::HYDRATE_SCALAR_COLUMN) - ); - assertType( - 'list', - $query->execute(null, AbstractQuery::HYDRATE_SCALAR_COLUMN) - ); - assertType( - 'list', - $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SCALAR_COLUMN) - ); - assertType( - 'list', - $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SCALAR_COLUMN) + $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_ARRAY) ); assertType( 'mixed', - $query->getSingleResult(AbstractQuery::HYDRATE_SCALAR_COLUMN) + $query->getSingleResult(AbstractQuery::HYDRATE_ARRAY) ); assertType( 'mixed', - $query->getOneOrNullResult(AbstractQuery::HYDRATE_SCALAR_COLUMN) - ); - - $query = $em->createQuery(' - SELECT m.intColumn - FROM QueryResult\Entities\Many m - '); - - assertType( - 'list', - $query->getResult(AbstractQuery::HYDRATE_SCALAR_COLUMN) - ); - assertType( - 'list', - $query->getSingleColumnResult() - ); - assertType( - 'list', - $query->execute(null, AbstractQuery::HYDRATE_SCALAR_COLUMN) - ); - assertType( - 'list', - $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SCALAR_COLUMN) - ); - assertType( - 'list', - $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SCALAR_COLUMN) - ); - assertType( - 'int', - $query->getSingleResult(AbstractQuery::HYDRATE_SCALAR_COLUMN) - ); - assertType( - 'int|null', - $query->getOneOrNullResult(AbstractQuery::HYDRATE_SCALAR_COLUMN) + $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY) ); }