Completed
Pull Request — master (#1078)
by
unknown
01:47
created

DoctrineExtension::loadMessengerServices()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.2728
c 0
b 0
f 0
cc 5
nc 5
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\Repository\ServiceEntityRepositoryInterface;
8
use Doctrine\ORM\Version;
9
use LogicException;
10
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
11
use Symfony\Bridge\Doctrine\Messenger\DoctrineClearEntityManagerWorkerSubscriber;
12
use Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddleware;
13
use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor;
14
use Symfony\Bridge\Doctrine\Validator\DoctrineLoader;
15
use Symfony\Component\Cache\Adapter\ArrayAdapter;
16
use Symfony\Component\Cache\DoctrineProvider;
17
use Symfony\Component\Config\FileLocator;
18
use Symfony\Component\DependencyInjection\Alias;
19
use Symfony\Component\DependencyInjection\ChildDefinition;
20
use Symfony\Component\DependencyInjection\ContainerBuilder;
21
use Symfony\Component\DependencyInjection\Definition;
22
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
23
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
24
use Symfony\Component\DependencyInjection\Reference;
25
use Symfony\Component\Form\AbstractType;
26
use Symfony\Component\Messenger\MessageBusInterface;
27
use Symfony\Component\Messenger\Transport\Doctrine\DoctrineTransportFactory;
28
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
29
use function class_exists;
30
use function sprintf;
31
32
/**
33
 * DoctrineExtension is an extension for the Doctrine DBAL and ORM library.
34
 */
35
class DoctrineExtension extends AbstractDoctrineExtension
36
{
37
    /** @var string */
38
    private $defaultConnection;
39
40
    /**
41
     * {@inheritDoc}
42
     */
43
    public function load(array $configs, ContainerBuilder $container)
44
    {
45
        $configuration = $this->getConfiguration($configs, $container);
46
        $config        = $this->processConfiguration($configuration, $configs);
0 ignored issues
show
Bug introduced by
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 45 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...
47
48
        if (! empty($config['dbal'])) {
49
            $this->dbalLoad($config['dbal'], $container);
50
51
            $this->loadMessengerServices($container);
52
        }
53
54
        if (empty($config['orm'])) {
55
            return;
56
        }
57
58
        if (empty($config['dbal'])) {
59
            throw new LogicException('Configuring the ORM layer requires to configure the DBAL layer as well.');
60
        }
61
62
        $this->ormLoad($config['orm'], $container);
63
    }
64
65
    /**
66
     * Loads the DBAL configuration.
67
     *
68
     * Usage example:
69
     *
70
     *      <doctrine:dbal id="myconn" dbname="sfweb" user="root" />
71
     *
72
     * @param array            $config    An array of configuration settings
73
     * @param ContainerBuilder $container A ContainerBuilder instance
74
     */
75
    protected function dbalLoad(array $config, ContainerBuilder $container)
76
    {
77
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
78
        $loader->load('dbal.xml');
79
80
        if (empty($config['default_connection'])) {
81
            $keys                         = array_keys($config['connections']);
82
            $config['default_connection'] = reset($keys);
83
        }
84
85
        $this->defaultConnection = $config['default_connection'];
86
87
        $container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection));
88
        $container->getAlias('database_connection')->setPublic(true);
89
        $container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false));
90
91
        $container->setParameter('doctrine.dbal.connection_factory.types', $config['types']);
92
93
        $connections = [];
94
95
        foreach (array_keys($config['connections']) as $name) {
96
            $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name);
97
        }
98
99
        $container->setParameter('doctrine.connections', $connections);
100
        $container->setParameter('doctrine.default_connection', $this->defaultConnection);
101
102
        foreach ($config['connections'] as $name => $connection) {
103
            $this->loadDbalConnection($name, $connection, $container);
104
        }
105
    }
106
107
    /**
108
     * Loads a configured DBAL connection.
109
     *
110
     * @param string           $name       The name of the connection
111
     * @param array            $connection A dbal connection configuration.
112
     * @param ContainerBuilder $container  A ContainerBuilder instance
113
     */
114
    protected function loadDbalConnection($name, array $connection, ContainerBuilder $container)
115
    {
116
        $configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new ChildDefinition('doctrine.dbal.connection.configuration'));
117
        $logger        = null;
118
        if ($connection['logging']) {
119
            $logger = new Reference('doctrine.dbal.logger');
120
        }
121
        unset($connection['logging']);
