Completed
Pull Request — master (#658)
by Magnus
03:02
created

DoctrineExtension::loadDbalConnection()   C

Complexity

Conditions 10
Paths 192

Size

Total Lines 74
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 74
rs 5.4676
c 0
b 0
f 0
cc 10
eloc 44
nc 192
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
/*
4
 * This file is part of the Doctrine Bundle
5
 *
6
 * The code was originally distributed inside the Symfony framework.
7
 *
8
 * (c) Fabien Potencier <[email protected]>
9
 * (c) Doctrine Project, Benjamin Eberlei <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
16
17
use Doctrine\ORM\Version;
18
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
19
use Symfony\Component\DependencyInjection\Alias;
20
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
21
use Symfony\Component\DependencyInjection\ContainerBuilder;
22
use Symfony\Component\DependencyInjection\Definition;
23
use Symfony\Component\DependencyInjection\DefinitionDecorator;
24
use Symfony\Component\DependencyInjection\ChildDefinition;
25
use Symfony\Component\DependencyInjection\Reference;
26
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
27
use Symfony\Component\Config\FileLocator;
28
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\SymfonyBridgeAdapter;
29
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader;
30
31
/**
32
 * DoctrineExtension is an extension for the Doctrine DBAL and ORM library.
33
 *
34
 * @author Jonathan H. Wage <[email protected]>
35
 * @author Fabien Potencier <[email protected]>
36
 * @author Benjamin Eberlei <[email protected]>
37
 * @author Fabio B. Silva <[email protected]>
38
 * @author Kinn Coelho Julião <[email protected]>
39
 */
40
class DoctrineExtension extends AbstractDoctrineExtension
41
{
42
    /**
43
     * @var string
44
     */
45
    private $defaultConnection;
46
47
    /**
48
     * @var array
49
     */
50
    private $entityManagers;
51
52
    /**
53
     * @var SymfonyBridgeAdapter
54
     */
55
    private $adapter;
56
57
    /**
58
     * @param SymfonyBridgeAdapter $adapter
0 ignored issues
show
Documentation introduced by
Should the type for parameter $adapter not be null|SymfonyBridgeAdapter?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
59
     */
60
    public function __construct(SymfonyBridgeAdapter $adapter = null)
61
    {
62
        $this->adapter = $adapter ?: new SymfonyBridgeAdapter(new CacheProviderLoader(), 'doctrine.orm', 'orm');
63
    }
64
65
    /**
66
     * {@inheritDoc}
67
     */
68
    public function load(array $configs, ContainerBuilder $container)
69
    {
70
        $configuration = $this->getConfiguration($configs, $container);
71
        $config = $this->processConfiguration($configuration, $configs);
0 ignored issues
show
Bug introduced by
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 70 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...
72
73
        $this->adapter->loadServicesConfiguration($container);
74
75
        if (!empty($config['dbal'])) {
76
            $this->dbalLoad($config['dbal'], $container);
77
        }
78
79
        if (!empty($config['orm'])) {
80
            if (empty($config['dbal'])) {
81
                throw new \LogicException('Configuring the ORM layer requires to configure the DBAL layer as well.');
82
            }
83
84
            if (!class_exists('Doctrine\ORM\Version')) {
85
                throw new \LogicException('To configure the ORM layer, you must first install the doctrine/orm package.');
86
            }
87
88
            $this->ormLoad($config['orm'], $container);
89
        }
90
    }
91
92
    /**
93
     * Loads the DBAL configuration.
94
     *
95
     * Usage example:
96
     *
97
     *      <doctrine:dbal id="myconn" dbname="sfweb" user="root" />
98
     *
99
     * @param array            $config    An array of configuration settings
100
     * @param ContainerBuilder $container A ContainerBuilder instance
101
     */
102
    protected function dbalLoad(array $config, ContainerBuilder $container)
103
    {
104
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
105
        $loader->load('dbal.xml');
106
107
        if (empty($config['default_connection'])) {
108
            $keys = array_keys($config['connections']);
109
            $config['default_connection'] = reset($keys);
110
        }
111
112
        $this->defaultConnection = $config['default_connection'];
113
114
        $container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection));
115
        $container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false));
