Completed
Push — master ( efc5ed...26c16f )
by Andreas
02:05 queued 12s
created

DoctrineExtension::getMetadataDriverClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
4
5
use Doctrine\Bundle\DoctrineBundle\Dbal\RegexSchemaAssetFilter;
6
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
7
use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface;
8
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface;
9
use Doctrine\ORM\UnitOfWork;
10
use LogicException;
11
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
12
use Symfony\Bridge\Doctrine\Messenger\DoctrineClearEntityManagerWorkerSubscriber;
13
use Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddleware;
14
use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor;
15
use Symfony\Bridge\Doctrine\Validator\DoctrineLoader;
16
use Symfony\Component\Cache\Adapter\ArrayAdapter;
17
use Symfony\Component\Cache\DoctrineProvider;
18
use Symfony\Component\Config\FileLocator;
19
use Symfony\Component\DependencyInjection\Alias;
20
use Symfony\Component\DependencyInjection\ChildDefinition;
21
use Symfony\Component\DependencyInjection\ContainerBuilder;
22
use Symfony\Component\DependencyInjection\Definition;
23
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
24
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
25
use Symfony\Component\DependencyInjection\Reference;
26
use Symfony\Component\Form\AbstractType;
27
use Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineTransportFactory;
28
use Symfony\Component\Messenger\MessageBusInterface;
29
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
30
use function class_exists;
31
use function sprintf;
32
33
/**
34
 * DoctrineExtension is an extension for the Doctrine DBAL and ORM library.
35
 */
36
class DoctrineExtension extends AbstractDoctrineExtension
37
{
38
    /** @var string */
39
    private $defaultConnection;
40
41
    /**
42
     * {@inheritDoc}
43
     */
44
    public function load(array $configs, ContainerBuilder $container)
45
    {
46
        $configuration = $this->getConfiguration($configs, $container);
47
        $config        = $this->processConfiguration($configuration, $configs);
0 ignored issues
show
Bug introduced by
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 46 can be null; however, Symfony\Component\Depend...:processConfiguration() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
48
49
        if (! empty($config['dbal'])) {
50
            $this->dbalLoad($config['dbal'], $container);
51
52
            $this->loadMessengerServices($container);
53
        }
54
55
        if (empty($config['orm'])) {
56
            return;
57
        }
58
59
        if (empty($config['dbal'])) {
60
            throw new LogicException('Configuring the ORM layer requires to configure the DBAL layer as well.');
61
        }
62
63
        $this->ormLoad($config['orm'], $container);
64
    }
65
66
    /**
67
     * Loads the DBAL configuration.
68
     *
69
     * Usage example:
70
     *
71
     *      <doctrine:dbal id="myconn" dbname="sfweb" user="root" />
72
     *
73
     * @param array            $config    An array of configuration settings
74
     * @param ContainerBuilder $container A ContainerBuilder instance
75
     */
76
    protected function dbalLoad(array $config, ContainerBuilder $container)
77
    {
78
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
79
        $loader->load('dbal.xml');
80
81
        if (empty($config['default_connection'])) {
82
            $keys                         = array_keys($config['connections']);
83
            $config['default_connection'] = reset($keys);
84
        }
85
86
        $this->defaultConnection = $config['default_connection'];
87
88
        $container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection));
89
        $container->getAlias('database_connection')->setPublic(true);
90
        $container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false));
91
92
        $container->setParameter('doctrine.dbal.connection_factory.types', $config['types']);
93
94
        $connections = [];
95
96
        foreach (array_keys($config['connections']) as $name) {
97
            $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name);
98
        }
99
100
        $container->setParameter('doctrine.connections', $connections);
101
        $container->setParameter('doctrine.default_connection', $this->defaultConnection);
102
103
        foreach ($config['connections'] as $name => $connection) {
104
            $this->loadDbalConnection($name, $connection, $container);
105
        }
106
    }
107
108
    /**
109
     * Loads a configured DBAL connection.
110
     *
111
     * @param string           $name       The name of the connection
112
     * @param array            $connection A dbal connection configuration.
113
     * @param ContainerBuilder $container  A ContainerBuilder instance
114
     */
115
    protected function loadDbalConnection($name, array $connection, ContainerBuilder $container)
116
    {
117
        $configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new ChildDefinition('doctrine.dbal.connection.configuration'));
118
        $logger        = null;
119
        if ($connection['logging']) {
120
            $logger = new Reference('doctrine.dbal.logger');
121
        }
122
        unset($connection['logging']);
