Completed
Push — master ( 56db77...fd5190 )
by Mike
06:09
created

DoctrineExtension   F

Complexity

Total Complexity 104

Size/Duplication

Total Lines 786
Duplicated Lines 5.73 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

Changes 19
Bugs 7 Features 5
Metric Value
wmc 104
c 19
b 7
f 5
lcom 1
cbo 12
dl 45
loc 786
rs 3.4285

20 Methods

Rating   Name   Duplication   Size   Complexity  
F loadOrmEntityManager() 0 131 23
A getObjectManagerElementName() 0 4 1
A getMappingResourceConfigDirectory() 0 4 1
A getMappingResourceExtension() 0 4 1
A getXsdValidationBasePath() 0 4 1
A getNamespace() 0 4 1
A getConfiguration() 0 4 1
A __construct() 0 4 2
B load() 0 31 4
B dbalLoad() 0 40 5
F loadDbalConnection() 0 81 15
D getConnectionOptions() 45 83 15
F ormLoad() 0 74 15
A loadOrmEntityManagerMappingInformation() 0 11 1
C loadOrmSecondLevelCache() 0 89 12
A getMappingObjectDefaultName() 0 4 1
A loadCacheDriver() 0 13 2
A loadOrmCacheDrivers() 0 6 1
A loadPropertyInfoExtractor() 0 16 1
A loadObjectManagerCacheDriver() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like DoctrineExtension often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DoctrineExtension, and based on these observations, apply Extract Interface, too.

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\Reference;
25
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
26
use Symfony\Component\Config\FileLocator;
27
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\SymfonyBridgeAdapter;
28
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader;
29
30
/**
31
 * DoctrineExtension is an extension for the Doctrine DBAL and ORM library.
32
 *
33
 * @author Jonathan H. Wage <[email protected]>
34
 * @author Fabien Potencier <[email protected]>
35
 * @author Benjamin Eberlei <[email protected]>
36
 * @author Fabio B. Silva <[email protected]>
37
 * @author Kinn Coelho Julião <[email protected]>
38
 */
39
class DoctrineExtension extends AbstractDoctrineExtension
40
{
41
    /**
42
     * @var string
43
     */
44
    private $defaultConnection;
45
46
    /**
47
     * @var array
48
     */
49
    private $entityManagers;
50
51
    /**
52
     * @var SymfonyBridgeAdapter
53
     */
54
    private $adapter;
55
56
    /**
57
     * @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...
58
     */
59
    public function __construct(SymfonyBridgeAdapter $adapter = null)
60
    {
61
        $this->adapter = $adapter ?: new SymfonyBridgeAdapter(new CacheProviderLoader(), 'doctrine.orm', 'orm');
62
    }
63
64
    /**
65
     * {@inheritDoc}
66
     */
67
    public function load(array $configs, ContainerBuilder $container)
68
    {
69
        $configuration = $this->getConfiguration($configs, $container);
70
        $config = $this->processConfiguration($configuration, $configs);
71
72
        $this->adapter->loadServicesConfiguration($container);
73
74
        if (!empty($config['dbal'])) {
75
            $this->dbalLoad($config['dbal'], $container);
76
        }
77
78
        if (!empty($config['orm'])) {
79
            if (empty($config['dbal'])) {
80
                throw new \LogicException('Configuring the ORM layer requires to configure the DBAL layer as well.');
81
            }
82
83
            $this->ormLoad($config['orm'], $container);
84
        }
85
86
        $this->addClassesToCompile(array(
87
            'Doctrine\\Common\\Annotations\\DocLexer',
88
            'Doctrine\\Common\\Annotations\\FileCacheReader',
89
            'Doctrine\\Common\\Annotations\\PhpParser',
90
            'Doctrine\\Common\\Annotations\\Reader',
91
            'Doctrine\\Common\\Lexer',
92
            'Doctrine\\Common\\Persistence\\ConnectionRegistry',
93
            'Doctrine\\Common\\Persistence\\Proxy',
94
            'Doctrine\\Common\\Util\\ClassUtils',
95
            'Doctrine\\Bundle\\DoctrineBundle\\Registry',
96
        ));
97
    }
98
99
    /**
100
     * Loads the DBAL configuration.
101
     *
102
     * Usage example:
103
     *
104
     *      <doctrine:dbal id="myconn" dbname="sfweb" user="root" />
105
     *
106
     * @param array            $config    An array of configuration settings
107
     * @param ContainerBuilder $container A ContainerBuilder instance
108
     */
109
    protected function dbalLoad(array $config, ContainerBuilder $container)
110
    {
111
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
112
        $loader->load('dbal.xml');
113
114
        if (empty($config['default_connection'])) {
115
            $keys = array_keys($config['connections']);
116
            $config['default_connection'] = reset($keys);
117
        }
118
119
        $this->defaultConnection = $config['default_connection'];
120
121
        $container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection));
