Completed
Pull Request — master (#905)
by Gabriel
01:56
created

DoctrineExtension::ormLoad()   F

Complexity

Conditions 16
Paths 2304

Size

Total Lines 91

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 91
rs 1.3163
c 0
b 0
f 0
cc 16
nc 2304
nop 2

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