122
123
        if ($connection['profiling']) {
124
            $profilingAbstractId = $connection['profiling_collect_backtrace'] ?
125
                'doctrine.dbal.logger.backtrace' :
126
                'doctrine.dbal.logger.profiling';
127
128
            $profilingLoggerId = $profilingAbstractId . '.' . $name;
129
            $container->setDefinition($profilingLoggerId, new ChildDefinition($profilingAbstractId));
130
            $profilingLogger = new Reference($profilingLoggerId);
131
            $container->getDefinition('data_collector.doctrine')->addMethodCall('addLogger', [$name, $profilingLogger]);
132
133
            if ($logger !== null) {
134
                $chainLogger = new ChildDefinition('doctrine.dbal.logger.chain');
135
                $chainLogger->addMethodCall('addLogger', [$profilingLogger]);
136
137
                $loggerId = 'doctrine.dbal.logger.chain.' . $name;
138
                $container->setDefinition($loggerId, $chainLogger);
139
                $logger = new Reference($loggerId);
140
            } else {
141
                $logger = $profilingLogger;
142
            }
143
        }
144
        unset($connection['profiling'], $connection['profiling_collect_backtrace']);
145
146
        if (isset($connection['auto_commit'])) {
147
            $configuration->addMethodCall('setAutoCommit', [$connection['auto_commit']]);
148
        }
149
150
        unset($connection['auto_commit']);
151
152
        if (isset($connection['schema_filter']) && $connection['schema_filter']) {
153
            $definition = new Definition(RegexSchemaAssetFilter::class, [$connection['schema_filter']]);
154
            $definition->addTag('doctrine.dbal.schema_filter', ['connection' => $name]);
155
            $container->setDefinition(sprintf('doctrine.dbal.%s_regex_schema_filter', $name), $definition);
156
        }
157
158
        unset($connection['schema_filter']);
159
160
        if ($logger) {
161
            $configuration->addMethodCall('setSQLLogger', [$logger]);
162
        }
163
164
        // event manager
165
        $container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new ChildDefinition('doctrine.dbal.connection.event_manager'));
166
167
        // connection
168
        $options = $this->getConnectionOptions($connection);
169
170
        $def = $container
171
            ->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new ChildDefinition('doctrine.dbal.connection'))
172
            ->setPublic(true)
173
            ->setArguments([
174
                $options,
175
                new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)),
176
                new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)),
177
                $connection['mapping_types'],
178
            ]);
179
180
        // Set class in case "wrapper_class" option was used to assist IDEs
181
        if (isset($options['wrapperClass'])) {
182
            $def->setClass($options['wrapperClass']);
183
        }
184
185
        if (! empty($connection['use_savepoints'])) {
186
            $def->addMethodCall('setNestTransactionsWithSavepoints', [$connection['use_savepoints']]);
187
        }
188
189
        // Create a shard_manager for this connection
190
        if (! isset($options['shards'])) {
191
            return;
192
        }
193
194
        $shardManagerDefinition = new Definition($options['shardManagerClass'], [new Reference(sprintf('doctrine.dbal.%s_connection', $name))]);
195
        $container->setDefinition(sprintf('doctrine.dbal.%s_shard_manager', $name), $shardManagerDefinition);
196
    }
197
198
    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...
199
    {
200
        $options = $connection;
201
202
        if (isset($options['platform_service'])) {
203
            $options['platform'] = new Reference($options['platform_service']);
204
            unset($options['platform_service']);
205
        }
206
        unset($options['mapping_types']);
207
208
        if (isset($options['shard_choser_service'])) {
209
            $options['shard_choser'] = new Reference($options['shard_choser_service']);
210
            unset($options['shard_choser_service']);
211
        }
212
213
        foreach ([
214
            'options' => 'driverOptions',
215
            'driver_class' => 'driverClass',
216
            'wrapper_class' => 'wrapperClass',
217
            'keep_slave' => 'keepSlave',
218
            'shard_choser' => 'shardChoser',
219
            'shard_manager_class' => 'shardManagerClass',
220
            'server_version' => 'serverVersion',
221
            'default_table_options' => 'defaultTableOptions',
222
        ] as $old => $new) {
223
            if (! isset($options[$old])) {
224
                continue;
225
            }
226
227
            $options[$new] = $options[$old];
228
            unset($options[$old]);
229
        }
230
231
        if (! empty($options['slaves']) && ! empty($options['shards'])) {
232
            throw new InvalidArgumentException('Sharding and master-slave connection cannot be used together');
233
        }
234
235
        if (! empty($options['slaves'])) {
236
            $nonRewrittenKeys = [
237
                'driver' => true,
238
                'driverOptions' => true,
239
                'driverClass' => true,
240
                'wrapperClass' => true,
241
                'keepSlave' => true,
242
                'shardChoser' => true,
243
                'platform' => true,
244
                'slaves' => true,
245
                'master' => true,
246
                'shards' => true,
247
                'serverVersion' => true,
248
                'defaultTableOptions' => true,
249
                // included by safety but should have been unset already
250
                'logging' => true,
251
                'profiling' => true,
252
                'mapping_types' => true,
253
                'platform_service' => true,
254
            ];
255 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...
256
                if (isset($nonRewrittenKeys[$key])) {
257
                    continue;
258
                }
259
                $options['master'][$key] = $value;
260
                unset($options[$key]);
261
            }
262
            if (empty($options['wrapperClass'])) {
263
                // Change the wrapper class only if the user does not already forced using a custom one.
264
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Connections\\MasterSlaveConnection';
265
            }
266
        } else {
267
            unset($options['slaves']);
268
        }