122
        $container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false));
123
124
        $container->setParameter('doctrine.dbal.connection_factory.types', $config['types']);
125
126
        $connections = array();
127
128
        foreach (array_keys($config['connections']) as $name) {
129
            $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name);
130
        }
131
132
        $container->setParameter('doctrine.connections', $connections);
133
        $container->setParameter('doctrine.default_connection', $this->defaultConnection);
134
135
        $def = $container->getDefinition('doctrine.dbal.connection');
136
        if (method_exists($def, 'setFactory')) {
137
            // to be inlined in dbal.xml when dependency on Symfony DependencyInjection is bumped to 2.6
138
            $def->setFactory(array(new Reference('doctrine.dbal.connection_factory'), 'createConnection'));
139
        } else {
140
            // to be removed when dependency on Symfony DependencyInjection is bumped to 2.6
141
            $def->setFactoryService('doctrine.dbal.connection_factory');
0 ignored issues
show
Bug introduced by
The method setFactoryService() does not exist on Symfony\Component\DependencyInjection\Definition. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
142
            $def->setFactoryMethod('createConnection');
0 ignored issues
show
Bug introduced by
The method setFactoryMethod() does not exist on Symfony\Component\DependencyInjection\Definition. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
143
        }
144
145
        foreach ($config['connections'] as $name => $connection) {
146
            $this->loadDbalConnection($name, $connection, $container);
147
        }
148
    }
149
150
    /**
151
     * Loads a configured DBAL connection.
152
     *
153
     * @param string           $name       The name of the connection
154
     * @param array            $connection A dbal connection configuration.
155
     * @param ContainerBuilder $container  A ContainerBuilder instance
156
     */
157
    protected function loadDbalConnection($name, array $connection, ContainerBuilder $container)
158
    {
159
        // configuration
160
        $configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new DefinitionDecorator('doctrine.dbal.connection.configuration'));
161
        $logger = null;
162
        if ($connection['logging']) {
163
            $logger = new Reference('doctrine.dbal.logger');
164
        }
165
        unset ($connection['logging']);
166
        if ($connection['profiling']) {
167
            $profilingLoggerId = 'doctrine.dbal.logger.profiling.'.$name;
168
            $container->setDefinition($profilingLoggerId, new DefinitionDecorator('doctrine.dbal.logger.profiling'));
169
            $profilingLogger = new Reference($profilingLoggerId);
170
            $container->getDefinition('data_collector.doctrine')->addMethodCall('addLogger', array($name, $profilingLogger));
171
172
            if (null !== $logger) {
173
                $chainLogger = new DefinitionDecorator('doctrine.dbal.logger.chain');
174
                $chainLogger->addMethodCall('addLogger', array($profilingLogger));
175
176
                $loggerId = 'doctrine.dbal.logger.chain.'.$name;
177
                $container->setDefinition($loggerId, $chainLogger);
178
                $logger = new Reference($loggerId);
179
            } else {
180
                $logger = $profilingLogger;
181
            }
182
        }
