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