123
124
        if ($connection['profiling']) {
125
            $profilingAbstractId = $connection['profiling_collect_backtrace'] ?
126
                'doctrine.dbal.logger.backtrace' :
127
                'doctrine.dbal.logger.profiling';
128
129
            $profilingLoggerId = $profilingAbstractId . '.' . $name;
130
            $container->setDefinition($profilingLoggerId, new ChildDefinition($profilingAbstractId));
131
            $profilingLogger = new Reference($profilingLoggerId);
132
            $container->getDefinition('data_collector.doctrine')
133
                ->addMethodCall('addLogger', [$name, $profilingLogger])
134
                ->replaceArgument(1, $connection['profiling_collect_schema_errors']);
135
136
            if ($logger !== null) {
137
                $chainLogger = new ChildDefinition('doctrine.dbal.logger.chain');
138
                $chainLogger->addMethodCall('addLogger', [$profilingLogger]);
139
140
                $loggerId = 'doctrine.dbal.logger.chain.' . $name;
141
                $container->setDefinition($loggerId, $chainLogger);
142
                $logger = new Reference($loggerId);
143
            } else {
144
                $logger = $profilingLogger;
145
            }
146
        }
147
        unset(
148
            $connection['profiling'],
149
            $connection['profiling_collect_backtrace'],
150
            $connection['profiling_collect_schema_errors']
151
        );
152
153
        if (isset($connection['auto_commit'])) {
154
            $configuration->addMethodCall('setAutoCommit', [$connection['auto_commit']]);
155
        }
156
157
        unset($connection['auto_commit']);
158
159
        if (isset($connection['schema_filter']) && $connection['schema_filter']) {
160
            $definition = new Definition(RegexSchemaAssetFilter::class, [$connection['schema_filter']]);
161
            $definition->addTag('doctrine.dbal.schema_filter', ['connection' => $name]);
162
            $container->setDefinition(sprintf('doctrine.dbal.%s_regex_schema_filter', $name), $definition);
163
        }
164
165
        unset($connection['schema_filter']);
166
167
        if ($logger) {
168
            $configuration->addMethodCall('setSQLLogger', [$logger]);
169
        }
170
171
        // event manager
172
        $container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new ChildDefinition('doctrine.dbal.connection.event_manager'));
173
174
        // connection
175
        $options = $this->getConnectionOptions($connection);
176
177
        $def = $container
178
            ->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new ChildDefinition('doctrine.dbal.connection'))
179
            ->setPublic(true)
180
            ->setArguments([
181
                $options,
182
                new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)),
183
                new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)),
184
                $connection['mapping_types'],
185
            ]);
186
187
        // Set class in case "wrapper_class" option was used to assist IDEs
188
        if (isset($options['wrapperClass'])) {
189
            $def->setClass($options['wrapperClass']);
190
        }
191
192
        if (! empty($connection['use_savepoints'])) {
193
            $def->addMethodCall('setNestTransactionsWithSavepoints', [$connection['use_savepoints']]);
194
        }
195
196
        // Create a shard_manager for this connection
197
        if (! isset($options['shards'])) {
198
            return;
199
        }
200
201
        $shardManagerDefinition = new Definition($options['shardManagerClass'], [new Reference(sprintf('doctrine.dbal.%s_connection', $name))]);
202
        $container->setDefinition(sprintf('doctrine.dbal.%s_shard_manager', $name), $shardManagerDefinition);
203
    }
204
205
    protected function getConnectionOptions($connection)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