269
270
        if (! empty($options['shards'])) {
271
            $nonRewrittenKeys = [
272
                'driver' => true,
273
                'driverOptions' => true,
274
                'driverClass' => true,
275
                'wrapperClass' => true,
276
                'keepSlave' => true,
277
                'shardChoser' => true,
278
                'platform' => true,
279
                'slaves' => true,
280
                'global' => true,
281
                'shards' => true,
282
                'serverVersion' => true,
283
                'defaultTableOptions' => true,
284
                // included by safety but should have been unset already
285
                'logging' => true,
286
                'profiling' => true,
287
                'mapping_types' => true,
288
                'platform_service' => true,
289
            ];
290 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...
291
                if (isset($nonRewrittenKeys[$key])) {
292
                    continue;
293
                }
294
                $options['global'][$key] = $value;
295
                unset($options[$key]);
296
            }
297
            if (empty($options['wrapperClass'])) {
298
                // Change the wrapper class only if the user does not already forced using a custom one.
299
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardConnection';
300
            }
301
            if (empty($options['shardManagerClass'])) {
302
                // Change the shard manager class only if the user does not already forced using a custom one.
303
                $options['shardManagerClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardManager';
304
            }
305
        } else {
306
            unset($options['shards']);
307
        }
308
309
        return $options;
310
    }
311
312
    /**
313
     * Loads the Doctrine ORM configuration.
314
     *
315
     * Usage example:
316
     *
317
     *     <doctrine:orm id="mydm" connection="myconn" />
318
     *
319
     * @param array            $config    An array of configuration settings
320
     * @param ContainerBuilder $container A ContainerBuilder instance
321
     */
322
    protected function ormLoad(array $config, ContainerBuilder $container)
323
    {
324
        if (! class_exists(Version::class)) {
325
            throw new LogicException('To configure the ORM layer, you must first install the doctrine/orm package.');
326
        }
327
328
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
329
        $loader->load('orm.xml');
330
331
        if (class_exists(AbstractType::class)) {
332
            $container->getDefinition('form.type.entity')->addTag('kernel.reset', ['method' => 'reset']);
333
        }
334
335
        $entityManagers = [];
336
        foreach (array_keys($config['entity_managers']) as $name) {
337
            $entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name);
338
        }
339
        $container->setParameter('doctrine.entity_managers', $entityManagers);
340
341
        if (empty($config['default_entity_manager'])) {
342
            $tmp                              = array_keys($entityManagers);
343
            $config['default_entity_manager'] = reset($tmp);
344
        }
345
        $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']);
346
347
        $options = ['auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace'];
348
        foreach ($options as $key) {
349
            $container->setParameter('doctrine.orm.' . $key, $config[$key]);
350
        }
351
352
        $container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager']));
353
        $container->getAlias('doctrine.orm.entity_manager')->setPublic(true);
354
355
        $config['entity_managers'] = $this->fixManagersAutoMappings($config['entity_managers'], $container->getParameter('kernel.bundles'));
356
357
        foreach ($config['entity_managers'] as $name => $entityManager) {
358
            $entityManager['name'] = $name;
359
            $this->loadOrmEntityManager($entityManager, $container);
360
361
            if (interface_exists(PropertyInfoExtractorInterface::class)) {
362
                $this->loadPropertyInfoExtractor($name, $container);
363
            }
364
365
            $this->loadValidatorLoader($name, $container);
366
        }