116
117
        $container->setParameter('doctrine.dbal.connection_factory.types', $config['types']);
118
119
        $connections = array();
120
121
        foreach (array_keys($config['connections']) as $name) {
122
            $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name);
123
        }
124
125
        $container->setParameter('doctrine.connections', $connections);
126
        $container->setParameter('doctrine.default_connection', $this->defaultConnection);
127
128
        foreach ($config['connections'] as $name => $connection) {
129
            $this->loadDbalConnection($name, $connection, $container);
130
        }
131
    }
132
133
    /**
134
     * Loads a configured DBAL connection.
135
     *
136
     * @param string           $name       The name of the connection
137
     * @param array            $connection A dbal connection configuration.
138
     * @param ContainerBuilder $container  A ContainerBuilder instance
139
     */
140
    protected function loadDbalConnection($name, array $connection, ContainerBuilder $container)
141
    {
142
        // configuration
143
        $defitionClassname = $this->getDefinitionClassname();
144
145
        $configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new $defitionClassname('doctrine.dbal.connection.configuration'));
146
        $logger = null;
147
        if ($connection['logging']) {
148
            $logger = new Reference('doctrine.dbal.logger');
149
        }
150
        unset ($connection['logging']);
151
        if ($connection['profiling']) {
152
            $profilingLoggerId = 'doctrine.dbal.logger.profiling.'.$name;
153
            $container->setDefinition($profilingLoggerId, new $defitionClassname('doctrine.dbal.logger.profiling'));
154
            $profilingLogger = new Reference($profilingLoggerId);
155
            $container->getDefinition('data_collector.doctrine')->addMethodCall('addLogger', array($name, $profilingLogger));
156
157
            if (null !== $logger) {
158
                $chainLogger = new $defitionClassname('doctrine.dbal.logger.chain');
159
                $chainLogger->addMethodCall('addLogger', array($profilingLogger));
160
161
                $loggerId = 'doctrine.dbal.logger.chain.'.$name;
162
                $container->setDefinition($loggerId, $chainLogger);
163
                $logger = new Reference($loggerId);
164
            } else {
165
                $logger = $profilingLogger;
166
            }
167
        }
168
        unset($connection['profiling']);
169
170
        if (isset($connection['auto_commit'])) {
171
            $configuration->addMethodCall('setAutoCommit', array($connection['auto_commit']));
172
        }
173
174
        unset($connection['auto_commit']);
175
176
        if (isset($connection['schema_filter']) && $connection['schema_filter']) {
177
            $configuration->addMethodCall('setFilterSchemaAssetsExpression', array($connection['schema_filter']));
178
        }
179
180
        unset($connection['schema_filter']);
181
182
        if ($logger) {
183
            $configuration->addMethodCall('setSQLLogger', array($logger));
184
        }
185
186
        // event manager
187
        $container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new $defitionClassname('doctrine.dbal.connection.event_manager'));
188
189
        // connection
190
        $options = $this->getConnectionOptions($connection);
191
192
        $def = $container
193
            ->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new $defitionClassname('doctrine.dbal.connection'))
194
            ->setArguments(array(
195
                $options,
196
                new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)),
197
                new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)),
198
                $connection['mapping_types'],
199
            ))
200
        ;
201
202
        if (!empty($connection['use_savepoints'])) {
203
            $def->addMethodCall('setNestTransactionsWithSavepoints', array($connection['use_savepoints']));
204
        }
205
206
        // Create a shard_manager for this connection
207
        if (isset($options['shards'])) {
208
            $shardManagerDefinition = new Definition($options['shardManagerClass'], array(
209
                new Reference(sprintf('doctrine.dbal.%s_connection', $name))
210
            ));
211
            $container->setDefinition(sprintf('doctrine.dbal.%s_shard_manager', $name), $shardManagerDefinition);
212
        }
213
    }
214
215
    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...