206
    {
207
        $options = $connection;
208
209
        if (isset($options['platform_service'])) {
210
            $options['platform'] = new Reference($options['platform_service']);
211
            unset($options['platform_service']);
212
        }
213
        unset($options['mapping_types']);
214
215
        if (isset($options['shard_choser_service'])) {
216
            $options['shard_choser'] = new Reference($options['shard_choser_service']);
217
            unset($options['shard_choser_service']);
218
        }
219
220
        foreach ([
221
            'options' => 'driverOptions',
222
            'driver_class' => 'driverClass',
223
            'wrapper_class' => 'wrapperClass',
224
            'keep_slave' => 'keepSlave',
225
            'shard_choser' => 'shardChoser',
226
            'shard_manager_class' => 'shardManagerClass',
227
            'server_version' => 'serverVersion',
228
            'default_table_options' => 'defaultTableOptions',
229
        ] as $old => $new) {
230
            if (! isset($options[$old])) {
231
                continue;
232
            }
233
234
            $options[$new] = $options[$old];
235
            unset($options[$old]);
236
        }
237
238
        if (! empty($options['slaves']) && ! empty($options['shards'])) {
239
            throw new InvalidArgumentException('Sharding and master-slave connection cannot be used together');
240
        }
241
242
        if (! empty($options['slaves'])) {
243
            $nonRewrittenKeys = [
244
                'driver' => true,
245
                'driverOptions' => true,
246
                'driverClass' => true,
247
                'wrapperClass' => true,
248
                'keepSlave' => true,
249
                'shardChoser' => true,
250
                'platform' => true,
251
                'slaves' => true,
252
                'master' => true,
253
                'shards' => true,
254
                'serverVersion' => true,
255
                'defaultTableOptions' => true,
256
                // included by safety but should have been unset already
257
                'logging' => true,
258
                'profiling' => true,
259
                'mapping_types' => true,
260
                'platform_service' => true,
261
            ];
262 View Code Duplication
            foreach ($options as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
263
                if (isset($nonRewrittenKeys[$key])) {
264
                    continue;
265
                }
266
                $options['master'][$key] = $value;
267
                unset($options[$key]);
268
            }
269
            if (empty($options['wrapperClass'])) {
270
                // Change the wrapper class only if the user does not already forced using a custom one.
271
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Connections\\MasterSlaveConnection';
272
            }
273
        } else {
274
            unset($options['slaves']);
275
        }
276
277
        if (! empty($options['shards'])) {
278
            $nonRewrittenKeys = [
279
                'driver' => true,
280
                'driverOptions' => true,
281
                'driverClass' => true,
282
                'wrapperClass' => true,
283
                'keepSlave' => true,
284
                'shardChoser' => true,
285
                'platform' => true,
286
                'slaves' => true,
287
                'global' => true,
288
                'shards' => true,
289
                'serverVersion' => true,
290
                'defaultTableOptions' => true,
291
                // included by safety but should have been unset already
292
                'logging' => true,
293
                'profiling' => true,
294
                'mapping_types' => true,
295
                'platform_service' => true,
296
            ];
297 View Code Duplication
            foreach ($options as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
298
                if (isset($nonRewrittenKeys[$key])) {
299
                    continue;
300
                }
301
                $options['global'][$key] = $value;
302
                unset($options[$key]);
303
            }
304
            if (empty($options['wrapperClass'])) {
305
                // Change the wrapper class only if the user does not already forced using a custom one.
306
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardConnection';
307
            }
308
            if (empty($options['shardManagerClass'])) {
309
                // Change the shard manager class only if the user does not already forced using a custom one.
310
                $options['shardManagerClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardManager';
311
            }
312
        } else {
313
            unset($options['shards']);
314
        }
315
316
        return $options;
317
    }
318
319
    /**
320
     * Loads the Doctrine ORM configuration.
321
     *
322
     * Usage example:
323
     *
324
     *     <doctrine:orm id="mydm" connection="myconn" />
325
     *
326
     * @param array            $config    An array of configuration settings
327
     * @param ContainerBuilder $container A ContainerBuilder instance
328
     */
329
    protected function ormLoad(array $config, ContainerBuilder $container)
330
    {
331
        if (! class_exists(UnitOfWork::class)) {
332
            throw new LogicException('To configure the ORM layer, you must first install the doctrine/orm package.');
333
        }
334
335
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
336
        $loader->load('orm.xml');
337
338
        if (class_exists(AbstractType::class)) {
339
            $container->getDefinition('form.type.entity')->addTag('kernel.reset', ['method' => 'reset']);
340
        }
341
342
        $entityManagers = [];
343
        foreach (array_keys($config['entity_managers']) as $name) {
344
            $entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name);
345
        }
346
        $container->setParameter('doctrine.entity_managers', $entityManagers);
347
348
        if (empty($config['default_entity_manager'])) {
349
            $tmp                              = array_keys($entityManagers);
350
            $config['default_entity_manager'] = reset($tmp);
351
        }
352
        $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']);
353
354
        $options = ['auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace'];
355
        foreach ($options as $key) {
356
            $container->setParameter('doctrine.orm.' . $key, $config[$key]);
357
        }
358
359
        $container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager']));
360
        $container->getAlias('doctrine.orm.entity_manager')->setPublic(true);
361
362
        $config['entity_managers'] = $this->fixManagersAutoMappings($config['entity_managers'], $container->getParameter('kernel.bundles'));
363
364
        foreach ($config['entity_managers'] as $name => $entityManager) {
365
            $entityManager['name'] = $name;
366
            $this->loadOrmEntityManager($entityManager, $container);
367
368
            if (interface_exists(PropertyInfoExtractorInterface::class)) {
369
                $this->loadPropertyInfoExtractor($name, $container);
370
            }
371
372
            $this->loadValidatorLoader($name, $container);
373
        }
374
375
        if ($config['resolve_target_entities']) {
376
            $def = $container->findDefinition('doctrine.orm.listeners.resolve_target_entity');
377
            foreach ($config['resolve_target_entities'] as $name => $implementation) {
378
                $def->addMethodCall('addResolveTargetEntity', [
379
                    $name,
380
                    $implementation,
381
                    [],
382
                ]);
383
            }
384
385
            $def->addTag('doctrine.event_subscriber');
386
        }
387
388
        $container->registerForAutoconfiguration(ServiceEntityRepositoryInterface::class)
389
            ->addTag(ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG);
390
391
        $container->registerForAutoconfiguration(EventSubscriberInterface::class)
392
            ->addTag('doctrine.event_subscriber');
393
    }