367
368
        if ($config['resolve_target_entities']) {
369
            $def = $container->findDefinition('doctrine.orm.listeners.resolve_target_entity');
370
            foreach ($config['resolve_target_entities'] as $name => $implementation) {
371
                $def->addMethodCall('addResolveTargetEntity', [
372
                    $name,
373
                    $implementation,
374
                    [],
375
                ]);
376
            }
377
378
            $def->addTag('doctrine.event_subscriber');
379
        }
380
381
        $container->registerForAutoconfiguration(ServiceEntityRepositoryInterface::class)
382
            ->addTag(ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG);
383
    }
384
385
    /**
386
     * Loads a configured ORM entity manager.
387
     *
388
     * @param array            $entityManager A configured ORM entity manager.
389
     * @param ContainerBuilder $container     A ContainerBuilder instance
390
     */
391
    protected function loadOrmEntityManager(array $entityManager, ContainerBuilder $container)
392
    {
393
        $ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new ChildDefinition('doctrine.orm.configuration'));
394
395
        $this->loadOrmEntityManagerMappingInformation($entityManager, $ormConfigDef, $container);
396
        $this->loadOrmCacheDrivers($entityManager, $container);
397
398
        if (isset($entityManager['entity_listener_resolver']) && $entityManager['entity_listener_resolver']) {
399
            $container->setAlias(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $entityManager['entity_listener_resolver']);
400
        } else {
401
            $definition = new Definition('%doctrine.orm.entity_listener_resolver.class%');
402
            $definition->addArgument(new Reference('service_container'));
403
            $container->setDefinition(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $definition);
404
        }
405
406
        $methods = [
407
            'setMetadataCacheImpl' => new Reference(sprintf('doctrine.orm.%s_metadata_cache', $entityManager['name'])),
408
            'setQueryCacheImpl' => new Reference(sprintf('doctrine.orm.%s_query_cache', $entityManager['name'])),
409
            'setResultCacheImpl' => new Reference(sprintf('doctrine.orm.%s_result_cache', $entityManager['name'])),
410
            'setMetadataDriverImpl' => new Reference('doctrine.orm.' . $entityManager['name'] . '_metadata_driver'),
411
            'setProxyDir' => '%doctrine.orm.proxy_dir%',
412
            'setProxyNamespace' => '%doctrine.orm.proxy_namespace%',
413
            'setAutoGenerateProxyClasses' => '%doctrine.orm.auto_generate_proxy_classes%',
414
            'setClassMetadataFactoryName' => $entityManager['class_metadata_factory_name'],
415
            'setDefaultRepositoryClassName' => $entityManager['default_repository_class'],
416
            'setNamingStrategy' => new Reference($entityManager['naming_strategy']),
417
            'setQuoteStrategy' => new Reference($entityManager['quote_strategy']),
418
            'setEntityListenerResolver' => new Reference(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name'])),
419
        ];
420
421
        $listenerId        = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $entityManager['name']);
422
        $listenerDef       = $container->setDefinition($listenerId, new Definition('%doctrine.orm.listeners.attach_entity_listeners.class%'));
423
        $listenerTagParams = ['event' => 'loadClassMetadata'];
424
        if (isset($entityManager['connection'])) {
425
            $listenerTagParams['connection'] = $entityManager['connection'];
426
        }
427
        $listenerDef->addTag('doctrine.event_listener', $listenerTagParams);
428
429
        if (isset($entityManager['second_level_cache'])) {
430
            $this->loadOrmSecondLevelCache($entityManager, $ormConfigDef, $container);
431
        }
432
433
        if ($entityManager['repository_factory']) {
434
            $methods['setRepositoryFactory'] = new Reference($entityManager['repository_factory']);
435
        }
436
437
        foreach ($methods as $method => $arg) {
438
            $ormConfigDef->addMethodCall($method, [$arg]);
439
        }
440
441
        foreach ($entityManager['hydrators'] as $name => $class) {
442
            $ormConfigDef->addMethodCall('addCustomHydrationMode', [$name, $class]);
443
        }
444
445
        if (! empty($entityManager['dql'])) {
446
            foreach ($entityManager['dql']['string_functions'] as $name => $function) {
447
                $ormConfigDef->addMethodCall('addCustomStringFunction', [$name, $function]);
448
            }
449
            foreach ($entityManager['dql']['numeric_functions'] as $name => $function) {
450
                $ormConfigDef->addMethodCall('addCustomNumericFunction', [$name, $function]);
451
            }
452
            foreach ($entityManager['dql']['datetime_functions'] as $name => $function) {
453
                $ormConfigDef->addMethodCall('addCustomDatetimeFunction', [$name, $function]);
454
            }
455
        }
