Completed
Push — master ( 601d80...357a47 )
by Andreas
01:53 queued 10s
created

DoctrineExtension::loadOrmCacheDrivers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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