Skip to content

Deprecate TypedExpression in favor of ExpressionWithReturnType - #12543

Merged
greg0ire merged 1 commit into
doctrine:3.7.xfrom
GromNaN:feature/typed-expression-name
Aug 4, 2026
Merged

Deprecate TypedExpression in favor of ExpressionWithReturnType#12543
greg0ire merged 1 commit into
doctrine:3.7.xfrom
GromNaN:feature/typed-expression-name

Conversation

@GromNaN

@GromNaN GromNaN commented Jul 31, 2026

Copy link
Copy Markdown
Member

Improvement

Q A
New Feature no
RFC no
BC Break no

Follow-up to this comment.

History

The TypedExpression interface was introduced in December 2019 (commit 24e9a7c, issue #7941) to let DQL functions declare their scalar return type. At the time, DBAL 2.x exposed Type::getName() on the instance, so SqlWalker cheaply extracted the name via \$expr->getReturnType()->getName(). Returning a Type object was the natural handle for "a type" and required no static lookup on the walker side.

DBAL 4 removed Type::getName() in favour of TypeRegistry::lookupName($type). SqlWalker had to reach for Type::getTypeRegistry()->lookupName(...), and implementers had to keep calling the static Type::getType(Types::INTEGER) to hand back a Type instance the walker only uses to extract a name. The round-trip is vestigial, and it pins two static Type::* calls that block the instance-based type registry work in #12421.

Solution

Introduce a new interface Doctrine\ORM\Query\AST\ExpressionWithReturnType that exposes the DBAL type name directly. TypedExpression is deprecated; SqlWalker prefers the new interface, falls back to the legacy one with a runtime deprecation, and finally to the historical 'string' default.

Upgrade

Before:

use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\AST\TypedExpression;

class MyFunction extends FunctionNode implements TypedExpression
{
    public function getReturnType(): Type
    {
        return Type::getType(Types::INTEGER);
    }
}

After:

use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\AST\ExpressionWithReturnType;

class MyFunction extends FunctionNode implements ExpressionWithReturnType
{
    public function getReturnTypeName(): string
    {
        return Types::INTEGER;
    }
}

`TypedExpression::getReturnType()` returns a `Doctrine\DBAL\Types\Type`
instance, which forces implementers to perform a static, global type
lookup (`Type::getType()`). `SqlWalker` then converts the instance back
to a name via `TypeRegistry::lookupName()`, making the round-trip
pointless and introducing two static `Type::*` calls that block moving
the ORM to instance-based type registries.

Introduce `Doctrine\ORM\Query\AST\ExpressionWithReturnType`, which
exposes the DBAL type name directly (`getReturnTypeName(): string`).
`SqlWalker` prefers the new interface, falls back to the legacy one
with a deprecation notice, and finally to the `'string'` default.

`CountFunction` and `LengthFunction` now implement both interfaces:
`getReturnTypeName()` returns a plain `Types::*` constant, and the
legacy `getReturnType()` delegates and is marked `@deprecated`.
@GromNaN
GromNaN force-pushed the feature/typed-expression-name branch from 6a33845 to a03ae56 Compare July 31, 2026 20:29
@greg0ire greg0ire added this to the 3.7.0 milestone Aug 4, 2026
@greg0ire
greg0ire merged commit 6a9cca8 into doctrine:3.7.x Aug 4, 2026
109 checks passed
@greg0ire

greg0ire commented Aug 4, 2026

Copy link
Copy Markdown
Member

Thanks @GromNaN !

@GromNaN
GromNaN deleted the feature/typed-expression-name branch August 4, 2026 06:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants