Completed
Push — master ( 28438d...1818d3 )
by Marco
05:22
created

DoctrineExtension::loadDbalConnection()   F

Complexity

Conditions 11
Paths 384

Size

Total Lines 79
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 79
rs 3.8532
c 0
b 0
f 0
cc 11
eloc 46
nc 384
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
        // Set class in case "wrapper_class" option was used to assist IDEs
203
        if (isset($options['wrapperClass'])) {
204
            $def->setClass($options['wrapperClass']);
205
        }
206
207
        if (!empty($connection['use_savepoints'])) {
208
            $def->addMethodCall('setNestTransactionsWithSavepoints', array($connection['use_savepoints']));
209
        }
210
211
        // Create a shard_manager for this connection
212
        if (isset($options['shards'])) {
213
            $shardManagerDefinition = new Definition($options['shardManagerClass'], array(
214
                new Reference(sprintf('doctrine.dbal.%s_connection', $name))
215
            ));
216
            $container->setDefinition(sprintf('doctrine.dbal.%s_shard_manager', $name), $shardManagerDefinition);
217
        }
218
    }
219
220
    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...
221
    {
222
        $options = $connection;
223
224
        if (isset($options['platform_service'])) {
225
            $options['platform'] = new Reference($options['platform_service']);
226
            unset($options['platform_service']);
227
        }
228
        unset($options['mapping_types']);
229
230
        if (isset($options['shard_choser_service'])) {
231
            $options['shard_choser'] = new Reference($options['shard_choser_service']);
232
            unset($options['shard_choser_service']);
233
        }
234
235
        foreach (array(
236
            'options' => 'driverOptions',
237
            'driver_class' => 'driverClass',
238
            'wrapper_class' => 'wrapperClass',
239
            'keep_slave' => 'keepSlave',
240
            'shard_choser' => 'shardChoser',
241
            'shard_manager_class' => 'shardManagerClass',
242
            'server_version' => 'serverVersion',
243
            'default_table_options' => 'defaultTableOptions',
244
        ) as $old => $new) {
245
            if (isset($options[$old])) {
246
                $options[$new] = $options[$old];
247
                unset($options[$old]);
248
            }
249
        }
250
251
        if (!empty($options['slaves']) && !empty($options['shards'])) {
252
            throw new InvalidArgumentException('Sharding and master-slave connection cannot be used together');
253
        }
254
255
        if (!empty($options['slaves'])) {
256
            $nonRewrittenKeys = array(
257
                'driver' => true, 'driverOptions' => true, 'driverClass' => true,
258
                'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true,
259
                'platform' => true, 'slaves' => true, 'master' => true, 'shards' => true,
260
                'serverVersion' => true,
261
                // included by safety but should have been unset already
262
                'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true,
263
            );
264
            foreach ($options as $key => $value) {
265
                if (isset($nonRewrittenKeys[$key])) {
266
                    continue;
267
                }
268
                $options['master'][$key] = $value;
269
                unset($options[$key]);
270
            }
271
            if (empty($options['wrapperClass'])) {
272
                // Change the wrapper class only if the user does not already forced using a custom one.
273
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Connections\\MasterSlaveConnection';
274
            }
275
        } else {
276
            unset($options['slaves']);
277
        }
278
279
        if (!empty($options['shards'])) {
280
            $nonRewrittenKeys = array(
281
                'driver' => true, 'driverOptions' => true, 'driverClass' => true,
282
                'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true,
283
                'platform' => true, 'slaves' => true, 'global' => true, 'shards' => true,
284
                'serverVersion' => true,
285
                // included by safety but should have been unset already
286
                'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true,
287
            );
288
            foreach ($options as $key => $value) {
289
                if (isset($nonRewrittenKeys[$key])) {
290
                    continue;
291
                }
292
                $options['global'][$key] = $value;
293
                unset($options[$key]);
294
            }
295
            if (empty($options['wrapperClass'])) {
296
                // Change the wrapper class only if the user does not already forced using a custom one.
297
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardConnection';
298
            }
299
            if (empty($options['shardManagerClass'])) {
300
                // Change the shard manager class only if the user does not already forced using a custom one.
301
                $options['shardManagerClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardManager';
302
            }
303
        } else {
304
            unset($options['shards']);
305
        }
306
307
        return $options;
308
    }
309
310
    /**
311
     * Loads the Doctrine ORM configuration.
312
     *
313
     * Usage example:
314
     *
315
     *     <doctrine:orm id="mydm" connection="myconn" />
316
     *
317
     * @param array            $config    An array of configuration settings
318
     * @param ContainerBuilder $container A ContainerBuilder instance
319
     */
320
    protected function ormLoad(array $config, ContainerBuilder $container)
321
    {
322
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
323
        $loader->load('orm.xml');
324
325
        $this->entityManagers = array();
326
        foreach (array_keys($config['entity_managers']) as $name) {
327
            $this->entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name);
328
        }
329
        $container->setParameter('doctrine.entity_managers', $this->entityManagers);
330
331
        if (empty($config['default_entity_manager'])) {
332
            $tmp = array_keys($this->entityManagers);
333
            $config['default_entity_manager'] = reset($tmp);
334
        }
335
        $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']);
336
337
        $options = array('auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace');
338
        foreach ($options as $key) {
339
            $container->setParameter('doctrine.orm.'.$key, $config[$key]);
340
        }
341
342
        $container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager']));
343
344
        $config['entity_managers'] = $this->fixManagersAutoMappings($config['entity_managers'], $container->getParameter('kernel.bundles'));
345
346
        $loadPropertyInfoExtractor = interface_exists('Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface')
347
            && class_exists('Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor');
348
349
        foreach ($config['entity_managers'] as $name => $entityManager) {
350
            $entityManager['name'] = $name;
351
            $this->loadOrmEntityManager($entityManager, $container);
352
353
            if ($loadPropertyInfoExtractor) {
354
                $this->loadPropertyInfoExtractor($name, $container);
355
            }
356
        }
357
358
        if ($config['resolve_target_entities']) {
359
            $def = $container->findDefinition('doctrine.orm.listeners.resolve_target_entity');
360
            foreach ($config['resolve_target_entities'] as $name => $implementation) {
361
                $def->addMethodCall('addResolveTargetEntity', array(
362
                    $name, $implementation, array(),
363
                ));
364
            }
365
366
            // BC: ResolveTargetEntityListener implements the subscriber interface since
367
            // 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...
368
            if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) {
369
                $def->addTag('doctrine.event_listener', array('event' => 'loadClassMetadata'));
370
            } else {
371
                $def->addTag('doctrine.event_subscriber');
372
            }
373
        }