216
    {
217
        $options = $connection;
218
219
        if (isset($options['platform_service'])) {
220
            $options['platform'] = new Reference($options['platform_service']);
221
            unset($options['platform_service']);
222
        }
223
        unset($options['mapping_types']);
224
225
        if (isset($options['shard_choser_service'])) {
226
            $options['shard_choser'] = new Reference($options['shard_choser_service']);
227
            unset($options['shard_choser_service']);
228
        }
229
230
        foreach (array(
231
            'options' => 'driverOptions',
232
            'driver_class' => 'driverClass',
233
            'wrapper_class' => 'wrapperClass',
234
            'keep_slave' => 'keepSlave',
235
            'shard_choser' => 'shardChoser',
236
            'shard_manager_class' => 'shardManagerClass',
237
            'server_version' => 'serverVersion',
238
            'default_table_options' => 'defaultTableOptions',
239
        ) as $old => $new) {
240
            if (isset($options[$old])) {
241
                $options[$new] = $options[$old];
242
                unset($options[$old]);
243
            }
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 = array(
252
                'driver' => true, 'driverOptions' => true, 'driverClass' => true,
253
                'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true,
254
                'platform' => true, 'slaves' => true, 'master' => true, 'shards' => true,
255
                'serverVersion' => true,
256
                // included by safety but should have been unset already
257
                'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true,
258
            );
259
            foreach ($options as $key => $value) {
260
                if (isset($nonRewrittenKeys[$key])) {
261
                    continue;
262
                }
263
                $options['master'][$key] = $value;
264
                unset($options[$key]);
265
            }
266
            if (empty($options['wrapperClass'])) {
267
                // Change the wrapper class only if the user does not already forced using a custom one.
268
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Connections\\MasterSlaveConnection';
269
            }
270
        } else {
271
            unset($options['slaves']);
272
        }
273
274
        if (!empty($options['shards'])) {
275
            $nonRewrittenKeys = array(
276
                'driver' => true, 'driverOptions' => true, 'driverClass' => true,
277
                'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true,
278
                'platform' => true, 'slaves' => true, 'global' => true, 'shards' => true,
279
                'serverVersion' => true,
280
                // included by safety but should have been unset already
281
                'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true,
282
            );
283
            foreach ($options as $key => $value) {
284
                if (isset($nonRewrittenKeys[$key])) {
285
                    continue;
286
                }
287
                $options['global'][$key] = $value;
288
                unset($options[$key]);
289
            }
290
            if (empty($options['wrapperClass'])) {
291
                // Change the wrapper class only if the user does not already forced using a custom one.
292
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardConnection';
293
            }
294
            if (empty($options['shardManagerClass'])) {
295
                // Change the shard manager class only if the user does not already forced using a custom one.
296
                $options['shardManagerClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardManager';
297
            }
298
        } else {
299
            unset($options['shards']);
300
        }
301
302
        return $options;
303
    }
304
305
    /**
306
     * Loads the Doctrine ORM configuration.
307
     *
308
     * Usage example:
309
     *
310
     *     <doctrine:orm id="mydm" connection="myconn" />
311
     *
312
     * @param array            $config    An array of configuration settings
313
     * @param ContainerBuilder $container A ContainerBuilder instance
314
     */
315
    protected function ormLoad(array $config, ContainerBuilder $container)
316
    {
317
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
318
        $loader->load('orm.xml');
319
320
        $this->entityManagers = array();
321
        foreach (array_keys($config['entity_managers']) as $name) {
322
            $this->entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name);
323
        }
324
        $container->setParameter('doctrine.entity_managers', $this->entityManagers);
325
326
        if (empty($config['default_entity_manager'])) {
327
            $tmp = array_keys($this->entityManagers);
328
            $config['default_entity_manager'] = reset($tmp);
329
        }
330
        $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']);
331
332
        $options = array('auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace');
333
        foreach ($options as $key) {
334
            $container->setParameter('doctrine.orm.'.$key, $config[$key]);
335
        }
336
337
        $container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager']));
338
339
        $config['entity_managers'] = $this->fixManagersAutoMappings($config['entity_managers'], $container->getParameter('kernel.bundles'));
340
341
        $loadPropertyInfoExtractor = interface_exists('Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface')
342
            && class_exists('Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor');
343
344
        foreach ($config['entity_managers'] as $name => $entityManager) {
345
            $entityManager['name'] = $name;
346
            $this->loadOrmEntityManager($entityManager, $container);
347
348
            if ($loadPropertyInfoExtractor) {
349
                $this->loadPropertyInfoExtractor($name, $container);
350
            }
351
        }
352
353
        if ($config['resolve_target_entities']) {
354
            $def = $container->findDefinition('doctrine.orm.listeners.resolve_target_entity');
355
            foreach ($config['resolve_target_entities'] as $name => $implementation) {
356
                $def->addMethodCall('addResolveTargetEntity', array(
357
                    $name, $implementation, array(),
358
                ));
359
            }
360
361
            // BC: ResolveTargetEntityListener implements the subscriber interface since
362
            // v2.5.0-beta1 (Commit 437f812)
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
363
            if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) {
364
                $def->addTag('doctrine.event_listener', array('event' => 'loadClassMetadata'));
365
            } else {
366
                $def->addTag('doctrine.event_subscriber');
367
            }
368
        }