183
        unset($connection['profiling']);
184
185
        if (isset($connection['auto_commit'])) {
186
            $configuration->addMethodCall('setAutoCommit', array($connection['auto_commit']));
187
        }
188
189
        unset($connection['auto_commit']);
190
191
        if (isset($connection['schema_filter']) && $connection['schema_filter']) {
192
            $configuration->addMethodCall('setFilterSchemaAssetsExpression', array($connection['schema_filter']));
193
        }
194
195
        unset($connection['schema_filter']);
196
197
        if ($logger) {
198
            $configuration->addMethodCall('setSQLLogger', array($logger));
199
        }
200
201
        // event manager
202
        $container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new DefinitionDecorator('doctrine.dbal.connection.event_manager'));
203
204
        // connection
205
        // PDO ignores the charset property before 5.3.6 so the init listener has to be used instead.
206
        if (isset($connection['charset']) && version_compare(PHP_VERSION, '5.3.6', '<')) {
207
            if ((isset($connection['driver']) && stripos($connection['driver'], 'mysql') !== false) ||
208
                 (isset($connection['driver_class']) && stripos($connection['driver_class'], 'mysql') !== false)) {
209
                $mysqlSessionInit = new Definition('%doctrine.dbal.events.mysql_session_init.class%');
210
                $mysqlSessionInit->setArguments(array($connection['charset']));
211
                $mysqlSessionInit->setPublic(false);
212
                $mysqlSessionInit->addTag('doctrine.event_subscriber', array('connection' => $name));
213
214
                $container->setDefinition(
215
                    sprintf('doctrine.dbal.%s_connection.events.mysqlsessioninit', $name),
216
                    $mysqlSessionInit
217
                );
218
                unset($connection['charset']);
219
            }
220
        }
221
222
        $options = $this->getConnectionOptions($connection);
223
224
        $def = $container
225
            ->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new DefinitionDecorator('doctrine.dbal.connection'))
226
            ->setArguments(array(
227
                $options,
228
                new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)),
229
                new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)),
230
                $connection['mapping_types'],
231
            ))
232
        ;
233
234
        if (!empty($connection['use_savepoints'])) {
235
            $def->addMethodCall('setNestTransactionsWithSavepoints', array($connection['use_savepoints']));
236
        }
237
    }
238
239
    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...
240
    {
241
        $options = $connection;
242
243
        if (isset($options['platform_service'])) {
244
            $options['platform'] = new Reference($options['platform_service']);
245
            unset($options['platform_service']);
246
        }
247
        unset($options['mapping_types']);
248
249
        if (isset($options['shard_choser_service'])) {
250
            $options['shard_choser'] = new Reference($options['shard_choser_service']);
251
            unset($options['shard_choser_service']);
252
        }
253
254
        foreach (array(
255
            'options' => 'driverOptions',
256
            'driver_class' => 'driverClass',
257
            'wrapper_class' => 'wrapperClass',
258
            'keep_slave' => 'keepSlave',
259
            'shard_choser' => 'shardChoser',
260
            'server_version' => 'serverVersion',
261
            'default_table_options' => 'defaultTableOptions',
262
        ) as $old => $new) {
263
            if (isset($options[$old])) {
264
                $options[$new] = $options[$old];
265
                unset($options[$old]);
266
            }
267
        }
268
269
        if (!empty($options['slaves']) && !empty($options['shards'])) {
270
            throw new InvalidArgumentException('Sharding and master-slave connection cannot be used together');
271
        }
272
273 View Code Duplication
        if (!empty($options['slaves'])) {
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...
274
            $nonRewrittenKeys = array(
275
                'driver' => true, 'driverOptions' => true, 'driverClass' => true,
276
                'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true,
277
                'platform' => true, 'slaves' => true, 'master' => true, 'shards' => true,
278
                'serverVersion' => true,
279
                // included by safety but should have been unset already
280
                'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true,
281
            );
282
            foreach ($options as $key => $value) {
283
                if (isset($nonRewrittenKeys[$key])) {
284
                    continue;
285
                }
286
                $options['master'][$key] = $value;
287
                unset($options[$key]);
288
            }
289
            if (empty($options['wrapperClass'])) {
290
                // Change the wrapper class only if the user does not already forced using a custom one.
291
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Connections\\MasterSlaveConnection';
292
            }
293
        } else {
294
            unset($options['slaves']);
295
        }
296
297 View Code Duplication
        if (!empty($options['shards'])) {
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...
298
            $nonRewrittenKeys = array(
299
                'driver' => true, 'driverOptions' => true, 'driverClass' => true,
300
                'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true,
301
                'platform' => true, 'slaves' => true, 'global' => true, 'shards' => true,
302
                // included by safety but should have been unset already
303
                'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true,
304
            );
305
            foreach ($options as $key => $value) {
306
                if (isset($nonRewrittenKeys[$key])) {
307
                    continue;
308
                }
309
                $options['global'][$key] = $value;
310
                unset($options[$key]);
311
            }
312
            if (empty($options['wrapperClass'])) {
313
                // Change the wrapper class only if the user does not already forced using a custom one.
314
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardConnection';
315
            }
316
        } else {
317
            unset($options['shards']);
318
        }
319
320
        return $options;
321
    }