456
457
        $enabledFilters    = [];
458
        $filtersParameters = [];
459
        foreach ($entityManager['filters'] as $name => $filter) {
460
            $ormConfigDef->addMethodCall('addFilter', [$name, $filter['class']]);
461
            if ($filter['enabled']) {
462
                $enabledFilters[] = $name;
463
            }
464
            if (! $filter['parameters']) {
465
                continue;
466
            }
467
468
            $filtersParameters[$name] = $filter['parameters'];
469
        }
470
471
        $managerConfiguratorName = sprintf('doctrine.orm.%s_manager_configurator', $entityManager['name']);
472
        $container
473
            ->setDefinition($managerConfiguratorName, new ChildDefinition('doctrine.orm.manager_configurator.abstract'))
474
            ->replaceArgument(0, $enabledFilters)
475
            ->replaceArgument(1, $filtersParameters);
476
477
        if (! isset($entityManager['connection'])) {
478
            $entityManager['connection'] = $this->defaultConnection;
479
        }
480
481
        $container
482
            ->setDefinition(sprintf('doctrine.orm.%s_entity_manager', $entityManager['name']), new ChildDefinition('doctrine.orm.entity_manager.abstract'))
483
            ->setPublic(true)
484
            ->setArguments([
485
                new Reference(sprintf('doctrine.dbal.%s_connection', $entityManager['connection'])),
486
                new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])),
487
            ])
488
            ->setConfigurator([new Reference($managerConfiguratorName), 'configure']);
489
490
        $container->setAlias(
491
            sprintf('doctrine.orm.%s_entity_manager.event_manager', $entityManager['name']),
492
            new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $entityManager['connection']), false)
493
        );
494
495
        if (! isset($entityManager['entity_listeners'])) {
496
            return;
497
        }
498
499
        if (! isset($listenerDef)) {
500
            throw new InvalidArgumentException('Entity listeners configuration requires doctrine-orm 2.5.0 or newer');
501
        }
502
503
        $entities = $entityManager['entity_listeners']['entities'];
504
505
        foreach ($entities as $entityListenerClass => $entity) {
506
            foreach ($entity['listeners'] as $listenerClass => $listener) {
507
                foreach ($listener['events'] as $listenerEvent) {
508
                    $listenerEventName = $listenerEvent['type'];
509
                    $listenerMethod    = $listenerEvent['method'];
510
511
                    $listenerDef->addMethodCall('addEntityListener', [
512
                        $entityListenerClass,
513
                        $listenerClass,
514
                        $listenerEventName,
515
                        $listenerMethod,
516
                    ]);
517
                }
518
            }
519
        }
520
    }
521
522
    /**
523
     * Loads an ORM entity managers bundle mapping information.
524
     *
525
     * There are two distinct configuration possibilities for mapping information:
526
     *
527
     * 1. Specify a bundle and optionally details where the entity and mapping information reside.
528
     * 2. Specify an arbitrary mapping location.
529
     *
530
     * @param array            $entityManager A configured ORM entity manager
531
     * @param Definition       $ormConfigDef  A Definition instance
532
     * @param ContainerBuilder $container     A ContainerBuilder instance
533
     *
534
     * @example
535
     *
536
     *  doctrine.orm:
537
     *     mappings:
538
     *         MyBundle1: ~
539
     *         MyBundle2: yml
540
     *         MyBundle3: { type: annotation, dir: Entities/ }
541
     *         MyBundle4: { type: xml, dir: Resources/config/doctrine/mapping }
542
     *         MyBundle5:
543
     *             type: yml
544
     *             dir: bundle-mappings/
545
     *             alias: BundleAlias
546
     *         arbitrary_key:
547
     *             type: xml
548
     *             dir: %kernel.project_dir%/src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entities
549
     *             prefix: DoctrineExtensions\Entities\
550
     *             alias: DExt
551
     *
552
     * In the case of bundles everything is really optional (which leads to autodetection for this bundle) but
553
     * in the mappings key everything except alias is a required argument.
554
     */
