diff --git a/UPGRADE.md b/UPGRADE.md index ef8b5c605f..cad3628dbe 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,11 @@ # Upgrade to 4.0 +## Require implementation of `OutputWalker`, remove `SqlWalker::getExecutor()` + +The `SqlWalker::getExecutor()` method is removed. Output walkers should +implement the `\Doctrine\ORM\Query\OutputWalker` interface and create +`Doctrine\ORM\Query\Exec\SqlFinalizer` instances. + ## Remove `DatabaseDriver` The class `Doctrine\ORM\Mapping\Driver\DatabaseDriver` is removed. diff --git a/psalm-baseline.xml b/psalm-baseline.xml index bf151dcafb..88526e4592 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -896,9 +896,6 @@ - - - diff --git a/src/Query.php b/src/Query.php index 890b2a9dfa..b7dae94d10 100644 --- a/src/Query.php +++ b/src/Query.php @@ -7,14 +7,11 @@ use Doctrine\DBAL\LockMode; use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\Type; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query\AST\DeleteStatement; use Doctrine\ORM\Query\AST\SelectStatement; use Doctrine\ORM\Query\AST\UpdateStatement; use Doctrine\ORM\Query\Exec\AbstractSqlExecutor; -use Doctrine\ORM\Query\Exec\SqlFinalizer; -use Doctrine\ORM\Query\OutputWalker; use Doctrine\ORM\Query\Parameter; use Doctrine\ORM\Query\ParameterTypeInferer; use Doctrine\ORM\Query\Parser; @@ -30,7 +27,6 @@ use function count; use function get_debug_type; use function in_array; -use function is_a; use function ksort; use function md5; use function reset; @@ -665,31 +661,10 @@ protected function getQueryCacheId(): string { ksort($this->hints); - if (! $this->hasHint(self::HINT_CUSTOM_OUTPUT_WALKER)) { - // Assume Parser will create the SqlOutputWalker; save is_a call, which might trigger a class load - $firstAndMaxResult = ''; - } else { - $outputWalkerClass = $this->getHint(self::HINT_CUSTOM_OUTPUT_WALKER); - if (is_a($outputWalkerClass, OutputWalker::class, true)) { - $firstAndMaxResult = ''; - } else { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/11188/', - 'Your output walker class %s should implement %s in order to provide a %s. This also means the output walker should not use the query firstResult/maxResult values, which should be read from the query by the SqlFinalizer only.', - $outputWalkerClass, - OutputWalker::class, - SqlFinalizer::class, - ); - $firstAndMaxResult = '&firstResult=' . $this->firstResult . '&maxResult=' . $this->maxResults; - } - } - return md5( $this->getDQL() . serialize($this->hints) . '&platform=' . get_debug_type($this->getEntityManager()->getConnection()->getDatabasePlatform()) . ($this->em->hasFilters() ? $this->em->getFilters()->getHash() : '') . - $firstAndMaxResult . '&hydrationMode=' . $this->hydrationMode . '&types=' . serialize($this->parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT', ); } diff --git a/src/Query/Parser.php b/src/Query/Parser.php index bcfad85c42..65abf6ca90 100644 --- a/src/Query/Parser.php +++ b/src/Query/Parser.php @@ -5,7 +5,6 @@ namespace Doctrine\ORM\Query; use Doctrine\Common\Lexer\Token; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Exception\DuplicateFieldException; use Doctrine\ORM\Exception\NoMatchingPropertyException; @@ -13,7 +12,6 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query; use Doctrine\ORM\Query\AST\Functions; -use Doctrine\ORM\Query\Exec\SqlFinalizer; use LogicException; use ReflectionClass; @@ -158,7 +156,7 @@ final class Parser /** * The custom last tree walker, if any, that is responsible for producing the output. * - * @var class-string|null + * @var class-string|null */ private $customOutputWalker; @@ -177,24 +175,6 @@ public function __construct(private readonly Query $query) $this->parserResult = new ParserResult(); } - /** - * Sets a custom tree walker that produces output. - * This tree walker will be run last over the AST, after any other walkers. - * - * @param class-string $className - */ - public function setCustomOutputTreeWalker(string $className): void - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/11641', - '%s is deprecated, set the output walker class with the \Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER query hint instead', - __METHOD__, - ); - - $this->customOutputWalker = $className; - } - /** * Adds a custom tree walker for modifying the AST. * @@ -359,23 +339,7 @@ public function parse(): ParserResult $outputWalkerClass = $this->customOutputWalker ?: SqlOutputWalker::class; $outputWalker = new $outputWalkerClass($this->query, $this->parserResult, $this->queryComponents); - if ($outputWalker instanceof OutputWalker) { - $finalizer = $outputWalker->getFinalizer($AST); - $this->parserResult->setSqlFinalizer($finalizer); - } else { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/11188/', - 'Your output walker class %s should implement %s in order to provide a %s. This also means the output walker should not use the query firstResult/maxResult values, which should be read from the query by the SqlFinalizer only.', - $outputWalkerClass, - OutputWalker::class, - SqlFinalizer::class, - ); - // @phpstan-ignore method.deprecated - $executor = $outputWalker->getExecutor($AST); - // @phpstan-ignore method.deprecated - $this->parserResult->setSqlExecutor($executor); - } + $this->parserResult->setSqlFinalizer($outputWalker->getFinalizer($AST)); return $this->parserResult; } diff --git a/src/Query/SqlWalker.php b/src/Query/SqlWalker.php index 8164ae13bb..c8797e736b 100644 --- a/src/Query/SqlWalker.php +++ b/src/Query/SqlWalker.php @@ -230,22 +230,6 @@ public function setQueryComponent(string $dqlAlias, array $queryComponent): void $this->queryComponents[$dqlAlias] = $queryComponent; } - /** - * Gets an executor that can be used to execute the result of this walker. - * - * @deprecated Output walkers should no longer create the executor directly, but instead provide - * a SqlFinalizer by implementing the `OutputWalker` interface. Thus, this method is - * no longer needed and will be removed in 4.0. - */ - public function getExecutor(AST\SelectStatement|AST\UpdateStatement|AST\DeleteStatement $statement): Exec\AbstractSqlExecutor - { - return match (true) { - $statement instanceof AST\UpdateStatement => $this->createUpdateStatementExecutor($statement), - $statement instanceof AST\DeleteStatement => $this->createDeleteStatementExecutor($statement), - default => new Exec\SingleSelectExecutor($statement, $this), - }; - } - /** @psalm-internal Doctrine\ORM */ protected function createUpdateStatementExecutor(AST\UpdateStatement $AST): Exec\AbstractSqlExecutor { diff --git a/tests/Tests/ORM/Functional/PaginationTest.php b/tests/Tests/ORM/Functional/PaginationTest.php index db89c353d4..66cc3f9a9b 100644 --- a/tests/Tests/ORM/Functional/PaginationTest.php +++ b/tests/Tests/ORM/Functional/PaginationTest.php @@ -12,6 +12,7 @@ use Doctrine\ORM\Query\AST\PathExpression; use Doctrine\ORM\Query\AST\SelectStatement; use Doctrine\ORM\Query\AST\WhereClause; +use Doctrine\ORM\Query\SqlOutputWalker; use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\Query\TreeWalkerAdapter; use Doctrine\ORM\Tools\Pagination\Paginator; @@ -643,7 +644,7 @@ public function testCountQueryStripsParametersInSelect(): void self::assertCount(2, $getCountQuery->invoke($paginator)->getParameters()); self::assertCount(9, $paginator); - $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, SqlWalker::class); + $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, SqlOutputWalker::class); $paginator = new Paginator($query); diff --git a/tests/Tests/ORM/Functional/ParserResultSerializationTest.php b/tests/Tests/ORM/Functional/ParserResultSerializationTest.php index 6918bd8e50..f673f5ebd9 100644 --- a/tests/Tests/ORM/Functional/ParserResultSerializationTest.php +++ b/tests/Tests/ORM/Functional/ParserResultSerializationTest.php @@ -32,25 +32,6 @@ protected function setUp(): void parent::setUp(); } - /** @param Closure(ParserResult): ParserResult $toSerializedAndBack */ - #[DataProvider('provideToSerializedAndBack')] - public function testSerializeParserResultForQueryWithSqlWalker(Closure $toSerializedAndBack): void - { - $query = $this->_em - ->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyEmployee u WHERE u.name = :name'); - - // Use the (legacy) SqlWalker which directly puts an SqlExecutor instance into the parser result - $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, Query\SqlWalker::class); - - $parserResult = self::parseQuery($query); - $unserialized = $toSerializedAndBack($parserResult); - - $this->assertInstanceOf(ParserResult::class, $unserialized); - $this->assertInstanceOf(ResultSetMapping::class, $unserialized->getResultSetMapping()); - $this->assertEquals(['name' => [0]], $unserialized->getParameterMappings()); - $this->assertNotNull($unserialized->prepareSqlExecutor($query)); - } - /** @param Closure(ParserResult): ParserResult $toSerializedAndBack */ #[DataProvider('provideToSerializedAndBack')] public function testSerializeParserResultForQueryWithSqlOutputWalker(Closure $toSerializedAndBack): void