374
    }
375
376
    /**
377
     * Loads a configured ORM entity manager.
378
     *
379
     * @param array            $entityManager A configured ORM entity manager.
380
     * @param ContainerBuilder $container     A ContainerBuilder instance
381
     */
382
    protected function loadOrmEntityManager(array $entityManager, ContainerBuilder $container)
383
    {
384
        $definitionClassname = $this->getDefinitionClassname();
385
        $ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new $definitionClassname('doctrine.orm.configuration'));
386
387
        $this->loadOrmEntityManagerMappingInformation($entityManager, $ormConfigDef, $container);
388
        $this->loadOrmCacheDrivers($entityManager, $container);
389
390
        if (isset($entityManager['entity_listener_resolver']) && $entityManager['entity_listener_resolver']) {
391
            $container->setAlias(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $entityManager['entity_listener_resolver']);
392
        } else {
393
            $definition = new Definition('%doctrine.orm.entity_listener_resolver.class%');
394
            $definition->addArgument(new Reference('service_container'));
395
            $container->setDefinition(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $definition);
396
        }
397
398
        $methods = array(
399
            'setMetadataCacheImpl' => new Reference(sprintf('doctrine.orm.%s_metadata_cache', $entityManager['name'])),
400
            'setQueryCacheImpl' => new Reference(sprintf('doctrine.orm.%s_query_cache', $entityManager['name'])),
401
            'setResultCacheImpl' => new Reference(sprintf('doctrine.orm.%s_result_cache', $entityManager['name'])),
402
            'setMetadataDriverImpl' => new Reference('doctrine.orm.'.$entityManager['name'].'_metadata_driver'),
403
            'setProxyDir' => '%doctrine.orm.proxy_dir%',
404
            'setProxyNamespace' => '%doctrine.orm.proxy_namespace%',
405
            'setAutoGenerateProxyClasses' => '%doctrine.orm.auto_generate_proxy_classes%',
406
            'setClassMetadataFactoryName' => $entityManager['class_metadata_factory_name'],
407
            'setDefaultRepositoryClassName' => $entityManager['default_repository_class'],
408
        );
