Skip to content

Commit fd11d62

Browse files
committed
Inject a ServiceLocator into TypeRegistry for lazy type resolution
Replace the eager array of type instances/definitions with a Symfony ServiceLocator, leveraging the new ServiceProviderInterface support in DBAL's TypeRegistry. Types are now instantiated on first use rather than at container build time.
1 parent 1db2468 commit fd11d62

3 files changed

Lines changed: 33 additions & 46 deletions

File tree

src/DependencyInjection/Compiler/DatabaseTypePass.php

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

77
use Doctrine\DBAL\Types\TypeRegistry;
88
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
9+
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
910
use Symfony\Component\DependencyInjection\ContainerBuilder;
1011
use Symfony\Component\DependencyInjection\Definition;
1112
use Symfony\Component\DependencyInjection\Reference;
@@ -48,11 +49,11 @@ public function process(ContainerBuilder $container): void
4849
}
4950

5051
foreach (array_keys($container->getParameter('doctrine.connections')) as $name) {
51-
$instances = [];
52+
$services = [];
5253

53-
// Config-based types become inline definitions
54+
// Config-based types become inline definitions in the ServiceLocator
5455
foreach ($configTypes as $typeName => $typeConfig) {
55-
$instances[$typeName] = (new Definition($typeConfig['class']))->setShared(false);
56+
$services[$typeName] = new Definition($typeConfig['class']);
5657
}
5758

5859
// Service-tagged types: global (no connection restriction) or matching this connection
@@ -62,14 +63,16 @@ public function process(ContainerBuilder $container): void
6263
continue;
6364
}
6465

65-
$instances[$tag['type'] ?? $id] = new Reference($id);
66+
$services[$tag['type'] ?? $id] = new Reference($id);
6667
}
6768
}
6869

6970
$registryId = sprintf('doctrine.dbal.%s_connection.type_registry', $name);
7071
$registryRef = new Reference($registryId);
7172

72-
$container->setDefinition($registryId, new Definition(TypeRegistry::class, [$instances]));
73+
// Inject a ServiceLocator so types are resolved lazily on first use
74+
$locatorRef = ServiceLocatorTagPass::register($container, $services);
75+
$container->setDefinition($registryId, new Definition(TypeRegistry::class, [$locatorRef]));
7376

7477
$container
7578
->getDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name))

src/DependencyInjection/DoctrineExtension.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
66

7+
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDatabaseType;
78
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
89
use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener;
9-
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDatabaseType;
1010
use Doctrine\Bundle\DoctrineBundle\Attribute\AsMiddleware;
1111
use Doctrine\Bundle\DoctrineBundle\CacheWarmer\DoctrineMetadataCacheWarmer;
1212
use Doctrine\Bundle\DoctrineBundle\ConnectionFactory;
@@ -563,7 +563,6 @@ private function dbalLoad(array $config, ContainerBuilder $container): void
563563
$definition->addTag('doctrine.dbal.type', $tag);
564564
});
565565