369
    }
370
371
    /**
372
     * Loads a configured ORM entity manager.
373
     *
374
     * @param array            $entityManager A configured ORM entity manager.
375
     * @param ContainerBuilder $container     A ContainerBuilder instance
376
     */
377
    protected function loadOrmEntityManager(array $entityManager, ContainerBuilder $container)
378
    {
379
        $definitionClassname = $this->getDefinitionClassname();
380
        $ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new $definitionClassname('doctrine.orm.configuration'));
381
382
        $this->loadOrmEntityManagerMappingInformation($entityManager, $ormConfigDef, $container);
383
        $this->loadOrmCacheDrivers($entityManager, $container);
384
385
        if (isset($entityManager['entity_listener_resolver']) && $entityManager['entity_listener_resolver']) {
386
            $container->setAlias(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $entityManager['entity_listener_resolver']);
387
        } else {
388
            $definition = new Definition('%doctrine.orm.entity_listener_resolver.class%');
389
            $definition->addArgument(new Reference('service_container'));
390
            $container->setDefinition(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $definition);
391
        }
392
393
        $methods = array(
394
            'setMetadataCacheImpl' => new Reference(sprintf('doctrine.orm.%s_metadata_cache', $entityManager['name'])),
395
            'setQueryCacheImpl' => new Reference(sprintf('doctrine.orm.%s_query_cache', $entityManager['name'])),
396
            'setResultCacheImpl' => new Reference(sprintf('doctrine.orm.%s_result_cache', $entityManager['name'])),
397
            'setMetadataDriverImpl' => new Reference('doctrine.orm.'.$entityManager['name'].'_metadata_driver'),
398
            'setProxyDir' => '%doctrine.orm.proxy_dir%',
399
            'setProxyNamespace' => '%doctrine.orm.proxy_namespace%',
400
            'setAutoGenerateProxyClasses' => '%doctrine.orm.auto_generate_proxy_classes%',
401
            'setClassMetadataFactoryName' => $entityManager['class_metadata_factory_name'],
402
            'setDefaultRepositoryClassName' => $entityManager['default_repository_class'],
403
        );
404
        // check for version to keep BC
405
        if (version_compare(Version::VERSION, "2.3.0-DEV") >= 0) {
406
            $methods = array_merge($methods, array(
407
                'setNamingStrategy' => new Reference($entityManager['naming_strategy']),
408
                'setQuoteStrategy' => new Reference($entityManager['quote_strategy']),
409
            ));
410
        }
411
412
        if (version_compare(Version::VERSION, "2.4.0-DEV") >= 0) {
413
            $methods = array_merge($methods, array(
414
                'setEntityListenerResolver' => new Reference(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name'])),
415
            ));
416
        }
417
418
        if (version_compare(Version::VERSION, "2.5.0-DEV") >= 0) {
419
            $listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $entityManager['name']);
420
            $listenerDef = $container->setDefinition($listenerId, new Definition('%doctrine.orm.listeners.attach_entity_listeners.class%'));
421
            $listenerTagParams = array('event' => 'loadClassMetadata');
422
            if (isset($entityManager['connection'])) {
423
                $listenerTagParams['connection'] = $entityManager['connection'];
424
            }
425
            $listenerDef->addTag('doctrine.event_listener', $listenerTagParams);
426
        }