409
        // check for version to keep BC
410
        if (version_compare(Version::VERSION, "2.3.0-DEV") >= 0) {
411
            $methods = array_merge($methods, array(
412
                'setNamingStrategy' => new Reference($entityManager['naming_strategy']),
413
                'setQuoteStrategy' => new Reference($entityManager['quote_strategy']),
414
            ));
415
        }
416
417
        if (version_compare(Version::VERSION, "2.4.0-DEV") >= 0) {
418
            $methods = array_merge($methods, array(
419
                'setEntityListenerResolver' => new Reference(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name'])),
420
            ));
421
        }
422
423
        if (version_compare(Version::VERSION, "2.5.0-DEV") >= 0) {
424
            $listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $entityManager['name']);
425
            $listenerDef = $container->setDefinition($listenerId, new Definition('%doctrine.orm.listeners.attach_entity_listeners.class%'));
426
            $listenerTagParams = array('event' => 'loadClassMetadata');
427
            if (isset($entityManager['connection'])) {
428
                $listenerTagParams['connection'] = $entityManager['connection'];
429
            }
430
            $listenerDef->addTag('doctrine.event_listener', $listenerTagParams);
431
        }
432
433
        if (isset($entityManager['second_level_cache'])) {
434
            $this->loadOrmSecondLevelCache($entityManager, $ormConfigDef, $container);
435
        }
436
437
        if ($entityManager['repository_factory']) {
438
            $methods['setRepositoryFactory'] = new Reference($entityManager['repository_factory']);
439
        }
440
441
        foreach ($methods as $method => $arg) {
442
            $ormConfigDef->addMethodCall($method, array($arg));
443
        }
444
445
        foreach ($entityManager['hydrators'] as $name => $class) {
446
            $ormConfigDef->addMethodCall('addCustomHydrationMode', array($name, $class));
447
        }
448
449
        if (!empty($entityManager['dql'])) {
450
            foreach ($entityManager['dql']['string_functions'] as $name => $function) {
451
                $ormConfigDef->addMethodCall('addCustomStringFunction', array($name, $function));
452
            }
453
            foreach ($entityManager['dql']['numeric_functions'] as $name => $function) {
454
                $ormConfigDef->addMethodCall('addCustomNumericFunction', array($name, $function));
455
            }
456
            foreach ($entityManager['dql']['datetime_functions'] as $name => $function) {
457
                $ormConfigDef->addMethodCall('addCustomDatetimeFunction', array($name, $function));
458
            }
459
        }
460
461
        $enabledFilters = array();
462
        $filtersParameters = array();
463
        foreach ($entityManager['filters'] as $name => $filter) {
464
            $ormConfigDef->addMethodCall('addFilter', array($name, $filter['class']));
465
            if ($filter['enabled']) {
466
                $enabledFilters[] = $name;
467
            }
468
            if ($filter['parameters']) {
469
                $filtersParameters[$name] = $filter['parameters'];
470
            }
471
        }
472
473
        $managerConfiguratorName = sprintf('doctrine.orm.%s_manager_configurator', $entityManager['name']);
474
        $container
475
            ->setDefinition($managerConfiguratorName, new $definitionClassname('doctrine.orm.manager_configurator.abstract'))
476
            ->replaceArgument(0, $enabledFilters)
477
            ->replaceArgument(1, $filtersParameters)
478
        ;
479
480
        if (!isset($entityManager['connection'])) {
481
            $entityManager['connection'] = $this->defaultConnection;
482
        }
483
484
        $container
485
            ->setDefinition(sprintf('doctrine.orm.%s_entity_manager', $entityManager['name']), new $definitionClassname('doctrine.orm.entity_manager.abstract'))
486
            ->setArguments(array(
487
                new Reference(sprintf('doctrine.dbal.%s_connection', $entityManager['connection'])),
488
                new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])),
489
            ))
490
            ->setConfigurator(array(new Reference($managerConfiguratorName), 'configure'))
491
        ;
492
493
        $container->setAlias(
494
            sprintf('doctrine.orm.%s_entity_manager.event_manager', $entityManager['name']),
495
            new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $entityManager['connection']), false)
496
        );