322
323
    /**
324
     * Loads the Doctrine ORM configuration.
325
     *
326
     * Usage example:
327
     *
328
     *     <doctrine:orm id="mydm" connection="myconn" />
329
     *
330
     * @param array            $config    An array of configuration settings
331
     * @param ContainerBuilder $container A ContainerBuilder instance
332
     */
333
    protected function ormLoad(array $config, ContainerBuilder $container)
334
    {
335
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
336
        $loader->load('orm.xml');
337
338
        $this->entityManagers = array();
339
        foreach (array_keys($config['entity_managers']) as $name) {
340
            $this->entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name);
341
        }
342
        $container->setParameter('doctrine.entity_managers', $this->entityManagers);
343
344
        if (empty($config['default_entity_manager'])) {
345
            $tmp = array_keys($this->entityManagers);
346
            $config['default_entity_manager'] = reset($tmp);
347
        }
348
        $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']);
349
350
        $options = array('auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace');
351
        foreach ($options as $key) {
352
            $container->setParameter('doctrine.orm.'.$key, $config[$key]);
353
        }
354
355
        $container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager']));
356
357
        // BC logic to handle DoctrineBridge < 2.6
358
        if (!method_exists($this, 'fixManagersAutoMappings')) {
359
            foreach ($config['entity_managers'] as $entityManager) {
360
                if ($entityManager['auto_mapping'] && count($config['entity_managers']) > 1) {
361
                    throw new \LogicException('You cannot enable "auto_mapping" when several entity managers are defined.');
362
                }
363
            }
364
        } else {
365
            $config['entity_managers'] = $this->fixManagersAutoMappings($config['entity_managers'], $container->getParameter('kernel.bundles'));
366
        }
367
368
        $def = $container->getDefinition('doctrine.orm.entity_manager.abstract');
369
        if (method_exists($def, 'setFactory')) {
370
            // to be inlined in dbal.xml when dependency on Symfony DependencyInjection is bumped to 2.6
371
            $def->setFactory(array('%doctrine.orm.entity_manager.class%', 'create'));
372
        } else {
373
            // to be removed when dependency on Symfony DependencyInjection is bumped to 2.6
374
            $def->setFactoryClass('%doctrine.orm.entity_manager.class%');
0 ignored issues
show
Bug introduced by
The method setFactoryClass() does not exist on Symfony\Component\DependencyInjection\Definition. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
375
            $def->setFactoryMethod('create');
0 ignored issues
show
Bug introduced by
The method setFactoryMethod() does not exist on Symfony\Component\DependencyInjection\Definition. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

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