394
395
    /**
396
     * Loads a configured ORM entity manager.
397
     *
398
     * @param array            $entityManager A configured ORM entity manager.
399
     * @param ContainerBuilder $container     A ContainerBuilder instance
400
     */
401
    protected function loadOrmEntityManager(array $entityManager, ContainerBuilder $container)
402
    {
403
        $ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new ChildDefinition('doctrine.orm.configuration'));
404
405
        $this->loadOrmEntityManagerMappingInformation($entityManager, $ormConfigDef, $container);
406
        $this->loadOrmCacheDrivers($entityManager, $container);
407
408
        if (isset($entityManager['entity_listener_resolver']) && $entityManager['entity_listener_resolver']) {
409
            $container->setAlias(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $entityManager['entity_listener_resolver']);
410
        } else {
411
            $definition = new Definition('%doctrine.orm.entity_listener_resolver.class%');
412
            $definition->addArgument(new Reference('service_container'));
413
            $container->setDefinition(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $definition);
414
        }
415
416
        $methods = [
417
            'setMetadataCacheImpl' => new Reference(sprintf('doctrine.orm.%s_metadata_cache', $entityManager['name'])),
418
            'setQueryCacheImpl' => new Reference(sprintf('doctrine.orm.%s_query_cache', $entityManager['name'])),
419
            'setResultCacheImpl' => new Reference(sprintf('doctrine.orm.%s_result_cache', $entityManager['name'])),
420
            'setMetadataDriverImpl' => new Reference('doctrine.orm.' . $entityManager['name'] . '_metadata_driver'),
421
            'setProxyDir' => '%doctrine.orm.proxy_dir%',
422
            'setProxyNamespace' => '%doctrine.orm.proxy_namespace%',
423
            'setAutoGenerateProxyClasses' => '%doctrine.orm.auto_generate_proxy_classes%',
424
            'setClassMetadataFactoryName' => $entityManager['class_metadata_factory_name'],
425
            'setDefaultRepositoryClassName' => $entityManager['default_repository_class'],
426
            'setNamingStrategy' => new Reference($entityManager['naming_strategy']),
427
            'setQuoteStrategy' => new Reference($entityManager['quote_strategy']),
428
            'setEntityListenerResolver' => new Reference(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name'])),
429
        ];
430
431
        $listenerId        = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $entityManager['name']);
432
        $listenerDef       = $container->setDefinition($listenerId, new Definition('%doctrine.orm.listeners.attach_entity_listeners.class%'));
433
        $listenerTagParams = ['event' => 'loadClassMetadata'];
434
        if (isset($entityManager['connection'])) {
435
            $listenerTagParams['connection'] = $entityManager['connection'];
436
        }
437
        $listenerDef->addTag('doctrine.event_listener', $listenerTagParams);
438
439
        if (isset($entityManager['second_level_cache'])) {
440
            $this->loadOrmSecondLevelCache($entityManager, $ormConfigDef, $container);
441
        }
442
443
        if ($entityManager['repository_factory']) {
444
            $methods['setRepositoryFactory'] = new Reference($entityManager['repository_factory']);
445
        }
446
447
        foreach ($methods as $method => $arg) {
448
            $ormConfigDef->addMethodCall($method, [$arg]);
449
        }
450
451
        foreach ($entityManager['hydrators'] as $name => $class) {
452
            $ormConfigDef->addMethodCall('addCustomHydrationMode', [$name, $class]);
453
        }