497
498
        if (isset($entityManager['entity_listeners'])) {
499
            if (!isset($listenerDef)) {
500
                throw new InvalidArgumentException('Entity listeners configuration requires doctrine-orm 2.5.0 or newer');
501
            }
502
503
            $entities = $entityManager['entity_listeners']['entities'];
504
505
            foreach ($entities as $entityListenerClass => $entity) {
506
                foreach ($entity['listeners'] as $listenerClass => $listener) {
507
                    foreach ($listener['events'] as $listenerEvent) {
508
                        $listenerEventName = $listenerEvent['type'];
509
                        $listenerMethod = $listenerEvent['method'];
510
511
                        $listenerDef->addMethodCall('addEntityListener', array(
512
                            $entityListenerClass, $listenerClass, $listenerEventName, $listenerMethod,
513
                        ));
514
                    }
515
                }
516
            }
517
518
        }
519
    }
520
521
    /**
522
     * Loads an ORM entity managers bundle mapping information.
523
     *
524
     * There are two distinct configuration possibilities for mapping information:
525
     *
526
     * 1. Specify a bundle and optionally details where the entity and mapping information reside.
527
     * 2. Specify an arbitrary mapping location.
528
     *
529
     * @example
530
     *
531
     *  doctrine.orm:
532
     *     mappings:
533
     *         MyBundle1: ~
534
     *         MyBundle2: yml
535
     *         MyBundle3: { type: annotation, dir: Entities/ }
536
     *         MyBundle4: { type: xml, dir: Resources/config/doctrine/mapping }
537
     *         MyBundle5:
538
     *             type: yml
539
     *             dir: bundle-mappings/
540
     *             alias: BundleAlias
541
     *         arbitrary_key:
542
     *             type: xml
543
     *             dir: %kernel.root_dir%/../src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entities
544
     *             prefix: DoctrineExtensions\Entities\
545
     *             alias: DExt
546
     *
547
     * In the case of bundles everything is really optional (which leads to autodetection for this bundle) but
548
     * in the mappings key everything except alias is a required argument.
549
     *
550
     * @param array            $entityManager A configured ORM entity manager
551
     * @param Definition       $ormConfigDef  A Definition instance
552
     * @param ContainerBuilder $container     A ContainerBuilder instance
553
     */
