From 3d0d9416546440e73e37e61d6a5835fc676ea18d Mon Sep 17 00:00:00 2001 From: Anne van de Venis Date: Wed, 26 Oct 2022 18:37:45 +0200 Subject: [PATCH 1/3] Return a generic array in Query fixes #377 --- stubs/ORM/Query.stub | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/stubs/ORM/Query.stub b/stubs/ORM/Query.stub index d3610bb7..8b7f53d8 100644 --- a/stubs/ORM/Query.stub +++ b/stubs/ORM/Query.stub @@ -3,10 +3,23 @@ namespace Doctrine\ORM; /** + * @template TKey as array-key * @template TResult The type of results returned by this query in HYDRATE_OBJECT mode * * @extends AbstractQuery */ final class Query extends AbstractQuery { + /** + * @param int|string $hydrationMode + * + * @phpstan-return ( + * $hydrationMode is self::HYDRATE_OBJECT + * ? array + * : mixed + * ) + */ + public function getResult($hydrationMode = self::HYDRATE_OBJECT) + { + } } From 189a8971743ad44581396f0d459eacb2f0625a57 Mon Sep 17 00:00:00 2001 From: Anne van de Venis Date: Thu, 27 Oct 2022 15:13:09 +0200 Subject: [PATCH 2/3] update tests --- .gitignore | 1 + .../Doctrine/CreateQueryDynamicReturnTypeExtension.php | 1 + src/Type/Doctrine/Query/QueryResultTypeWalker.php | 6 +++--- .../QueryBuilderGetQueryDynamicReturnTypeExtension.php | 2 ++ stubs/ORM/Query.stub | 7 ++++--- stubs/ORM/QueryBuilder.stub | 2 +- stubs/bleedingEdge/ORM/QueryBuilder.stub | 4 ++-- tests/Type/Doctrine/Query/QueryResultTypeWalkerTest.php | 2 ++ tests/Type/Doctrine/data/QueryResult/createQuery.php | 4 ++-- .../Doctrine/data/QueryResult/queryBuilderGetQuery.php | 4 ++-- tests/Type/Doctrine/data/QueryResult/queryResult.php | 2 +- 11 files changed, 21 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 2db21315..4c1cc828 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /vendor /composer.lock .phpunit.result.cache +.idea/ diff --git a/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php b/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php index 6384f718..91265e0b 100644 --- a/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php @@ -84,6 +84,7 @@ public function getTypeFromMethodCall( $typeBuilder = new QueryResultTypeBuilder(); try { + /** @var Query $query */ $query = $em->createQuery($queryString); QueryResultTypeWalker::walk($query, $typeBuilder, $this->descriptorRegistry); } catch (ORMException | DBALException | NewDBALException | CommonException $e) { diff --git a/src/Type/Doctrine/Query/QueryResultTypeWalker.php b/src/Type/Doctrine/Query/QueryResultTypeWalker.php index 5517cb1e..6fb5db6a 100644 --- a/src/Type/Doctrine/Query/QueryResultTypeWalker.php +++ b/src/Type/Doctrine/Query/QueryResultTypeWalker.php @@ -79,7 +79,7 @@ class QueryResultTypeWalker extends SqlWalker */ private $newObjectCounter = 0; - /** @var Query */ + /** @var Query */ private $query; /** @var EntityManagerInterface */ @@ -108,7 +108,7 @@ class QueryResultTypeWalker extends SqlWalker private $hasGroupByClause; /** - * @param Query $query + * @param Query $query */ public static function walk(Query $query, QueryResultTypeBuilder $typeBuilder, DescriptorRegistry $descriptorRegistry): void { @@ -123,7 +123,7 @@ public static function walk(Query $query, QueryResultTypeBuilder $typeBuilder, D /** * {@inheritDoc} * - * @param Query $query + * @param Query $query * @param ParserResult $parserResult * @param array $queryComponents */ diff --git a/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php b/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php index c828411f..ffb15410 100644 --- a/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php @@ -6,6 +6,7 @@ use Doctrine\DBAL\DBALException; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\ORMException; +use Doctrine\ORM\Query; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Identifier; use PHPStan\Analyser\Scope; @@ -159,6 +160,7 @@ private function getQueryType(string $dql): Type $typeBuilder = new QueryResultTypeBuilder(); try { + /** @var Query $query */ $query = $em->createQuery($dql); QueryResultTypeWalker::walk($query, $typeBuilder, $this->descriptorRegistry); } catch (ORMException | DBALException | CommonException $e) { diff --git a/stubs/ORM/Query.stub b/stubs/ORM/Query.stub index 8b7f53d8..ce9b2964 100644 --- a/stubs/ORM/Query.stub +++ b/stubs/ORM/Query.stub @@ -10,12 +10,13 @@ namespace Doctrine\ORM; */ final class Query extends AbstractQuery { + public const HYDRATE_OBJECT = 1; + /** * @param int|string $hydrationMode * - * @phpstan-return ( - * $hydrationMode is self::HYDRATE_OBJECT - * ? array + * @return ($hydrationMode is self::HYDRATE_OBJECT + * ? array * : mixed * ) */ diff --git a/stubs/ORM/QueryBuilder.stub b/stubs/ORM/QueryBuilder.stub index db14c5b2..f36db111 100644 --- a/stubs/ORM/QueryBuilder.stub +++ b/stubs/ORM/QueryBuilder.stub @@ -15,7 +15,7 @@ class QueryBuilder } /** - * @return Query + * @return Query */ public function getQuery() { diff --git a/stubs/bleedingEdge/ORM/QueryBuilder.stub b/stubs/bleedingEdge/ORM/QueryBuilder.stub index 9fb03cff..647b8ccc 100644 --- a/stubs/bleedingEdge/ORM/QueryBuilder.stub +++ b/stubs/bleedingEdge/ORM/QueryBuilder.stub @@ -8,7 +8,7 @@ class QueryBuilder { /** - * @return Query + * @return Query */ public function getQuery() { @@ -143,7 +143,7 @@ class QueryBuilder { } - + /** * @param literal-string|object|array $predicates * @return $this diff --git a/tests/Type/Doctrine/Query/QueryResultTypeWalkerTest.php b/tests/Type/Doctrine/Query/QueryResultTypeWalkerTest.php index a6b59762..b9884504 100644 --- a/tests/Type/Doctrine/Query/QueryResultTypeWalkerTest.php +++ b/tests/Type/Doctrine/Query/QueryResultTypeWalkerTest.php @@ -8,6 +8,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Query; use Doctrine\ORM\Query\AST\TypedExpression; use Doctrine\ORM\Tools\SchemaTool; use PHPStan\Testing\PHPStanTestCase; @@ -190,6 +191,7 @@ public function test(Type $expectedType, string $dql, ?string $expectedException { $em = self::$em; + /** @var Query $query */ $query = $em->createQuery($dql); $typeBuilder = new QueryResultTypeBuilder(); diff --git a/tests/Type/Doctrine/data/QueryResult/createQuery.php b/tests/Type/Doctrine/data/QueryResult/createQuery.php index 2511b435..87be56b0 100644 --- a/tests/Type/Doctrine/data/QueryResult/createQuery.php +++ b/tests/Type/Doctrine/data/QueryResult/createQuery.php @@ -29,14 +29,14 @@ public function testQueryResultTypeIsMixedWhenDQLIsNotKnown(EntityManagerInterfa { $query = $em->createQuery($dql); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } public function testQueryResultTypeIsMixedWhenDQLIsInvalid(EntityManagerInterface $em, string $dql): void { $query = $em->createQuery('invalid'); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } } diff --git a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php index 000f9d7e..bf2a8586 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php +++ b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php @@ -49,7 +49,7 @@ public function testQueryResultTypeIsMixedWhenDQLIsNotKnown(QueryBuilder $builde { $query = $builder->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } public function testQueryResultTypeIsMixedWhenDQLIsInvalid(EntityManagerInterface $em): void @@ -59,7 +59,7 @@ public function testQueryResultTypeIsMixedWhenDQLIsInvalid(EntityManagerInterfac ->from(Many::class, 'm') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } public function testQueryResultTypeIsVoidWithDeleteOrUpdate(EntityManagerInterface $em): void diff --git a/tests/Type/Doctrine/data/QueryResult/queryResult.php b/tests/Type/Doctrine/data/QueryResult/queryResult.php index 3a1e144e..e85cb2e1 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryResult.php +++ b/tests/Type/Doctrine/data/QueryResult/queryResult.php @@ -232,7 +232,7 @@ public function testReturnTypeOfQueryMethodsWithExplicitNonConstantHydrationMode * Test that we return the original return type when ResultType may be * VoidType * - * @param Query $query + * @param Query $query */ public function testReturnTypeOfQueryMethodsWithReturnTypeIsMixed(EntityManagerInterface $em, Query $query): void { From 8eba04020949eb28d44256ae246cd8a04144542a Mon Sep 17 00:00:00 2001 From: Anne van de Venis Date: Thu, 27 Oct 2022 15:36:27 +0200 Subject: [PATCH 3/3] fix failing tests --- .gitignore | 1 - Makefile | 2 +- .../Type/Doctrine/data/QueryResult/createQuery.php | 4 ++-- .../data/QueryResult/queryBuilderGetQuery.php | 4 ++-- .../Type/Doctrine/data/QueryResult/queryResult.php | 14 +++++++------- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 4c1cc828..2db21315 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ /vendor /composer.lock .phpunit.result.cache -.idea/ diff --git a/Makefile b/Makefile index bc09babb..45aabda4 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,6 @@ cs-fix: phpstan: php vendor/bin/phpstan analyse -l 8 -c phpstan.neon src tests -.PHONY: phpstan-generate-baseline +.PHONY: phpstan-generate-baselmake ine phpstan-generate-baseline: php vendor/bin/phpstan analyse -l 8 -c phpstan.neon src tests -b phpstan-baseline.neon diff --git a/tests/Type/Doctrine/data/QueryResult/createQuery.php b/tests/Type/Doctrine/data/QueryResult/createQuery.php index 87be56b0..2511b435 100644 --- a/tests/Type/Doctrine/data/QueryResult/createQuery.php +++ b/tests/Type/Doctrine/data/QueryResult/createQuery.php @@ -29,14 +29,14 @@ public function testQueryResultTypeIsMixedWhenDQLIsNotKnown(EntityManagerInterfa { $query = $em->createQuery($dql); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } public function testQueryResultTypeIsMixedWhenDQLIsInvalid(EntityManagerInterface $em, string $dql): void { $query = $em->createQuery('invalid'); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } } diff --git a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php index bf2a8586..8944aa7b 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php +++ b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php @@ -49,7 +49,7 @@ public function testQueryResultTypeIsMixedWhenDQLIsNotKnown(QueryBuilder $builde { $query = $builder->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query<(int|string), mixed>', $query); } public function testQueryResultTypeIsMixedWhenDQLIsInvalid(EntityManagerInterface $em): void @@ -59,7 +59,7 @@ public function testQueryResultTypeIsMixedWhenDQLIsInvalid(EntityManagerInterfac ->from(Many::class, 'm') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } public function testQueryResultTypeIsVoidWithDeleteOrUpdate(EntityManagerInterface $em): void diff --git a/tests/Type/Doctrine/data/QueryResult/queryResult.php b/tests/Type/Doctrine/data/QueryResult/queryResult.php index e85cb2e1..4b237e37 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryResult.php +++ b/tests/Type/Doctrine/data/QueryResult/queryResult.php @@ -237,31 +237,31 @@ public function testReturnTypeOfQueryMethodsWithExplicitNonConstantHydrationMode public function testReturnTypeOfQueryMethodsWithReturnTypeIsMixed(EntityManagerInterface $em, Query $query): void { assertType( - 'mixed', + 'array<(int|string)>', $query->getResult(AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'iterable', + 'iterable<(int|string)>', $query->toIterable([], AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'mixed', + 'array<(int|string)>', $query->execute(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'mixed', + 'array<(int|string)>', $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'mixed', + 'array<(int|string)>', $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'mixed', + '(int|string)', $query->getSingleResult(AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'mixed', + 'int|string|null', $query->getOneOrNullResult(AbstractQuery::HYDRATE_OBJECT) ); }