454
455
        if (! empty($entityManager['dql'])) {
456
            foreach ($entityManager['dql']['string_functions'] as $name => $function) {
457
                $ormConfigDef->addMethodCall('addCustomStringFunction', [$name, $function]);
458
            }
459
            foreach ($entityManager['dql']['numeric_functions'] as $name => $function) {
460
                $ormConfigDef->addMethodCall('addCustomNumericFunction', [$name, $function]);
461
            }
462
            foreach ($entityManager['dql']['datetime_functions'] as $name => $function) {
463
                $ormConfigDef->addMethodCall('addCustomDatetimeFunction', [$name, $function]);
464
            }
465
        }
466
467
        $enabledFilters    = [];
468
        $filtersParameters = [];
469
        foreach ($entityManager['filters'] as $name => $filter) {
470
            $ormConfigDef->addMethodCall('addFilter', [$name, $filter['class']]);
471
            if ($filter['enabled']) {
472
                $enabledFilters[] = $name;
473
            }
474
            if (! $filter['parameters']) {
475
                continue;
476
            }
477
478
            $filtersParameters[$name] = $filter['parameters'];
479
        }
480
481
        $managerConfiguratorName = sprintf('doctrine.orm.%s_manager_configurator', $entityManager['name']);
482
        $container
483
            ->setDefinition($managerConfiguratorName, new ChildDefinition('doctrine.orm.manager_configurator.abstract'))
484
            ->replaceArgument(0, $enabledFilters)
485
            ->replaceArgument(1, $filtersParameters);
486
487
        if (! isset($entityManager['connection'])) {
488
            $entityManager['connection'] = $this->defaultConnection;
489
        }
490
491
        $container
492
            ->setDefinition(sprintf('doctrine.orm.%s_entity_manager', $entityManager['name']), new ChildDefinition('doctrine.orm.entity_manager.abstract'))
493
            ->setPublic(true)
494
            ->setArguments([
495
                new Reference(sprintf('doctrine.dbal.%s_connection', $entityManager['connection'])),
496
                new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])),
497
            ])
498
            ->setConfigurator([new Reference($managerConfiguratorName), 'configure']);
499
500
        $container->setAlias(
501
            sprintf('doctrine.orm.%s_entity_manager.event_manager', $entityManager['name']),
502
            new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $entityManager['connection']), false)
503
        );
504
505
        if (! isset($entityManager['entity_listeners'])) {
506
            return;
507
        }
508
509
        if (! isset($listenerDef)) {
510
            throw new InvalidArgumentException('Entity listeners configuration requires doctrine-orm 2.5.0 or newer');
511
        }
512
513
        $entities = $entityManager['entity_listeners']['entities'];
514
515
        foreach ($entities as $entityListenerClass => $entity) {
516
            foreach ($entity['listeners'] as $listenerClass => $listener) {
517
                foreach ($listener['events'] as $listenerEvent) {
518
                    $listenerEventName = $listenerEvent['type'];
519
                    $listenerMethod    = $listenerEvent['method'];
520
521
                    $listenerDef->addMethodCall('addEntityListener', [
522
                        $entityListenerClass,
523
                        $listenerClass,
524
                        $listenerEventName,
525
                        $listenerMethod,
526
                    ]);
527
                }
528
            }
529
        }
530
    }
531
532
    /**
533
     * Loads an ORM entity managers bundle mapping information.
534
     *
535
     * There are two distinct configuration possibilities for mapping information:
536
     *
537
     * 1. Specify a bundle and optionally details where the entity and mapping information reside.
538
     * 2. Specify an arbitrary mapping location.
539
     *
540
     * @param array            $entityManager A configured ORM entity manager
541
     * @param Definition       $ormConfigDef  A Definition instance
542
     * @param ContainerBuilder $container     A ContainerBuilder instance
543
     *
544
     * @example
545
     *
546
     *  doctrine.orm:
547
     *     mappings:
548
     *         MyBundle1: ~
549
     *         MyBundle2: yml
550
     *         MyBundle3: { type: annotation, dir: Entities/ }
551
     *         MyBundle4: { type: xml, dir: Resources/config/doctrine/mapping }
552
     *         MyBundle5:
553
     *             type: yml
554
     *             dir: bundle-mappings/
555
     *             alias: BundleAlias
556
     *         arbitrary_key:
557
     *             type: xml
558
     *             dir: %kernel.project_dir%/src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entities
559
     *             prefix: DoctrineExtensions\Entities\
560
     *             alias: DExt
561
     *
562
     * In the case of bundles everything is really optional (which leads to autodetection for this bundle) but
563
     * in the mappings key everything except alias is a required argument.
564
     */