427
428
        if (isset($entityManager['second_level_cache'])) {
429
            $this->loadOrmSecondLevelCache($entityManager, $ormConfigDef, $container);
430
        }
431
432
        if ($entityManager['repository_factory']) {
433
            $methods['setRepositoryFactory'] = new Reference($entityManager['repository_factory']);
434
        }
435
436
        foreach ($methods as $method => $arg) {
437
            $ormConfigDef->addMethodCall($method, array($arg));
438
        }
439
440
        foreach ($entityManager['hydrators'] as $name => $class) {
441
            $ormConfigDef->addMethodCall('addCustomHydrationMode', array($name, $class));
442
        }
443
444
        if (!empty($entityManager['dql'])) {
445
            foreach ($entityManager['dql']['string_functions'] as $name => $function) {
446
                $ormConfigDef->addMethodCall('addCustomStringFunction', array($name, $function));
447
            }
448
            foreach ($entityManager['dql']['numeric_functions'] as $name => $function) {
449
                $ormConfigDef->addMethodCall('addCustomNumericFunction', array($name, $function));
450
            }
451
            foreach ($entityManager['dql']['datetime_functions'] as $name => $function) {
452
                $ormConfigDef->addMethodCall('addCustomDatetimeFunction', array($name, $function));
453
            }
454
        }
455
456
        $enabledFilters = array();
457
        $filtersParameters = array();
458
        foreach ($entityManager['filters'] as $name => $filter) {
459
            $ormConfigDef->addMethodCall('addFilter', array($name, $filter['class']));
460
            if ($filter['enabled']) {
461
                $enabledFilters[] = $name;
462
            }
463
            if ($filter['parameters']) {
464
                $filtersParameters[$name] = $filter['parameters'];
465
            }
466
        }
467
468
        $managerConfiguratorName = sprintf('doctrine.orm.%s_manager_configurator', $entityManager['name']);
469
        $container
470
            ->setDefinition($managerConfiguratorName, new $definitionClassname('doctrine.orm.manager_configurator.abstract'))
471
            ->replaceArgument(0, $enabledFilters)
472
            ->replaceArgument(1, $filtersParameters)
473
        ;
474
475
        if (!isset($entityManager['connection'])) {
476
            $entityManager['connection'] = $this->defaultConnection;
477
        }
478
479
        $container
480
            ->setDefinition(sprintf('doctrine.orm.%s_entity_manager', $entityManager['name']), new $definitionClassname('doctrine.orm.entity_manager.abstract'))
481
            ->setArguments(array(
482
                new Reference(sprintf('doctrine.dbal.%s_connection', $entityManager['connection'])),
483
                new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])),
484
            ))
485
            ->setConfigurator(array(new Reference($managerConfiguratorName), 'configure'))
486
        ;
487
488
        $container->setAlias(
489
            sprintf('doctrine.orm.%s_entity_manager.event_manager', $entityManager['name']),
490
            new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $entityManager['connection']), false)
491
        );
492
493
        if (isset($entityManager['entity_listeners'])) {
494
            if (!isset($listenerDef)) {
495
                throw new InvalidArgumentException('Entity listeners configuration requires doctrine-orm 2.5.0 or newer');
496
            }
497
498
            $entities = $entityManager['entity_listeners']['entities'];
499
500
            foreach ($entities as $entityListenerClass => $entity) {
501
                foreach ($entity['listeners'] as $listenerClass => $listener) {
502
                    foreach ($listener['events'] as $listenerEvent) {
503
                        $listenerEventName = $listenerEvent['type'];
504
                        $listenerMethod = $listenerEvent['method'];
505
506
                        $listenerDef->addMethodCall('addEntityListener', array(
507
                            $entityListenerClass, $listenerClass, $listenerEventName, $listenerMethod,
508
                        ));
509
                    }
510
                }
511
            }
512
513
        }
514
    }
