Skip to content

Commit c1273c9

Browse files
committed
Resolve types through the DBAL TypeProvider interface
DBAL 4.5 exposes Configuration::getTypeProvider() returning the new Doctrine\DBAL\Types\TypeProvider interface rather than the final TypeRegistry, so the type source is no longer required to be that concrete class. Renamed TypeRegistryLocator to TypeProviderLocator accordingly, along with the $typeRegistry properties and locals it feeds. The declared types are unions of TypeProvider and TypeRegistry, because the interface only exists as of DBAL 4.5 while these code paths still support older versions. PHP matches the first applicable member of a union and never loads the other, so the union resolves fine when TypeProvider is absent, whereas a sole TypeProvider type would raise a TypeError. Every such union sits on a private property, a private parameter, or the @internal locator, so none of it reaches the public API. DatabaseDriver now reads Column::getTypeName() instead of looking a name back up from an instance, which drops two deprecated calls at once. SqlWalker's deprecated TypedExpression branch keeps using Type::lookupName(), since a Type instance is all it has; both are deprecated and will be removed together.
1 parent 38c65bf commit c1273c9

13 files changed

Lines changed: 66 additions & 58 deletions

src/Internal/Hydration/AbstractHydrator.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
use BackedEnum;
88
use Doctrine\DBAL\Platforms\AbstractPlatform;
99
use Doctrine\DBAL\Result;
10+
use Doctrine\DBAL\Types\TypeProvider;
1011
use Doctrine\DBAL\Types\TypeRegistry;
1112
use Doctrine\ORM\EntityManagerInterface;
1213
use Doctrine\ORM\Events;
13-
use Doctrine\ORM\Internal\TypeRegistryLocator;
14+
use Doctrine\ORM\Internal\TypeProviderLocator;
1415
use Doctrine\ORM\Mapping\ClassMetadata;
1516
use Doctrine\ORM\Query\ResultSetMapping;
1617
use Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker;
@@ -55,7 +56,7 @@ abstract class AbstractHydrator
5556
*/
5657
protected UnitOfWork $uow;
5758

58-
private readonly TypeRegistry $typeRegistry;
59+
private readonly TypeProvider|TypeRegistry $typeProvider;
5960