555
    protected function loadOrmEntityManagerMappingInformation(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
556
    {
557
        // reset state of drivers and alias map. They are only used by this methods and children.
558
        $this->drivers  = [];
559
        $this->aliasMap = [];
560
561
        $this->loadMappingInformation($entityManager, $container);
562
        $this->registerMappingDrivers($entityManager, $container);
563
564
        $ormConfigDef->addMethodCall('setEntityNamespaces', [$this->aliasMap]);
565
    }
566
567
    /**
568
     * Loads an ORM second level cache bundle mapping information.
569
     *
570
     * @param array            $entityManager A configured ORM entity manager
571
     * @param Definition       $ormConfigDef  A Definition instance
572
     * @param ContainerBuilder $container     A ContainerBuilder instance
573
     *
574
     * @example
575
     *  entity_managers:
576
     *      default:
577
     *          second_level_cache:
578
     *              region_cache_driver: apc
579
     *              log_enabled: true
580
     *              regions:
581
     *                  my_service_region:
582
     *                      type: service
583
     *                      service : "my_service_region"
584
     *
585
     *                  my_query_region:
586
     *                      lifetime: 300
587
     *                      cache_driver: array
588
     *                      type: filelock
589
     *
590
     *                  my_entity_region:
591
     *                      lifetime: 600
592
     *                      cache_driver:
593
     *                          type: apc
594
     */
595
    protected function loadOrmSecondLevelCache(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
596
    {
597
        $driverId = null;
598
        $enabled  = $entityManager['second_level_cache']['enabled'];
599
600
        if (isset($entityManager['second_level_cache']['region_cache_driver'])) {
601
            $driverName = 'second_level_cache.region_cache_driver';
602
            $driverMap  = $entityManager['second_level_cache']['region_cache_driver'];
603
            $driverId   = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
604
        }
605
606
        $configId   = sprintf('doctrine.orm.%s_second_level_cache.cache_configuration', $entityManager['name']);
607
        $regionsId  = sprintf('doctrine.orm.%s_second_level_cache.regions_configuration', $entityManager['name']);
608
        $driverId   = $driverId ?: sprintf('doctrine.orm.%s_second_level_cache.region_cache_driver', $entityManager['name']);
609
        $configDef  = $container->setDefinition($configId, new Definition('%doctrine.orm.second_level_cache.cache_configuration.class%'));
610
        $regionsDef = $container->setDefinition($regionsId, new Definition('%doctrine.orm.second_level_cache.regions_configuration.class%'));
611
612
        $slcFactoryId = sprintf('doctrine.orm.%s_second_level_cache.default_cache_factory', $entityManager['name']);
613
        $factoryClass = isset($entityManager['second_level_cache']['factory']) ? $entityManager['second_level_cache']['factory'] : '%doctrine.orm.second_level_cache.default_cache_factory.class%';
614
615
        $definition = new Definition($factoryClass, [new Reference($regionsId), new Reference($driverId)]);
616
617
        $slcFactoryDef = $container
618
            ->setDefinition($slcFactoryId, $definition);
619
620
        if (isset($entityManager['second_level_cache']['regions'])) {
621
            foreach ($entityManager['second_level_cache']['regions'] as $name => $region) {
622
                $regionRef  = null;
623
                $regionType = $region['type'];
624
625
                if ($regionType === 'service') {
626
                    $regionId  = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
627
                    $regionRef = new Reference($region['service']);
628
629
                    $container->setAlias($regionId, new Alias($region['service'], false));
630
                }
631
632
                if ($regionType === 'default' || $regionType === 'filelock') {
633
                    $regionId   = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
634
                    $driverName = sprintf('second_level_cache.region.%s_driver', $name);
635
                    $driverMap  = $region['cache_driver'];
636
                    $driverId   = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
637
                    $regionRef  = new Reference($regionId);
638
639
                    $container
640
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.default_region.class%'))
641
                        ->setArguments([$name, new Reference($driverId), $region['lifetime']]);
642
                }
643
644
                if ($regionType === 'filelock') {
645
                    $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s_filelock', $entityManager['name'], $name);
646
647
                    $container
648
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.filelock_region.class%'))
649
                        ->setArguments([$regionRef, $region['lock_path'], $region['lock_lifetime']]);
650
651
                    $regionRef = new Reference($regionId);
652
                    $regionsDef->addMethodCall('getLockLifetime', [$name, $region['lock_lifetime']]);
653
                }
654
655
                $regionsDef->addMethodCall('setLifetime', [$name, $region['lifetime']]);
656
                $slcFactoryDef->addMethodCall('setRegion', [$regionRef]);
657
            }
658
        }
659
660
        if ($entityManager['second_level_cache']['log_enabled']) {
661
            $loggerChainId   = sprintf('doctrine.orm.%s_second_level_cache.logger_chain', $entityManager['name']);
662
            $loggerStatsId   = sprintf('doctrine.orm.%s_second_level_cache.logger_statistics', $entityManager['name']);
663
            $loggerChaingDef = $container->setDefinition($loggerChainId, new Definition('%doctrine.orm.second_level_cache.logger_chain.class%'));
664
            $loggerStatsDef  = $container->setDefinition($loggerStatsId, new Definition('%doctrine.orm.second_level_cache.logger_statistics.class%'));
665
666
            $loggerChaingDef->addMethodCall('setLogger', ['statistics', $loggerStatsDef]);
667
            $configDef->addMethodCall('setCacheLogger', [$loggerChaingDef]);
668
669
            foreach ($entityManager['second_level_cache']['loggers'] as $name => $logger) {
670
                $loggerId  = sprintf('doctrine.orm.%s_second_level_cache.logger.%s', $entityManager['name'], $name);
671
                $loggerRef = new Reference($logger['service']);
672
673
                $container->setAlias($loggerId, new Alias($logger['service'], false));
674
                $loggerChaingDef->addMethodCall('setLogger', [$name, $loggerRef]);
675
            }
676
        }
677
678
        $configDef->addMethodCall('setCacheFactory', [$slcFactoryDef]);
679
        $configDef->addMethodCall('setRegionsConfiguration', [$regionsDef]);
680
        $ormConfigDef->addMethodCall('setSecondLevelCacheEnabled', [$enabled]);
681
        $ormConfigDef->addMethodCall('setSecondLevelCacheConfiguration', [$configDef]);
682
    }
683
684
    /**
685
     * {@inheritDoc}
686
     */
687
    protected function getObjectManagerElementName($name) : string
688
    {
689
        return 'doctrine.orm.' . $name;
690
    }
691
692
    protected function getMappingObjectDefaultName() : string
693
    {
694
        return 'Entity';
695
    }
696
697
    /**
698
     * {@inheritDoc}
699
     */
700
    protected function getMappingResourceConfigDirectory() : string
701
    {
702
        return 'Resources/config/doctrine';
703
    }
704
705
    /**
706
     * {@inheritDoc}
707
     */
708
    protected function getMappingResourceExtension() : string
709
    {
710
        return 'orm';
711
    }
712
713
    /**
714
     * {@inheritDoc}
715
     */
716
    protected function loadCacheDriver($cacheName, $objectManagerName, array $cacheDriver, ContainerBuilder $container) : string
717
    {
718
        $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...
719
        $aliasId   = $this->getObjectManagerElementName(sprintf('%s_%s', $objectManagerName, $cacheName));
720
721
        switch ($cacheDriver['type'] ?? 'pool') {
722
            case 'service':
723
                $serviceId = $cacheDriver['id'];
724
                break;
725
726
            case 'pool':
727
                $serviceId = $this->createPoolCacheDefinition($container, $cacheDriver['pool'] ?? $this->createArrayAdapterCachePool($container, $objectManagerName, $cacheName));
728
                break;
729
730
            case 'provider':
731
                $serviceId = sprintf('doctrine_cache.providers.%s', $cacheDriver['cache_provider']);
732
                break;
733
734
            default:
735
                throw new \InvalidArgumentException(sprintf(
736
                    'Unknown cache of type "%s" configured for cache "%s" in entity manager "%s".',
737
                    $cacheDriver['type'],
738
                    $cacheName,
739
                    $objectManagerName
740
                ));
741
        }
742
743
        $container->setAlias($aliasId, new Alias($serviceId, false));
744
745
        return $aliasId;
746
    }
747
748
    /**
749
     * Loads a configured entity managers cache drivers.
750
     *
751
     * @param array            $entityManager A configured ORM entity manager.
752
     * @param ContainerBuilder $container     A ContainerBuilder instance
753
     */
754
    protected function loadOrmCacheDrivers(array $entityManager, ContainerBuilder $container)
755
    {
756
        $this->loadCacheDriver('metadata_cache', $entityManager['name'], $entityManager['metadata_cache_driver'], $container);
757
        $this->loadCacheDriver('result_cache', $entityManager['name'], $entityManager['result_cache_driver'], $container);
758
        $this->loadCacheDriver('query_cache', $entityManager['name'], $entityManager['query_cache_driver'], $container);
759
    }
760
761
    /**
762
     * Loads a property info extractor for each defined entity manager.
763
     *
764
     * @param string $entityManagerName
765
     */
766
    private function loadPropertyInfoExtractor($entityManagerName, ContainerBuilder $container)
767
    {
768
        $propertyExtractorDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.property_info_extractor', $entityManagerName), DoctrineExtractor::class);
769
        $argumentId                  = sprintf('doctrine.orm.%s_entity_manager', $entityManagerName);
770
771
        $propertyExtractorDefinition->addArgument(new Reference($argumentId));
772
773
        $propertyExtractorDefinition->addTag('property_info.list_extractor', ['priority' => -1001]);
774
        $propertyExtractorDefinition->addTag('property_info.type_extractor', ['priority' => -999]);
775
        $propertyExtractorDefinition->addTag('property_info.access_extractor', ['priority' => -999]);
776
    }
777
778
    /**
779
     * Loads a validator loader for each defined entity manager.
780
     */
781
    private function loadValidatorLoader(string $entityManagerName, ContainerBuilder $container) : void
782
    {
783
        $validatorLoaderDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.validator_loader', $entityManagerName), DoctrineLoader::class);
784
        $validatorLoaderDefinition->addArgument(new Reference(sprintf('doctrine.orm.%s_entity_manager', $entityManagerName)));
785
786
        $validatorLoaderDefinition->addTag('validator.auto_mapper', ['priority' => -100]);
787
    }
788
789
    /**
790
     * @param array  $objectManager
791
     * @param string $cacheName
792
     */
793
    public function loadObjectManagerCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName)
794
    {
795
        $this->loadCacheDriver($cacheName, $objectManager['name'], $objectManager[$cacheName . '_driver'], $container);
796
    }
797
798
    /**
799
     * {@inheritDoc}
800
     */
801
    public function getXsdValidationBasePath() : string
802
    {
803
        return __DIR__ . '/../Resources/config/schema';
804
    }
805
806
    /**
807
     * {@inheritDoc}
808
     */
809
    public function getNamespace() : string
810
    {
811
        return 'http://symfony.com/schema/dic/doctrine';
812
    }
813
814
    /**
815
     * {@inheritDoc}
816
     */
817
    public function getConfiguration(array $config, ContainerBuilder $container) : Configuration
818
    {
819
        return new Configuration($container->getParameter('kernel.debug'));
820
    }
821
822
    protected function getMetadataDriverClass(string $driverType) : string
823
    {
824
        return '%' . $this->getObjectManagerElementName('metadata.' . $driverType . '.class%');
825
    }
826
827
    private function loadMessengerServices(ContainerBuilder $container) : void
828
    {
829
        // If the Messenger component is installed and the doctrine transaction middleware is available, wire it:
830
        if (! interface_exists(MessageBusInterface::class) || ! class_exists(DoctrineTransactionMiddleware::class)) {
831
            return;
832
        }
833
834
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
835
        $loader->load('messenger.xml');
836
837
        if (! class_exists(DoctrineClearEntityManagerWorkerSubscriber::class)) {
838
            $container->removeDefinition('doctrine.orm.messenger.event_subscriber.doctrine_clear_entity_manager');
839
        }
840
841
        if (! class_exists(DoctrineTransportFactory::class)) {
842
            return;
843
        }
844
845
        $transportFactoryDefinition = $container->getDefinition('messenger.transport.doctrine.factory');
846
        $transportFactoryDefinition->addTag('messenger.transport_factory');
847
    }
848
849
    private function createPoolCacheDefinition(ContainerBuilder $container, string $poolName) : string
850
    {
851
        $serviceId = sprintf('doctrine.orm.cache.provider.%s', $poolName);
852
853
        $definition = $container->register($serviceId, DoctrineProvider::class);
854
        $definition->addArgument(new Reference($poolName));
855
        $definition->setPrivate(true);
856
857
        return $serviceId;
858
    }
859
860
    private function createArrayAdapterCachePool(ContainerBuilder $container, string $objectManagerName, string $cacheName) : string
861
    {
862
        $id = sprintf('cache.doctrine.orm.%s.%s', $objectManagerName, str_replace('_cache', '', $cacheName));
863
864
        $poolDefinition = $container->register($id, ArrayAdapter::class);
865
        $poolDefinition->addTag('cache.pool');
866
        $container->setDefinition($id, $poolDefinition);
867
868
        return $id;
869
    }
870
}
871