554
    protected function loadOrmEntityManagerMappingInformation(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
555
    {
556
        // reset state of drivers and alias map. They are only used by this methods and children.
557
        $this->drivers = array();
558
        $this->aliasMap = array();
559
560
        $this->loadMappingInformation($entityManager, $container);
561
        $this->registerMappingDrivers($entityManager, $container);
562
563
        $ormConfigDef->addMethodCall('setEntityNamespaces', array($this->aliasMap));
564
    }
565
566
    /**
567
     * Loads an ORM second level cache bundle mapping information.
568
     *
569
     * @example
570
     *  entity_managers:
571
     *      default:
572
     *          second_level_cache:
573
     *              region_cache_driver: apc
574
     *              log_enabled: true
575
     *              regions:
576
     *                  my_service_region:
577
     *                      type: service
578
     *                      service : "my_service_region"
579
     *
580
     *                  my_query_region:
581
     *                      lifetime: 300
582
     *                      cache_driver: array
583
     *                      type: filelock
584
     *
585
     *                  my_entity_region:
586
     *                      lifetime: 600
587
     *                      cache_driver:
588
     *                          type: apc
589
     *
590
     * @param array            $entityManager A configured ORM entity manager
591
     * @param Definition       $ormConfigDef  A Definition instance
592
     * @param ContainerBuilder $container     A ContainerBuilder instance
593
     */
594
    protected function loadOrmSecondLevelCache(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
595
    {
596
        if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) {
597
            throw new \InvalidArgumentException('Second-level cache requires doctrine-orm 2.5.0 or newer');
598
        }
599
600
        $driverId = null;
601
        $enabled = $entityManager['second_level_cache']['enabled'];
602
603
        if (isset($entityManager['second_level_cache']['region_cache_driver'])) {
604
            $driverName = 'second_level_cache.region_cache_driver';
605
            $driverMap = $entityManager['second_level_cache']['region_cache_driver'];
606
            $driverId = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
607
        }
608
609
        $configId = sprintf('doctrine.orm.%s_second_level_cache.cache_configuration', $entityManager['name']);
610
        $regionsId = sprintf('doctrine.orm.%s_second_level_cache.regions_configuration', $entityManager['name']);
611
        $driverId = $driverId ?: sprintf('doctrine.orm.%s_second_level_cache.region_cache_driver', $entityManager['name']);
612
        $configDef = $container->setDefinition($configId, new Definition('%doctrine.orm.second_level_cache.cache_configuration.class%'));
613
        $regionsDef = $container->setDefinition($regionsId, new Definition('%doctrine.orm.second_level_cache.regions_configuration.class%'));
614
615
        $slcFactoryId = sprintf('doctrine.orm.%s_second_level_cache.default_cache_factory', $entityManager['name']);
616
        $factoryClass = isset($entityManager['second_level_cache']['factory']) ? $entityManager['second_level_cache']['factory'] : '%doctrine.orm.second_level_cache.default_cache_factory.class%';
617
618
        $definition = new Definition($factoryClass, array(new Reference($regionsId), new Reference($driverId)));
619
620
        $slcFactoryDef = $container
621
            ->setDefinition($slcFactoryId, $definition);
622
623
        if (isset($entityManager['second_level_cache']['regions'])) {
624
            foreach ($entityManager['second_level_cache']['regions'] as $name => $region) {
625
                $regionRef = null;
626
                $regionType = $region['type'];
627
628
                if ($regionType === 'service') {
629
                    $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
630
                    $regionRef = new Reference($region['service']);
631
632
                    $container->setAlias($regionId, new Alias($region['service'], false));
633
                }
634
635
                if ($regionType === 'default' || $regionType === 'filelock') {
636
                    $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
637
                    $driverName = sprintf('second_level_cache.region.%s_driver', $name);
638
                    $driverMap = $region['cache_driver'];
639
                    $driverId = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
640
                    $regionRef = new Reference($regionId);
641
642
                    $container
643
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.default_region.class%'))
644
                        ->setArguments(array($name, new Reference($driverId), $region['lifetime']));
645
                }
646
647
                if ($regionType === 'filelock') {
648
                    $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s_filelock', $entityManager['name'], $name);
649
650
                    $container
651
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.filelock_region.class%'))
652
                        ->setArguments(array($regionRef, $region['lock_path'], $region['lock_lifetime']));
653
654
                    $regionRef = new Reference($regionId);
655
                    $regionsDef->addMethodCall('getLockLifetime', array($name, $region['lock_lifetime']));
656
                }
657
658
                $regionsDef->addMethodCall('setLifetime', array($name, $region['lifetime']));
659
                $slcFactoryDef->addMethodCall('setRegion', array($regionRef));
660
            }
661
        }
662
663
        if ($entityManager['second_level_cache']['log_enabled']) {
664
            $loggerChainId = sprintf('doctrine.orm.%s_second_level_cache.logger_chain', $entityManager['name']);
665
            $loggerStatsId = sprintf('doctrine.orm.%s_second_level_cache.logger_statistics', $entityManager['name']);
666
            $loggerChaingDef = $container->setDefinition($loggerChainId, new Definition('%doctrine.orm.second_level_cache.logger_chain.class%'));
667
            $loggerStatsDef = $container->setDefinition($loggerStatsId, new Definition('%doctrine.orm.second_level_cache.logger_statistics.class%'));
668
669
            $loggerChaingDef->addMethodCall('setLogger', array('statistics', $loggerStatsDef));
670
            $configDef->addMethodCall('setCacheLogger', array($loggerChaingDef));
671
672
            foreach ($entityManager['second_level_cache']['loggers'] as $name => $logger) {
673
                $loggerId = sprintf('doctrine.orm.%s_second_level_cache.logger.%s', $entityManager['name'], $name);
674
                $loggerRef = new Reference($logger['service']);
675
676
                $container->setAlias($loggerId, new Alias($logger['service'], false));
677
                $loggerChaingDef->addMethodCall('setLogger', array($name, $loggerRef));
678
            }
679
        }
680
681
        $configDef->addMethodCall('setCacheFactory', array($slcFactoryDef));
682
        $configDef->addMethodCall('setRegionsConfiguration', array($regionsDef));
683
        $ormConfigDef->addMethodCall('setSecondLevelCacheEnabled', array($enabled));
684
        $ormConfigDef->addMethodCall('setSecondLevelCacheConfiguration', array($configDef));
685
    }
686
687
    /**
688
     * {@inheritDoc}
689
     */
690
    protected function getObjectManagerElementName($name)
691
    {
692
        return 'doctrine.orm.'.$name;
693
    }
694
695
    protected function getMappingObjectDefaultName()
696
    {
697
        return 'Entity';
698
    }
699
700
    /**
701
     * {@inheritDoc}
702
     */
703
    protected function getMappingResourceConfigDirectory()
704
    {
705
        return 'Resources/config/doctrine';
706
    }
707
708
    /**
709
     * {@inheritDoc}
710
     */
711
    protected function getMappingResourceExtension()
712
    {
713
        return 'orm';
714
    }
715
716
    /**
717
     * {@inheritDoc}
718
     */
719
    protected function loadCacheDriver($driverName, $entityManagerName, array $driverMap, ContainerBuilder $container)
720
    {
721
        if (!empty($driverMap['cache_provider'])) {
722
            $aliasId = $this->getObjectManagerElementName(sprintf('%s_%s', $entityManagerName, $driverName));
723
            $serviceId = sprintf('doctrine_cache.providers.%s', $driverMap['cache_provider']);
724
725
            $container->setAlias($aliasId, new Alias($serviceId, false));
726
727
            return $aliasId;
728
        }
729
730
        return $this->adapter->loadCacheDriver($driverName, $entityManagerName, $driverMap, $container);
731
    }
732
733
    /**
734
     * Loads a configured entity managers cache drivers.
735
     *
736
     * @param array            $entityManager A configured ORM entity manager.
737
     * @param ContainerBuilder $container     A ContainerBuilder instance
738
     */
739
    protected function loadOrmCacheDrivers(array $entityManager, ContainerBuilder $container)
740
    {
741
        $this->loadCacheDriver('metadata_cache', $entityManager['name'], $entityManager['metadata_cache_driver'], $container);
742
        $this->loadCacheDriver('result_cache', $entityManager['name'], $entityManager['result_cache_driver'], $container);
743
        $this->loadCacheDriver('query_cache', $entityManager['name'], $entityManager['query_cache_driver'], $container);
744
    }
745
746
    /**
747
     * Loads a property info extractor for each defined entity manager.
748
     *
749
     * @param string           $entityManagerName
750
     * @param ContainerBuilder $container
751
     */
752
    private function loadPropertyInfoExtractor($entityManagerName, ContainerBuilder $container)
753
    {
754
        $metadataFactoryService = sprintf('doctrine.orm.%s_entity_manager.metadata_factory', $entityManagerName);
755
756
        $metadataFactoryDefinition = $container->register($metadataFactoryService, 'Doctrine\Common\Persistence\Mapping\ClassMetadataFactory');
757
        $metadataFactoryDefinition->setFactory(array(
758
            new Reference(sprintf('doctrine.orm.%s_entity_manager', $entityManagerName)),
759
            'getMetadataFactory'
760
        ));
761
        $metadataFactoryDefinition->setPublic(false);
762
763
        $propertyExtractorDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.property_info_extractor', $entityManagerName), 'Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor');
764
        $propertyExtractorDefinition->addArgument(new Reference($metadataFactoryService));
765
        $propertyExtractorDefinition->addTag('property_info.list_extractor', array('priority' => -1001));
766
        $propertyExtractorDefinition->addTag('property_info.type_extractor', array('priority' => -999));
767
    }
768
769
    /**
770
     * @param array            $objectManager
771
     * @param ContainerBuilder $container
772
     * @param string           $cacheName
773
     */
774
    public function loadObjectManagerCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName)
775
    {
776
        $this->loadCacheDriver($cacheName, $objectManager['name'], $objectManager[$cacheName.'_driver'], $container);
777
    }
778
779
    /**
780
     * {@inheritDoc}
781
     */
782
    public function getXsdValidationBasePath()
783
    {
784
        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...
785
    }
786
787
    /**
788
     * {@inheritDoc}
789
     */
790
    public function getNamespace()
791
    {
792
        return 'http://symfony.com/schema/dic/doctrine';
793
    }
794
795
    /**
796
     * {@inheritDoc}
797
     */
798
    public function getConfiguration(array $config, ContainerBuilder $container)
799
    {
800
        return new Configuration($container->getParameter('kernel.debug'));
801
    }
802
803
    /**
804
     * @return string
805
     */
806
    private function getDefinitionClassname(): string
807
    {
808
        return class_exists(ChildDefinition::class) ? ChildDefinition::class : DefinitionDecorator::class;
809
    }
810
}
811