515
516
    /**
517
     * Loads an ORM entity managers bundle mapping information.
518
     *
519
     * There are two distinct configuration possibilities for mapping information:
520
     *
521
     * 1. Specify a bundle and optionally details where the entity and mapping information reside.
522
     * 2. Specify an arbitrary mapping location.
523
     *
524
     * @example
525
     *
526
     *  doctrine.orm:
527
     *     mappings:
528
     *         MyBundle1: ~
529
     *         MyBundle2: yml
530
     *         MyBundle3: { type: annotation, dir: Entities/ }
531
     *         MyBundle4: { type: xml, dir: Resources/config/doctrine/mapping }
532
     *         MyBundle5:
533
     *             type: yml
534
     *             dir: bundle-mappings/
535
     *             alias: BundleAlias
536
     *         arbitrary_key:
537
     *             type: xml
538
     *             dir: %kernel.root_dir%/../src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entities
539
     *             prefix: DoctrineExtensions\Entities\
540
     *             alias: DExt
541
     *
542
     * In the case of bundles everything is really optional (which leads to autodetection for this bundle) but
543
     * in the mappings key everything except alias is a required argument.
544
     *
545
     * @param array            $entityManager A configured ORM entity manager
546
     * @param Definition       $ormConfigDef  A Definition instance
547
     * @param ContainerBuilder $container     A ContainerBuilder instance
548
     */
