Completed
Pull Request — master (#956)
by
unknown
03:06
created

DoctrineExtension::loadOrmSecondLevelCache()   C

Complexity

Conditions 12
Paths 32

Size

Total Lines 88

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 88
rs 5.8351
c 0
b 0
f 0
cc 12
nc 32
nop 3

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
826
    }
827
828
    /**
829
     * {@inheritDoc}
830
     */
831
    public function getNamespace()
832
    {
833
        return 'http://symfony.com/schema/dic/doctrine';
834
    }
835
836
    /**
837
     * {@inheritDoc}
838
     */
839
    public function getConfiguration(array $config, ContainerBuilder $container)
840
    {
841
        return new Configuration($container->getParameter('kernel.debug'));
842
    }
843
844
    private function loadMessengerServices(ContainerBuilder $container) : void
845
    {
846
        // If the Messenger component is installed and the doctrine transaction middleware is available, wire it:
847
        if (! interface_exists(MessageBusInterface::class) || ! class_exists(DoctrineTransactionMiddleware::class)) {
848
            return;
849
        }
850
851
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
852
        $loader->load('messenger.xml');
853
854
        if (class_exists(DoctrineCloseConnectionMiddleware::class)) {
855
            $loader->load('messenger/doctrine-close-connection-middleware.xml');
856
        }
857
858
        if (class_exists(DoctrinePingConnectionMiddleware::class)) {
859
            $loader->load('messenger/doctrine-ping-connection-middleware.xml');
860
        }
861
862
        if (! class_exists(DoctrineTransportFactory::class)) {
863
            return;
864
        }
865
866
        $transportFactoryDefinition = $container->getDefinition('messenger.transport.doctrine.factory');
867
        $transportFactoryDefinition->addTag('messenger.transport_factory');
868
    }
869
870
    private function createPoolCacheDefinition(ContainerBuilder $container, string $aliasId, string $poolName) : string
871
    {
872
        if (! class_exists(DoctrineProvider::class)) {
873
            throw new LogicException('Using the "pool" cache type is only supported when symfony/cache is installed.');
874
        }
875
876
        $serviceId = sprintf('doctrine.orm.cache.pool.%s', $poolName);
877
878
        $definition = $container->register($aliasId, DoctrineProvider::class);
879
        $definition->addArgument(new Reference($poolName));
880
        $definition->setPrivate(true);
881
882
        return $serviceId;
883
    }
884
}
885