566-
567566
$container->registerAttributeForAutoconfiguration(AsMiddleware::class, static function (ChildDefinition $definition, AsMiddleware $attribute): void {
568567
$priority = isset($attribute->priority) ? ['priority' => $attribute->priority] : [];
569568

tests/DependencyInjection/Compiler/DatabaseTypePassTest.php

Lines changed: 24 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
1717
use Symfony\Component\DependencyInjection\Reference;
1818

19+
use function array_filter;
20+
use function array_values;
1921
use function sprintf;
22+
use function sys_get_temp_dir;
2023

2124
class DatabaseTypePassTest extends TestCase
2225
{
@@ -204,7 +207,6 @@ static function (ContainerBuilder $container): void {
204207
}
205208
}
206209

207-
208210
/** @param array<string, array{class: string}> $configTypes */
209211
private function createContainer(
210212
callable $func,
@@ -213,11 +215,11 @@ private function createContainer(
213215
): ContainerBuilder {
214216
$params = ['kernel.debug' => false];
215217
if ($withOrm) {
216-
$params['kernel.bundles'] = [];
217-
$params['kernel.bundles_metadata'] = [];
218-
$params['kernel.project_dir'] = sys_get_temp_dir();
219-
$params['kernel.environment'] = 'test';
220-
$params['kernel.build_dir'] = sys_get_temp_dir();
218+
$params['kernel.bundles'] = [];
219+
$params['kernel.bundles_metadata'] = [];
220+
$params['kernel.project_dir'] = sys_get_temp_dir();
221+
$params['kernel.environment'] = 'test';
222+
$params['kernel.build_dir'] = sys_get_temp_dir();
221223
}
222224

223225
$container = new ContainerBuilder(new ParameterBag($params));
@@ -260,39 +262,28 @@ private function assertTypeRegistered(
260262
string $typeName,
261263
string $typeClass,
262264
): void {
263-
$registryDef = $container->getDefinition(sprintf('registry_%s', $connName));
264-
$instances = $registryDef->getArgument(0);
265+
$registry = $this->getRegistry($container, $connName);
265266

266-
self::assertArrayHasKey($typeName, $instances, sprintf(
267+
self::assertTrue($registry->has($typeName), sprintf(
267268
'Type "%s" not found in TypeRegistry for connection "%s".',
268269
$typeName,
269270
$connName,
270271
));
271272

272-
self::assertSame($typeClass, $this->resolveTypeClass($container, $instances[$typeName]));
273-
}
274-
275-
private function resolveTypeClass(ContainerBuilder $container, mixed $entry): string
276-
{
277-
if ($entry instanceof Reference) {
278-
$entry = $container->getDefinition((string) $entry);
279-
}
280-
281-
return $entry instanceof Definition ? $entry->getClass() ?? '' : '';
273+
self::assertInstanceOf($typeClass, $registry->get($typeName));
282274
}
283275

284276
private function assertTypeNotRegistered(ContainerBuilder $container, string $connName, string $typeName): void
285277
{
286278
$registryId = sprintf('doctrine.dbal.%s_connection.type_registry', $connName);
287279

288280
if (! $container->hasDefinition($registryId)) {
289-
// No TypeRegistry at all for this connection — type is definitely not registered
290281
return;
291282
}
292283

293-
$instances = $container->getDefinition($registryId)->getArgument(0);
284+
$registry = $this->getRegistry($container, $connName);
294285

295-
self::assertArrayNotHasKey($typeName, $instances, sprintf(
286+
self::assertFalse($registry->has($typeName), sprintf(
296287
'Type "%s" should not be registered in TypeRegistry for connection "%s".',
297288
$typeName,
298289
$connName,
@@ -305,32 +296,26 @@ private function assertConfigTypeRegistered(
305296
string $typeName,
306297
string $typeClass,
307298
): void {
308-
$registryDef = $container->getDefinition(sprintf('registry_%s', $connName));
309-
$instances = $registryDef->getArgument(0);
310-
311-
self::assertArrayHasKey($typeName, $instances, sprintf(
312-
'Config type "%s" not found in TypeRegistry for connection "%s".',
313-
$typeName,
314-
$connName,
315-
));
299+
$this->assertTypeRegistered($container, $connName, $typeName, $typeClass);
300+
}
316301

317-
$typeDef = $instances[$typeName];
318-
self::assertInstanceOf(Definition::class, $typeDef);
319-
self::assertSame($typeClass, $typeDef->getClass());
302+
private function getRegistry(ContainerBuilder $container, string $connName): TypeRegistry
303+
{
304+
return $container->get(sprintf('registry_%s', $connName));
320305
}
321306
}
322307

323308
class MoneyType extends Type
324309
{
325-
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
310+
public function getSQLDeclaration(mixed $column, AbstractPlatform $platform): string
326311
{
327312
return 'NUMERIC(10,2)';
328313
}
329314
}
330315

331316
class UuidType extends Type
332317
{
333-
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
318+
public function getSQLDeclaration(mixed $column, AbstractPlatform $platform): string
334319
{
335320
return 'CHAR(36)';
336321
}
@@ -339,7 +324,7 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform): st
339324
#[AsDatabaseType(name: 'money')]
340325
class AutoconfiguredMoneyType extends Type
341326
{
342-
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
327+
public function getSQLDeclaration(mixed $column, AbstractPlatform $platform): string
343328
{
344329
return 'NUMERIC(10,2)';
345330
}
@@ -348,7 +333,7 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform): st
348333
#[AsDatabaseType(name: 'money', connection: 'conn1')]
349334
class AutoconfiguredMoneyTypeForConn1 extends Type
350335
{
351-
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
336+
public function getSQLDeclaration(mixed $column, AbstractPlatform $platform): string
352337
{
353338
return 'NUMERIC(10,2)';
354339
}
@@ -357,7 +342,7 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform): st
357342
#[AsDatabaseType]
358343
class AutoconfiguredAnonymousType extends Type
359344
{
360-
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
345+
public function getSQLDeclaration(mixed $column, AbstractPlatform $platform): string
361346
{
362347
return 'VARCHAR(255)';
363348
}
@@ -367,7 +352,7 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform): st
367352
#[AsDatabaseType(name: 'multi_alias', connection: 'conn2')]
368353
class MultiConnectionType extends Type
369354
{
370-
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
355+
public function getSQLDeclaration(mixed $column, AbstractPlatform $platform): string
371356
{
372357
return 'VARCHAR(255)';
373358
}

0 commit comments

Comments
 (0)