6061
/**
6162
* Local ClassMetadata cache to avoid going to the EntityManager all the time.
@@ -90,7 +91,7 @@ public function __construct(protected EntityManagerInterface $em)
9091
{
9192
$this->platform = $em->getConnection()->getDatabasePlatform();
9293
$this->uow = $em->getUnitOfWork();
93-
$this->typeRegistry = TypeRegistryLocator::fromConnection($em->getConnection());
94+
$this->typeProvider = TypeProviderLocator::fromConnection($em->getConnection());
9495
}
9596

9697
/**
@@ -462,7 +463,7 @@ protected function hydrateColumnInfo(string $key): array|null
462463
$columnInfo = [
463464
'isIdentifier' => in_array($fieldName, $classMetadata->identifier, true),
464465
'fieldName' => $fieldName,
465-
'type' => $this->typeRegistry->get($fieldMapping->type),
466+
'type' => $this->typeProvider->get($fieldMapping->type),
466467
'dqlAlias' => $ownerMap,
467468
'enumType' => $this->rsm->enumMappings[$key] ?? null,
468469
];
@@ -490,7 +491,7 @@ protected function hydrateColumnInfo(string $key): array|null
490491
'isScalar' => true,
491492
'isNewObjectParameter' => true,
492493
'fieldName' => $this->rsm->scalarMappings[$key],
493-
'type' => $this->typeRegistry->get($this->rsm->typeMappings[$key]),
494+
'type' => $this->typeProvider->get($this->rsm->typeMappings[$key]),
494495
'argIndex' => $mapping['argIndex'],
495496
'objIndex' => $mapping['objIndex'],
496497
'enumType' => $this->rsm->enumMappings[$key] ?? null,
@@ -499,7 +500,7 @@ protected function hydrateColumnInfo(string $key): array|null
499500
case isset($this->rsm->scalarMappings[$key], $this->hints[LimitSubqueryWalker::FORCE_DBAL_TYPE_CONVERSION]):
500501
return $this->cache[$key] = [
501502
'fieldName' => $this->rsm->scalarMappings[$key],
502-
'type' => $this->typeRegistry->get($this->rsm->typeMappings[$key]),
503+
'type' => $this->typeProvider->get($this->rsm->typeMappings[$key]),
503504
'dqlAlias' => '',
504505
'enumType' => $this->rsm->enumMappings[$key] ?? null,
505506
];
@@ -508,7 +509,7 @@ protected function hydrateColumnInfo(string $key): array|null
508509
return $this->cache[$key] = [
509510
'isScalar' => true,
510511
'fieldName' => $this->rsm->scalarMappings[$key],
511-
'type' => $this->typeRegistry->get($this->rsm->typeMappings[$key]),
512+
'type' => $this->typeProvider->get($this->rsm->typeMappings[$key]),
512513
'enumType' => $this->rsm->enumMappings[$key] ?? null,
513514
];
514515

@@ -517,7 +518,7 @@ protected function hydrateColumnInfo(string $key): array|null
517518
$fieldName = $this->rsm->metaMappings[$key];
518519
$dqlAlias = $this->rsm->columnOwnerMap[$key];
519520
$type = isset($this->rsm->typeMappings[$key])
520-
? $this->typeRegistry->get($this->rsm->typeMappings[$key])
521+
? $this->typeProvider->get($this->rsm->typeMappings[$key])
521522
: null;
522523

523524
// Cache metadata fetch
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,26 @@
66

77
use Doctrine\DBAL\Connection;
88
use Doctrine\DBAL\Types\Type;
9+
use Doctrine\DBAL\Types\TypeProvider;
910
use Doctrine\DBAL\Types\TypeRegistry;
1011

1112
use function method_exists;
1213

1314
/**
14-
* Resolves the {@see TypeRegistry} owned by a DBAL connection.
15+
* Resolves the {@see TypeProvider} owned by a DBAL connection.
1516
*
1617
* @internal
1718
*/
18-
final class TypeRegistryLocator
19+
final class TypeProviderLocator
1920
{
20-
public static function fromConnection(Connection $connection): TypeRegistry
21+
public static function fromConnection(Connection $connection): TypeProvider|TypeRegistry
2122
{
2223
$configuration = $connection->getConfiguration();
2324

24-
// The method_exists() check is for DBAL < 4.5 compatibility, where Configuration::getTypeRegistry() does not exist yet.
25-
if (method_exists($configuration, 'getTypeRegistry')) {
26-
return $configuration->getTypeRegistry();
25+
// The method_exists() check is for DBAL < 4.5 compatibility, where Configuration::getTypeProvider() does not exist yet.
26+
// @phpstan-ignore function.alreadyNarrowedType (DBAL < 4.5 compatibility)
27+
if (method_exists($configuration, 'getTypeProvider')) {
28+
return $configuration->getTypeProvider();
2729
}
2830

2931
return Type::getTypeRegistry();

src/Mapping/Driver/DatabaseDriver.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use Doctrine\DBAL\Schema\PrimaryKeyConstraint;
1717
use Doctrine\DBAL\Schema\SchemaException;
1818
use Doctrine\DBAL\Schema\Table;
19-
use Doctrine\DBAL\Types\Type;
2019
use Doctrine\DBAL\Types\Types;
2120
use Doctrine\Inflector\Inflector;
2221
use Doctrine\Inflector\InflectorFactory;
@@ -422,7 +421,7 @@ private function buildFieldMapping(string $tableName, Column $column): array
422421
$fieldMapping = [
423422
'fieldName' => $this->getFieldNameForColumn($tableName, self::getAssetName($column), false),
424423
'columnName' => self::getAssetName($column),
425-
'type' => Type::getTypeRegistry()->lookupName($column->getType()),
424+
'type' => $column->getTypeName(),
426425
'nullable' => ! $column->getNotnull(),
427426
'options' => [
428427
'comment' => $column->getComment(),

src/Persisters/Collection/OneToManyPersister.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use Doctrine\Common\Collections\Criteria;
99
use Doctrine\DBAL\Exception as DBALException;
1010
use Doctrine\ORM\EntityNotFoundException;
11-
use Doctrine\ORM\Internal\TypeRegistryLocator;
11+
use Doctrine\ORM\Internal\TypeProviderLocator;
1212
use Doctrine\ORM\Mapping\MappingException;
1313
use Doctrine\ORM\Mapping\OneToManyAssociationMapping;
1414
use Doctrine\ORM\PersistentCollection;
@@ -219,13 +219,13 @@ private function deleteJoinedEntityCollection(PersistentCollection $collection):
219219
$idColumnNames = $rootClass->getIdentifierColumnNames();
220220
$idColumnList = implode(', ', $idColumnNames);
221221
$columnDefinitions = [];
222-
$typeRegistry = TypeRegistryLocator::fromConnection($this->em->getConnection());
222+
$typeProvider = TypeProviderLocator::fromConnection($this->em->getConnection());
223223

224224
foreach ($idColumnNames as $idColumnName) {
225225
$columnDefinitions[$idColumnName] = [
226226
'name' => $idColumnName,
227227
'notnull' => true,
228-
'type' => $typeRegistry->get(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $this->em)),
228+
'type' => $typeProvider->get(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $this->em)),
229229
];
230230
}
231231

src/Persisters/Entity/BasicEntityPersister.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
use Doctrine\DBAL\Platforms\AbstractPlatform;
1515
use Doctrine\DBAL\Result;
1616
use Doctrine\DBAL\Types\Type;
17+
use Doctrine\DBAL\Types\TypeProvider;
1718
use Doctrine\DBAL\Types\TypeRegistry;
1819
use Doctrine\DBAL\Types\Types;
1920
use Doctrine\ORM\Cache\Persister\CompatOrderings;
2021
use Doctrine\ORM\EntityManagerInterface;
21-
use Doctrine\ORM\Internal\TypeRegistryLocator;
22+
use Doctrine\ORM\Internal\TypeProviderLocator;
2223
use Doctrine\ORM\Mapping\AssociationMapping;
2324
use Doctrine\ORM\Mapping\ClassMetadata;
2425
use Doctrine\ORM\Mapping\JoinColumnMapping;
@@ -122,7 +123,7 @@ class BasicEntityPersister implements EntityPersister
122123
*/
123124
protected Connection $conn;
124125

125-
private readonly TypeRegistry $typeRegistry;
126+
private readonly TypeProvider|TypeRegistry $typeProvider;
126127

127128
/**
128129
* The database platform.
@@ -185,7 +186,7 @@ public function __construct(
185186
) {
186187
$this->conn = $em->getConnection();
187188
$this->platform = $this->conn->getDatabasePlatform();
188-
$this->typeRegistry = TypeRegistryLocator::fromConnection($this->conn);
189+
$this->typeProvider = TypeProviderLocator::fromConnection($this->conn);
189190
$this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy();
190191
$this->identifierFlattener = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory());
191192
$this->noLimitsContext = $this->currentPersisterContext = new CachedPersisterContext(
@@ -212,7 +213,7 @@ final protected function updateFilterHash(): void
212213

213214
final protected function getType(string $name): Type
214215
{
215-
return $this->typeRegistry->get($name);
216+
return $this->typeProvider->get($name);
216217
}
217218

218219
public function getClassMetadata(): ClassMetadata

src/Query/Exec/MultiTableDeleteExecutor.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use Doctrine\DBAL\Connection;
88
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
9-
use Doctrine\ORM\Internal\TypeRegistryLocator;
9+
use Doctrine\ORM\Internal\TypeProviderLocator;
1010
use Doctrine\ORM\Query\AST;
1111
use Doctrine\ORM\Query\AST\DeleteStatement;
1212
use Doctrine\ORM\Query\SqlWalker;
@@ -86,12 +86,12 @@ public function __construct(AST\Node $AST, SqlWalker $sqlWalker)
8686

8787
// 4. Store DDL for temporary identifier table.
8888
$columnDefinitions = [];
89-
$typeRegistry = TypeRegistryLocator::fromConnection($em->getConnection());
89+
$typeProvider = TypeProviderLocator::fromConnection($em->getConnection());
9090
foreach ($idColumnNames as $idColumnName) {
9191
$columnDefinitions[$idColumnName] = [
9292
'name' => $idColumnName,
9393
'notnull' => true,
94-
'type' => $typeRegistry->get(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em)),
94+
'type' => $typeProvider->get(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em)),
9595
];
9696
}
9797

src/Query/Exec/MultiTableUpdateExecutor.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use Doctrine\DBAL\Connection;
88
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
9-
use Doctrine\ORM\Internal\TypeRegistryLocator;
9+
use Doctrine\ORM\Internal\TypeProviderLocator;
1010
use Doctrine\ORM\Query\AST;
1111
use Doctrine\ORM\Query\AST\UpdateStatement;
1212
use Doctrine\ORM\Query\ParameterTypeInferer;
@@ -125,13 +125,13 @@ public function __construct(AST\Node $AST, SqlWalker $sqlWalker)
125125

126126
// 4. Store DDL for temporary identifier table.
127127
$columnDefinitions = [];
128-
$typeRegistry = TypeRegistryLocator::fromConnection($em->getConnection());
128+
$typeProvider = TypeProviderLocator::fromConnection($em->getConnection());
129129

130130
foreach ($idColumnNames as $idColumnName) {
131131
$columnDefinitions[$idColumnName] = [
132132
'name' => $idColumnName,
133133
'notnull' => true,
134-
'type' => $typeRegistry->get(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em)),
134+
'type' => $typeProvider->get(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em)),
135135
];
136136
}
137137

src/Query/ResultSetMappingBuilder.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use Doctrine\ORM\EntityManagerInterface;
88
use Doctrine\ORM\Internal\SQLResultCasing;
9-
use Doctrine\ORM\Internal\TypeRegistryLocator;
9+
use Doctrine\ORM\Internal\TypeProviderLocator;
1010
use Doctrine\ORM\Mapping\ClassMetadata;
1111
use Doctrine\ORM\Utility\PersisterHelper;
1212
use InvalidArgumentException;
@@ -239,7 +239,7 @@ private function getColumnAliasMap(
239239
public function generateSelectClause(array $tableAliases = []): string
240240
{
241241
$sql = '';
242-
$typeRegistry = TypeRegistryLocator::fromConnection($this->em->getConnection());
242+
$typeProvider = TypeProviderLocator::fromConnection($this->em->getConnection());
243243

244244
foreach ($this->columnOwnerMap as $columnName => $dqlAlias) {
245245
$tableAlias = $tableAliases[$dqlAlias] ?? $dqlAlias;
@@ -254,7 +254,7 @@ public function generateSelectClause(array $tableAliases = []): string
254254
$classFieldMapping = $class->fieldMappings[$fieldName];
255255
$columnSql = $tableAlias . '.' . $classFieldMapping->columnName;
256256

257-
$type = $typeRegistry->get($classFieldMapping->type);
257+
$type = $typeProvider->get($classFieldMapping->type);
258258
$columnSql = $type->convertToPHPValueSQL($columnSql, $this->em->getConnection()->getDatabasePlatform());
259259

260260
$sql .= $columnSql;

src/Query/SqlWalker.php

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
use Doctrine\DBAL\Connection;
99
use Doctrine\DBAL\LockMode;
1010
use Doctrine\DBAL\Platforms\AbstractPlatform;
11+
use Doctrine\DBAL\Types\Type;
12+
use Doctrine\DBAL\Types\TypeProvider;
1113
use Doctrine\DBAL\Types\TypeRegistry;
1214
use Doctrine\DBAL\Types\Types;
1315
use Doctrine\Deprecations\Deprecation;
1416
use Doctrine\ORM\EntityManagerInterface;
15-
use Doctrine\ORM\Internal\TypeRegistryLocator;
17+
use Doctrine\ORM\Internal\TypeProviderLocator;
1618
use Doctrine\ORM\Mapping\ClassMetadata;
1719
use Doctrine\ORM\Mapping\QuoteStrategy;
1820
use Doctrine\ORM\OptimisticLockException;
@@ -143,7 +145,7 @@ class SqlWalker
143145
*/
144146
private readonly QuoteStrategy $quoteStrategy;
145147

146-
private readonly TypeRegistry $typeRegistry;
148+
private readonly TypeProvider|TypeRegistry $typeProvider;
147149

148150
/** @phpstan-param array<string, QueryComponent> $queryComponents The query components (symbol table). */
149151
public function __construct(
@@ -156,7 +158,7 @@ public function __construct(
156158
$this->conn = $this->em->getConnection();
157159
$this->platform = $this->conn->getDatabasePlatform();
158160
$this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy();
159-
$this->typeRegistry = TypeRegistryLocator::fromConnection($this->conn);
161+
$this->typeProvider = TypeProviderLocator::fromConnection($this->conn);
160162
}
161163

162164
/**
@@ -1302,7 +1304,7 @@ public function walkSelectExpression(AST\SelectExpression $selectExpression): st
13021304
$columnAlias = $this->getSQLColumnAlias($fieldMapping->columnName);
13031305
$col = $sqlTableAlias . '.' . $columnName;
13041306

1305-
$type = $this->typeRegistry->get($fieldMapping->type);
1307+
$type = $this->typeProvider->get($fieldMapping->type);
13061308
$col = $type->convertToPHPValueSQL($col, $this->conn->getDatabasePlatform());
13071309

13081310
$sql .= $col . ' AS ' . $columnAlias;
@@ -1357,8 +1359,10 @@ public function walkSelectExpression(AST\SelectExpression $selectExpression): st
13571359
Query\AST\ExpressionWithReturnType::class,
13581360
);
13591361

1360-
// @phpstan-ignore method.deprecatedInterface
1361-
$this->rsm->addScalarResult($columnAlias, $resultAlias, $this->typeRegistry->lookupName($expr->getReturnType()));
1362+
// TypedExpression only exposes a Type instance, so the name has to be looked up.
1363+
// This branch and Type::lookupName() are both deprecated and go away together.
1364+
// @phpstan-ignore method.deprecatedInterface, staticMethod.deprecated
1365+
$this->rsm->addScalarResult($columnAlias, $resultAlias, Type::lookupName($expr->getReturnType()));
13621366

13631367
break;
13641368
}
@@ -1443,7 +1447,7 @@ public function walkObjectExpression(string $dqlAlias, array $partialFieldSet, s
14431447

14441448
$col = $sqlTableAlias . '.' . $quotedColumnName;
14451449

1446-
$type = $this->typeRegistry->get($mapping->type);
1450+
$type = $this->typeProvider->get($mapping->type);
14471451
$col = $type->convertToPHPValueSQL($col, $this->platform);
14481452

14491453
$sqlParts[] = $col . ' AS ' . $columnAlias;
@@ -1478,7 +1482,7 @@ public function walkObjectExpression(string $dqlAlias, array $partialFieldSet, s
14781482

14791483
$col = $sqlTableAlias . '.' . $quotedColumnName;
14801484

1481-
$type = $this->typeRegistry->get($mapping->type);
1485+
$type = $this->typeProvider->get($mapping->type);
14821486
$col = $type->convertToPHPValueSQL($col, $this->platform);
14831487

14841488
$sqlParts[] = $col . ' AS ' . $columnAlias;
@@ -1583,7 +1587,7 @@ public function walkNewObject(AST\NewObjectExpression $newObjectExpression, stri
15831587
$fieldType = $fieldMapping->type;
15841588
$col = trim($e->dispatch($this));
15851589

1586-
$type = $this->typeRegistry->get($fieldType);
1590+
$type = $this->typeProvider->get($fieldType);
15871591
$col = $type->convertToPHPValueSQL($col, $this->platform);
15881592

15891593
$sqlSelectExpressions[] = $col . ' AS ' . $columnAlias;
@@ -2217,8 +2221,8 @@ public function walkInputParameter(AST\InputParameter $inputParam): string
22172221

22182222
if ($parameter) {
22192223
$type = $parameter->getType();
2220-
if (is_string($type) && $this->typeRegistry->has($type)) {
2221-
return $this->typeRegistry->get($type)->convertToDatabaseValueSQL('?', $this->platform);
2224+
if (is_string($type) && $this->typeProvider->has($type)) {
2225+
return $this->typeProvider->get($type)->convertToDatabaseValueSQL('?', $this->platform);
22222226
}
22232227
}
22242228

src/Tools/Pagination/LimitSubqueryWalker.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace Doctrine\ORM\Tools\Pagination;
66

7-
use Doctrine\ORM\Internal\TypeRegistryLocator;
7+
use Doctrine\ORM\Internal\TypeProviderLocator;
88
use Doctrine\ORM\Query;
99
use Doctrine\ORM\Query\AST\Functions\IdentityFunction;
1010
use Doctrine\ORM\Query\AST\Node;
@@ -51,7 +51,7 @@ public function walkSelectStatement(SelectStatement $selectStatement): void
5151

5252
$query->setHint(
5353
self::IDENTIFIER_TYPE,
54-
TypeRegistryLocator::fromConnection($query->getEntityManager()->getConnection())->get($rootClass->fieldMappings[$identifier]->type),
54+
TypeProviderLocator::fromConnection($query->getEntityManager()->getConnection())->get($rootClass->fieldMappings[$identifier]->type),
5555
);
5656

5757
$query->setHint(self::FORCE_DBAL_TYPE_CONVERSION, true);

0 commit comments

Comments
 (0)