565
    protected function loadOrmEntityManagerMappingInformation(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
566
    {
567
        // reset state of drivers and alias map. They are only used by this methods and children.
568
        $this->drivers  = [];
569
        $this->aliasMap = [];
570
571
        $this->loadMappingInformation($entityManager, $container);
572
        $this->registerMappingDrivers($entityManager, $container);
573
574
        $ormConfigDef->addMethodCall('setEntityNamespaces', [$this->aliasMap]);
575
    }
576
577
    /**
578
     * Loads an ORM second level cache bundle mapping information.
579
     *
580
     * @param array            $entityManager A configured ORM entity manager
581
     * @param Definition       $ormConfigDef  A Definition instance
582
     * @param ContainerBuilder $container     A ContainerBuilder instance
583
     *
584
     * @example
585
     *  entity_managers:
586
     *      default:
587
     *          second_level_cache:
588
     *              region_cache_driver: apc
589
     *              log_enabled: true
590
     *              regions:
591
     *                  my_service_region:
592
     *                      type: service
593
     *                      service : "my_service_region"
594
     *
595
     *                  my_query_region:
596
     *                      lifetime: 300
597
     *                      cache_driver: array
598
     *                      type: filelock
599
     *
600
     *                  my_entity_region:
601
     *                      lifetime: 600
602
     *                      cache_driver:
603
     *                          type: apc
604
     */
605
    protected function loadOrmSecondLevelCache(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
606
    {
607
        $driverId = null;
608
        $enabled  = $entityManager['second_level_cache']['enabled'];
609
610
        if (isset($entityManager['second_level_cache']['region_cache_driver'])) {
611
            $driverName = 'second_level_cache.region_cache_driver';
612
            $driverMap  = $entityManager['second_level_cache']['region_cache_driver'];
613
            $driverId   = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
614
        }
615
616
        $configId   = sprintf('doctrine.orm.%s_second_level_cache.cache_configuration', $entityManager['name']);
617
        $regionsId  = sprintf('doctrine.orm.%s_second_level_cache.regions_configuration', $entityManager['name']);
618
        $driverId   = $driverId ?: sprintf('doctrine.orm.%s_second_level_cache.region_cache_driver', $entityManager['name']);
619
        $configDef  = $container->setDefinition($configId, new Definition('%doctrine.orm.second_level_cache.cache_configuration.class%'));
620
        $regionsDef = $container->setDefinition($regionsId, new Definition('%doctrine.orm.second_level_cache.regions_configuration.class%'));
621
622
        $slcFactoryId = sprintf('doctrine.orm.%s_second_level_cache.default_cache_factory', $entityManager['name']);
623
        $factoryClass = isset($entityManager['second_level_cache']['factory']) ? $entityManager['second_level_cache']['factory'] : '%doctrine.orm.second_level_cache.default_cache_factory.class%';
624
625
        $definition = new Definition($factoryClass, [new Reference($regionsId), new Reference($driverId)]);
626
627
        $slcFactoryDef = $container
628
            ->setDefinition($slcFactoryId, $definition);
629
630
        if (isset($entityManager['second_level_cache']['regions'])) {
631
            foreach ($entityManager['second_level_cache']['regions'] as $name => $region) {
632
                $regionRef  = null;
633
                $regionType = $region['type'];
634
635
                if ($regionType === 'service') {
636
                    $regionId  = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
637
                    $regionRef = new Reference($region['service']);
638
639
                    $container->setAlias($regionId, new Alias($region['service'], false));
640
                }
641
642
                if ($regionType === 'default' || $regionType === 'filelock') {
643
                    $regionId   = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
644
                    $driverName = sprintf('second_level_cache.region.%s_driver', $name);
645
                    $driverMap  = $region['cache_driver'];
646
                    $driverId   = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
647
                    $regionRef  = new Reference($regionId);
648
649
                    $container
650
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.default_region.class%'))
651
                        ->setArguments([$name, new Reference($driverId), $region['lifetime']]);
652
                }
653
654
                if ($regionType === 'filelock') {
655
                    $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s_filelock', $entityManager['name'], $name);
656
657
                    $container
658
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.filelock_region.class%'))
659
                        ->setArguments([$regionRef, $region['lock_path'], $region['lock_lifetime']]);
660
661
                    $regionRef = new Reference($regionId);
662
                    $regionsDef->addMethodCall('getLockLifetime', [$name, $region['lock_lifetime']]);
663
                }
664
665
                $regionsDef->addMethodCall('setLifetime', [$name, $region['lifetime']]);
666
                $slcFactoryDef->addMethodCall('setRegion', [$regionRef]);
667
            }
668
        }
669
670
        if ($entityManager['second_level_cache']['log_enabled']) {
671
            $loggerChainId   = sprintf('doctrine.orm.%s_second_level_cache.logger_chain', $entityManager['name']);
672
            $loggerStatsId   = sprintf('doctrine.orm.%s_second_level_cache.logger_statistics', $entityManager['name']);
673
            $loggerChaingDef = $container->setDefinition($loggerChainId, new Definition('%doctrine.orm.second_level_cache.logger_chain.class%'));
674
            $loggerStatsDef  = $container->setDefinition($loggerStatsId, new Definition('%doctrine.orm.second_level_cache.logger_statistics.class%'));
675
676
            $loggerChaingDef->addMethodCall('setLogger', ['statistics', $loggerStatsDef]);
677
            $configDef->addMethodCall('setCacheLogger', [$loggerChaingDef]);
678
679
            foreach ($entityManager['second_level_cache']['loggers'] as $name => $logger) {
680
                $loggerId  = sprintf('doctrine.orm.%s_second_level_cache.logger.%s', $entityManager['name'], $name);
681
                $loggerRef = new Reference($logger['service']);
682
683
                $container->setAlias($loggerId, new Alias($logger['service'], false));
684
                $loggerChaingDef->addMethodCall('setLogger', [$name, $loggerRef]);
685
            }
686
        }
687
688
        $configDef->addMethodCall('setCacheFactory', [$slcFactoryDef]);
689
        $configDef->addMethodCall('setRegionsConfiguration', [$regionsDef]);
690
        $ormConfigDef->addMethodCall('setSecondLevelCacheEnabled', [$enabled]);
691
        $ormConfigDef->addMethodCall('setSecondLevelCacheConfiguration', [$configDef]);
692
    }
693
694
    /**
695
     * {@inheritDoc}
696
     */
697
    protected function getObjectManagerElementName($name) : string
698
    {
699
        return 'doctrine.orm.' . $name;
700
    }
701
702
    protected function getMappingObjectDefaultName() : string
703
    {
704
        return 'Entity';
705
    }
706
707
    /**
708
     * {@inheritDoc}
709
     */
710
    protected function getMappingResourceConfigDirectory() : string
711
    {
712
        return 'Resources/config/doctrine';
713
    }
714
715
    /**
716
     * {@inheritDoc}
717
     */
718
    protected function getMappingResourceExtension() : string
719
    {
720
        return 'orm';
721
    }
722
723
    /**
724
     * {@inheritDoc}
725
     */
726
    protected function loadCacheDriver($cacheName, $objectManagerName, array $cacheDriver, ContainerBuilder $container) : string
727
    {
728
        $serviceId = null;
0 ignored issues
show
Unused Code introduced by
$serviceId is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
729
        $aliasId   = $this->getObjectManagerElementName(sprintf('%s_%s', $objectManagerName, $cacheName));
730
731
        switch ($cacheDriver['type'] ?? 'pool') {
732
            case 'service':
733
                $serviceId = $cacheDriver['id'];
734
                break;
735
736
            case 'pool':
737
                $serviceId = $this->createPoolCacheDefinition($container, $cacheDriver['pool'] ?? $this->createArrayAdapterCachePool($container, $objectManagerName, $cacheName));
738
                break;
739
740
            default:
741
                throw new \InvalidArgumentException(sprintf(
742
                    'Unknown cache of type "%s" configured for cache "%s" in entity manager "%s".',
743
                    $cacheDriver['type'],
744
                    $cacheName,
745
                    $objectManagerName
746
                ));
747
        }
748
749
        $container->setAlias($aliasId, new Alias($serviceId, false));
750
751
        return $aliasId;
752
    }
753
754
    /**
755
     * Loads a configured entity managers cache drivers.
756
     *
757
     * @param array            $entityManager A configured ORM entity manager.
758
     * @param ContainerBuilder $container     A ContainerBuilder instance
759
     */
760
    protected function loadOrmCacheDrivers(array $entityManager, ContainerBuilder $container)
761
    {
762
        $this->loadCacheDriver('metadata_cache', $entityManager['name'], $entityManager['metadata_cache_driver'], $container);
763
        $this->loadCacheDriver('result_cache', $entityManager['name'], $entityManager['result_cache_driver'], $container);
764
        $this->loadCacheDriver('query_cache', $entityManager['name'], $entityManager['query_cache_driver'], $container);
765
    }
766
767
    /**
768
     * Loads a property info extractor for each defined entity manager.
769
     */
770
    private function loadPropertyInfoExtractor(string $entityManagerName, ContainerBuilder $container) : void
771
    {
772
        $propertyExtractorDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.property_info_extractor', $entityManagerName), DoctrineExtractor::class);
773
        $argumentId                  = sprintf('doctrine.orm.%s_entity_manager', $entityManagerName);
774
775
        $propertyExtractorDefinition->addArgument(new Reference($argumentId));
776
777
        $propertyExtractorDefinition->addTag('property_info.list_extractor', ['priority' => -1001]);
778
        $propertyExtractorDefinition->addTag('property_info.type_extractor', ['priority' => -999]);
779
        $propertyExtractorDefinition->addTag('property_info.access_extractor', ['priority' => -999]);
780
    }
781
782
    /**
783
     * Loads a validator loader for each defined entity manager.
784
     */
785
    private function loadValidatorLoader(string $entityManagerName, ContainerBuilder $container) : void
786
    {
787
        $validatorLoaderDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.validator_loader', $entityManagerName), DoctrineLoader::class);
788
        $validatorLoaderDefinition->addArgument(new Reference(sprintf('doctrine.orm.%s_entity_manager', $entityManagerName)));
789
790
        $validatorLoaderDefinition->addTag('validator.auto_mapper', ['priority' => -100]);
791
    }
792
793
    /**
794
     * @param array  $objectManager
795
     * @param string $cacheName
796
     */
797
    public function loadObjectManagerCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName)
798
    {
799
        $this->loadCacheDriver($cacheName, $objectManager['name'], $objectManager[$cacheName . '_driver'], $container);
800
    }
801
802
    /**
803
     * {@inheritDoc}
804
     */
805
    public function getXsdValidationBasePath() : string
806
    {
807
        return __DIR__ . '/../Resources/config/schema';
808
    }
809
810
    /**
811
     * {@inheritDoc}
812
     */
813
    public function getNamespace() : string
814
    {
815
        return 'http://symfony.com/schema/dic/doctrine';
816
    }
817
818
    /**
819
     * {@inheritDoc}
820
     */
821
    public function getConfiguration(array $config, ContainerBuilder $container) : Configuration
822
    {
823
        return new Configuration($container->getParameter('kernel.debug'));
824
    }
825
826
    protected function getMetadataDriverClass(string $driverType) : string
827
    {
828
        return '%' . $this->getObjectManagerElementName('metadata.' . $driverType . '.class%');
829
    }
830
831
    private function loadMessengerServices(ContainerBuilder $container) : void
832
    {
833
        // If the Messenger component is installed and the doctrine transaction middleware is available, wire it:
834
        if (! interface_exists(MessageBusInterface::class) || ! class_exists(DoctrineTransactionMiddleware::class)) {
835
            return;
836
        }
837
838
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
839
        $loader->load('messenger.xml');
840
841
        if (! class_exists(DoctrineClearEntityManagerWorkerSubscriber::class)) {
842
            $container->removeDefinition('doctrine.orm.messenger.event_subscriber.doctrine_clear_entity_manager');
843
        }
844
845
        $transportFactoryDefinition = $container->getDefinition('messenger.transport.doctrine.factory');
846
        if (! class_exists(DoctrineTransportFactory::class)) {
847
            // If symfony/messenger < 5.1
848
            if (! class_exists(\Symfony\Component\Messenger\Transport\Doctrine\DoctrineTransportFactory::class)) {
849
                // Dont add the tag
850
                return;
851
            }
852
853
            $transportFactoryDefinition->setClass(\Symfony\Component\Messenger\Transport\Doctrine\DoctrineTransportFactory::class);
854
        }
855
856
        $transportFactoryDefinition->addTag('messenger.transport_factory');
857
    }
858
859
    private function createPoolCacheDefinition(ContainerBuilder $container, string $poolName) : string
860
    {
861
        $serviceId = sprintf('doctrine.orm.cache.provider.%s', $poolName);
862
863
        $definition = $container->register($serviceId, DoctrineProvider::class);
864
        $definition->addArgument(new Reference($poolName));
865
        $definition->setPrivate(true);
866
867
        return $serviceId;
868
    }
869
870
    private function createArrayAdapterCachePool(ContainerBuilder $container, string $objectManagerName, string $cacheName) : string
871
    {
872
        $id = sprintf('cache.doctrine.orm.%s.%s', $objectManagerName, str_replace('_cache', '', $cacheName));
873
874
        $poolDefinition = $container->register($id, ArrayAdapter::class);
875
        $poolDefinition->addTag('cache.pool');
876
        $container->setDefinition($id, $poolDefinition);
877
878
        return $id;
879
    }
880
}
881