549
    protected function loadOrmEntityManagerMappingInformation(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
550
    {
551
        // reset state of drivers and alias map. They are only used by this methods and children.
552
        $this->drivers = array();
553
        $this->aliasMap = array();
554
555
        $this->loadMappingInformation($entityManager, $container);
556
        $this->registerMappingDrivers($entityManager, $container);
557
558
        $ormConfigDef->addMethodCall('setEntityNamespaces', array($this->aliasMap));
559
    }
560
561
    /**
562
     * Loads an ORM second level cache bundle mapping information.
563
     *
564
     * @example
565
     *  entity_managers:
566
     *      default:
567
     *          second_level_cache:
568
     *              region_cache_driver: apc
569
     *              log_enabled: true
570
     *              regions:
571
     *                  my_service_region:
572
     *                      type: service
573
     *                      service : "my_service_region"
574
     *
575
     *                  my_query_region:
576
     *                      lifetime: 300
577
     *                      cache_driver: array
578
     *                      type: filelock
579
     *
580
     *                  my_entity_region:
581
     *                      lifetime: 600
582
     *                      cache_driver:
583
     *                          type: apc
584
     *
585
     * @param array            $entityManager A configured ORM entity manager
586
     * @param Definition       $ormConfigDef  A Definition instance
587
     * @param ContainerBuilder $container     A ContainerBuilder instance
588
     */
589
    protected function loadOrmSecondLevelCache(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
590
    {
591
        if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) {
592
            throw new \InvalidArgumentException('Second-level cache requires doctrine-orm 2.5.0 or newer');
593
        }
594
595
        $driverId = null;
596
        $enabled = $entityManager['second_level_cache']['enabled'];
597
598
        if (isset($entityManager['second_level_cache']['region_cache_driver'])) {
599
            $driverName = 'second_level_cache.region_cache_driver';
600
            $driverMap = $entityManager['second_level_cache']['region_cache_driver'];
601
            $driverId = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
602
        }
603
604
        $configId = sprintf('doctrine.orm.%s_second_level_cache.cache_configuration', $entityManager['name']);
605
        $regionsId = sprintf('doctrine.orm.%s_second_level_cache.regions_configuration', $entityManager['name']);
606
        $driverId = $driverId ?: sprintf('doctrine.orm.%s_second_level_cache.region_cache_driver', $entityManager['name']);
607
        $configDef = $container->setDefinition($configId, new Definition('%doctrine.orm.second_level_cache.cache_configuration.class%'));
608
        $regionsDef = $container->setDefinition($regionsId, new Definition('%doctrine.orm.second_level_cache.regions_configuration.class%'));
609
610
        $slcFactoryId = sprintf('doctrine.orm.%s_second_level_cache.default_cache_factory', $entityManager['name']);
611
        $factoryClass = isset($entityManager['second_level_cache']['factory']) ? $entityManager['second_level_cache']['factory'] : '%doctrine.orm.second_level_cache.default_cache_factory.class%';
612
613
        $definition = new Definition($factoryClass, array(new Reference($regionsId), new Reference($driverId)));
614
615
        $slcFactoryDef = $container
616
            ->setDefinition($slcFactoryId, $definition);
617
618
        if (isset($entityManager['second_level_cache']['regions'])) {
619
            foreach ($entityManager['second_level_cache']['regions'] as $name => $region) {
620
                $regionRef = null;
621
                $regionType = $region['type'];
622
623
                if ($regionType === 'service') {
624
                    $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
625
                    $regionRef = new Reference($region['service']);
626
627
                    $container->setAlias($regionId, new Alias($region['service'], false));
628
                }
629
630
                if ($regionType === 'default' || $regionType === 'filelock') {
631
                    $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
632
                    $driverName = sprintf('second_level_cache.region.%s_driver', $name);
633
                    $driverMap = $region['cache_driver'];
634
                    $driverId = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
635
                    $regionRef = new Reference($regionId);
636
637
                    $container
638
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.default_region.class%'))
639
                        ->setArguments(array($name, new Reference($driverId), $region['lifetime']));
640
                }
641
642
                if ($regionType === 'filelock') {
643
                    $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s_filelock', $entityManager['name'], $name);
644
645
                    $container
646
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.filelock_region.class%'))
647
                        ->setArguments(array($regionRef, $region['lock_path'], $region['lock_lifetime']));
648
649
                    $regionRef = new Reference($regionId);
650
                    $regionsDef->addMethodCall('getLockLifetime', array($name, $region['lock_lifetime']));
651
                }
652
653
                $regionsDef->addMethodCall('setLifetime', array($name, $region['lifetime']));
654
                $slcFactoryDef->addMethodCall('setRegion', array($regionRef));
655
            }
656
        }
657
658
        if ($entityManager['second_level_cache']['log_enabled']) {
659
            $loggerChainId = sprintf('doctrine.orm.%s_second_level_cache.logger_chain', $entityManager['name']);
660
            $loggerStatsId = sprintf('doctrine.orm.%s_second_level_cache.logger_statistics', $entityManager['name']);
661
            $loggerChaingDef = $container->setDefinition($loggerChainId, new Definition('%doctrine.orm.second_level_cache.logger_chain.class%'));
662
            $loggerStatsDef = $container->setDefinition($loggerStatsId, new Definition('%doctrine.orm.second_level_cache.logger_statistics.class%'));
663
664
            $loggerChaingDef->addMethodCall('setLogger', array('statistics', $loggerStatsDef));
665
            $configDef->addMethodCall('setCacheLogger', array($loggerChaingDef));
666
667
            foreach ($entityManager['second_level_cache']['loggers'] as $name => $logger) {
668
                $loggerId = sprintf('doctrine.orm.%s_second_level_cache.logger.%s', $entityManager['name'], $name);
669
                $loggerRef = new Reference($logger['service']);
670
671
                $container->setAlias($loggerId, new Alias($logger['service'], false));
672
                $loggerChaingDef->addMethodCall('setLogger', array($name, $loggerRef));
673
            }
674
        }
675
676
        $configDef->addMethodCall('setCacheFactory', array($slcFactoryDef));
677
        $configDef->addMethodCall('setRegionsConfiguration', array($regionsDef));
678
        $ormConfigDef->addMethodCall('setSecondLevelCacheEnabled', array($enabled));
679
        $ormConfigDef->addMethodCall('setSecondLevelCacheConfiguration', array($configDef));
680
    }
681
682
    /**
683
     * {@inheritDoc}
684
     */
685
    protected function getObjectManagerElementName($name)
686
    {
687
        return 'doctrine.orm.'.$name;
688
    }
689
690
    protected function getMappingObjectDefaultName()
691
    {
692
        return 'Entity';
693
    }
694
695
    /**
696
     * {@inheritDoc}
697
     */
698
    protected function getMappingResourceConfigDirectory()
699
    {
700
        return 'Resources/config/doctrine';
701
    }
702
703
    /**
704
     * {@inheritDoc}
705
     */
706
    protected function getMappingResourceExtension()
707
    {
708
        return 'orm';
709
    }
710
711
    /**
712
     * {@inheritDoc}
713
     */
714
    protected function loadCacheDriver($driverName, $entityManagerName, array $driverMap, ContainerBuilder $container)
715
    {
716
        if (!empty($driverMap['cache_provider'])) {
717
            $aliasId = $this->getObjectManagerElementName(sprintf('%s_%s', $entityManagerName, $driverName));
718
            $serviceId = sprintf('doctrine_cache.providers.%s', $driverMap['cache_provider']);
719
720
            $container->setAlias($aliasId, new Alias($serviceId, false));
721
722
            return $aliasId;
723
        }
724
725
        return $this->adapter->loadCacheDriver($driverName, $entityManagerName, $driverMap, $container);
726
    }
727
728
    /**
729
     * Loads a configured entity managers cache drivers.
730
     *
731
     * @param array            $entityManager A configured ORM entity manager.
732
     * @param ContainerBuilder $container     A ContainerBuilder instance
733
     */
734
    protected function loadOrmCacheDrivers(array $entityManager, ContainerBuilder $container)
735
    {
736
        $this->loadCacheDriver('metadata_cache', $entityManager['name'], $entityManager['metadata_cache_driver'], $container);
737
        $this->loadCacheDriver('result_cache', $entityManager['name'], $entityManager['result_cache_driver'], $container);
738
        $this->loadCacheDriver('query_cache', $entityManager['name'], $entityManager['query_cache_driver'], $container);
739
    }
740
741
    /**
742
     * Loads a property info extractor for each defined entity manager.
743
     *
744
     * @param string           $entityManagerName
745
     * @param ContainerBuilder $container
746
     */
747
    private function loadPropertyInfoExtractor($entityManagerName, ContainerBuilder $container)
748
    {
749
        $metadataFactoryService = sprintf('doctrine.orm.%s_entity_manager.metadata_factory', $entityManagerName);
750
751
        $metadataFactoryDefinition = $container->register($metadataFactoryService, 'Doctrine\Common\Persistence\Mapping\ClassMetadataFactory');
752
        $metadataFactoryDefinition->setFactory(array(
753
            new Reference(sprintf('doctrine.orm.%s_entity_manager', $entityManagerName)),
754
            'getMetadataFactory'
755
        ));
756
        $metadataFactoryDefinition->setPublic(false);
757
758
        $propertyExtractorDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.property_info_extractor', $entityManagerName), 'Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor');
759
        $propertyExtractorDefinition->addArgument(new Reference($metadataFactoryService));
760
        $propertyExtractorDefinition->addTag('property_info.list_extractor', array('priority' => -1001));
761
        $propertyExtractorDefinition->addTag('property_info.type_extractor', array('priority' => -999));
762
    }
763
764
    /**
765
     * @param array            $objectManager
766
     * @param ContainerBuilder $container
767
     * @param string           $cacheName
768
     */
769
    public function loadObjectManagerCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName)
770
    {
771
        $this->loadCacheDriver($cacheName, $objectManager['name'], $objectManager[$cacheName.'_driver'], $container);
772
    }
773
774
    /**
775
     * {@inheritDoc}
776
     */
777
    public function getXsdValidationBasePath()
778
    {
779
        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...
780
    }
781
782
    /**
783
     * {@inheritDoc}
784
     */
785
    public function getNamespace()
786
    {
787
        return 'http://symfony.com/schema/dic/doctrine';
788
    }
789
790
    /**
791
     * {@inheritDoc}
792
     */
793
    public function getConfiguration(array $config, ContainerBuilder $container)
794
    {
795
        return new Configuration($container->getParameter('kernel.debug'));
796
    }
797
798
    /**
799
     * @return string
800
     */
801
    private function getDefinitionClassname(): string
802
    {
803
        return class_exists(ChildDefinition::class) ? ChildDefinition::class : DefinitionDecorator::class;
804
    }
805
}
806