Completed
Pull Request — master (#648)
by Robin
02:27
created

DoctrineExtension::ormLoad()   C

Complexity

Conditions 10
Paths 240

Size

Total Lines 55
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 55
rs 5.5
c 0
b 0
f 0
cc 10
eloc 32
nc 240
nop 2

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/*
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);
0 ignored issues
show
Bug introduced by
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 69 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...
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
            if (!class_exists('Doctrine\ORM\Version')) {
84
                throw new \LogicException('To configure the ORM layer, you must first install the doctrine/orm package.');
85
            }
86
87
            $this->ormLoad($config['orm'], $container);
88
        }
89
90
        if (PHP_VERSION_ID < 70000) {
91
            $this->addClassesToCompile(array(
92
                'Doctrine\\Common\\Annotations\\DocLexer',
93
                'Doctrine\\Common\\Annotations\\FileCacheReader',
94
                'Doctrine\\Common\\Annotations\\PhpParser',
95
                'Doctrine\\Common\\Annotations\\Reader',
96
                'Doctrine\\Common\\Lexer',
97
                'Doctrine\\Common\\Persistence\\ConnectionRegistry',
98
                'Doctrine\\Common\\Persistence\\Proxy',
99
                'Doctrine\\Common\\Util\\ClassUtils',
100
                'Doctrine\\Bundle\\DoctrineBundle\\Registry',
101
            ));
102
        }
103
    }
104
105
    /**
106
     * Loads the DBAL configuration.
107
     *
108
     * Usage example:
109
     *
110
     *      <doctrine:dbal id="myconn" dbname="sfweb" user="root" />
111
     *
112
     * @param array            $config    An array of configuration settings
113
     * @param ContainerBuilder $container A ContainerBuilder instance
114
     */
115
    protected function dbalLoad(array $config, ContainerBuilder $container)
116
    {
117
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
118
        $loader->load('dbal.xml');
119
120
        if (empty($config['default_connection'])) {
121
            $keys = array_keys($config['connections']);
122
            $config['default_connection'] = reset($keys);
123
        }
124
125
        $this->defaultConnection = $config['default_connection'];
126
127
        $container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection));
128
        $container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false));
129
130
        $container->setParameter('doctrine.dbal.connection_factory.types', $config['types']);
131
132
        $connections = array();
133
134
        foreach (array_keys($config['connections']) as $name) {
135
            $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name);
136
        }
137
138
        $container->setParameter('doctrine.connections', $connections);
139
        $container->setParameter('doctrine.default_connection', $this->defaultConnection);
140
141
        foreach ($config['connections'] as $name => $connection) {
142
            $this->loadDbalConnection($name, $connection, $container);
143
        }
144
    }
145
146
    /**
147
     * Loads a configured DBAL connection.
148
     *
149
     * @param string           $name       The name of the connection
150
     * @param array            $connection A dbal connection configuration.
151
     * @param ContainerBuilder $container  A ContainerBuilder instance
152
     */
153
    protected function loadDbalConnection($name, array $connection, ContainerBuilder $container)
154
    {
155
        // configuration
156
        $configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new DefinitionDecorator('doctrine.dbal.connection.configuration'));
157
        $logger = null;
158
        if ($connection['logging']) {
159
            $logger = new Reference('doctrine.dbal.logger');
160
        }
161
        unset ($connection['logging']);
162
        if ($connection['profiling']) {
163
            $profilingLoggerId = 'doctrine.dbal.logger.profiling.'.$name;
164
            $container->setDefinition($profilingLoggerId, new DefinitionDecorator('doctrine.dbal.logger.profiling'));
165
            $profilingLogger = new Reference($profilingLoggerId);
166
            $container->getDefinition('data_collector.doctrine')->addMethodCall('addLogger', array($name, $profilingLogger));
167
168
            if (null !== $logger) {
169
                $chainLogger = new DefinitionDecorator('doctrine.dbal.logger.chain');
170
                $chainLogger->addMethodCall('addLogger', array($profilingLogger));
171
172
                $loggerId = 'doctrine.dbal.logger.chain.'.$name;
173
                $container->setDefinition($loggerId, $chainLogger);
174
                $logger = new Reference($loggerId);
175
            } else {
176
                $logger = $profilingLogger;
177
            }
178
        }
179
        unset($connection['profiling']);
180
181
        if (isset($connection['auto_commit'])) {
182
            $configuration->addMethodCall('setAutoCommit', array($connection['auto_commit']));
183
        }
184
185
        unset($connection['auto_commit']);
186
187
        if (isset($connection['schema_filter']) && $connection['schema_filter']) {
188
            $configuration->addMethodCall('setFilterSchemaAssetsExpression', array($connection['schema_filter']));
189
        }
190
191
        unset($connection['schema_filter']);
192
193
        if ($logger) {
194
            $configuration->addMethodCall('setSQLLogger', array($logger));
195
        }
196
197
        // event manager
198
        $container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new DefinitionDecorator('doctrine.dbal.connection.event_manager'));
199
200
        // connection
201
        // PDO ignores the charset property before 5.3.6 so the init listener has to be used instead.
202
        if (isset($connection['charset']) && version_compare(PHP_VERSION, '5.3.6', '<')) {
203
            if ((isset($connection['driver']) && stripos($connection['driver'], 'mysql') !== false) ||
204
                 (isset($connection['driver_class']) && stripos($connection['driver_class'], 'mysql') !== false)) {
205
                $mysqlSessionInit = new Definition('%doctrine.dbal.events.mysql_session_init.class%');
206
                $mysqlSessionInit->setArguments(array($connection['charset']));
207
                $mysqlSessionInit->setPublic(false);
208
                $mysqlSessionInit->addTag('doctrine.event_subscriber', array('connection' => $name));
209
210
                $container->setDefinition(
211
                    sprintf('doctrine.dbal.%s_connection.events.mysqlsessioninit', $name),
212
                    $mysqlSessionInit
213
                );
214
                unset($connection['charset']);
215
            }
216
        }
217
218
        $options = $this->getConnectionOptions($connection);
219
220
        $def = $container
221
            ->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new DefinitionDecorator('doctrine.dbal.connection'))
222
            ->setArguments(array(
223
                $options,
224
                new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)),
225
                new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)),
226
                $connection['mapping_types'],
227
            ))
228
        ;
229
230
        if (!empty($connection['use_savepoints'])) {
231
            $def->addMethodCall('setNestTransactionsWithSavepoints', array($connection['use_savepoints']));
232
        }
233
234
        // Create a shard_manager for this connection
235
        if (isset($options['shards'])) {
236
            $shardManagerDefinition = new Definition($options['shardManagerClass'], array(
237
                new Reference(sprintf('doctrine.dbal.%s_connection', $name))
238
            ));
239
            $container->setDefinition(sprintf('doctrine.dbal.%s_shard_manager', $name), $shardManagerDefinition);
240
        }
241
    }
242
243
    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...
244
    {
245
        $options = $connection;
246
247
        if (isset($options['platform_service'])) {
248
            $options['platform'] = new Reference($options['platform_service']);
249
            unset($options['platform_service']);
250
        }
251
        unset($options['mapping_types']);
252
253
        if (isset($options['shard_choser_service'])) {
254
            $options['shard_choser'] = new Reference($options['shard_choser_service']);
255
            unset($options['shard_choser_service']);
256
        }
257
258
        foreach (array(
259
            'options' => 'driverOptions',
260
            'driver_class' => 'driverClass',
261
            'wrapper_class' => 'wrapperClass',
262
            'keep_slave' => 'keepSlave',
263
            'shard_choser' => 'shardChoser',
264
            'shard_manager_class' => 'shardManagerClass',
265
            'server_version' => 'serverVersion',
266
            'default_table_options' => 'defaultTableOptions',
267
        ) as $old => $new) {
268
            if (isset($options[$old])) {
269
                $options[$new] = $options[$old];
270
                unset($options[$old]);
271
            }
272
        }
273
274
        if (!empty($options['slaves']) && !empty($options['shards'])) {
275
            throw new InvalidArgumentException('Sharding and master-slave connection cannot be used together');
276
        }
277
278
        if (!empty($options['slaves'])) {
279
            $nonRewrittenKeys = array(
280
                'driver' => true, 'driverOptions' => true, 'driverClass' => true,
281
                'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true,
282
                'platform' => true, 'slaves' => true, 'master' => true, 'shards' => true,
283
                'serverVersion' => true,
284
                // included by safety but should have been unset already
285
                'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true,
286
            );
287
            foreach ($options as $key => $value) {
288
                if (isset($nonRewrittenKeys[$key])) {
289
                    continue;
290
                }
291
                $options['master'][$key] = $value;
292
                unset($options[$key]);
293
            }
294
            if (empty($options['wrapperClass'])) {
295
                // Change the wrapper class only if the user does not already forced using a custom one.
296
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Connections\\MasterSlaveConnection';
297
            }
298
        } else {
299
            unset($options['slaves']);
300
        }
301
302
        if (!empty($options['shards'])) {
303
            $nonRewrittenKeys = array(
304
                'driver' => true, 'driverOptions' => true, 'driverClass' => true,
305
                'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true,
306
                'platform' => true, 'slaves' => true, 'global' => true, 'shards' => true,
307
                'serverVersion' => true,
308
                // included by safety but should have been unset already
309
                'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true,
310
            );
311
            foreach ($options as $key => $value) {
312
                if (isset($nonRewrittenKeys[$key])) {
313
                    continue;
314
                }
315
                $options['global'][$key] = $value;
316
                unset($options[$key]);
317
            }
318
            if (empty($options['wrapperClass'])) {
319
                // Change the wrapper class only if the user does not already forced using a custom one.
320
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardConnection';
321
            }
322
            if (empty($options['shardManagerClass'])) {
323
                // Change the shard manager class only if the user does not already forced using a custom one.
324
                $options['shardManagerClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardManager';
325
            }
326
        } else {
327
            unset($options['shards']);
328
        }
329
330
        return $options;
331
    }
332
333
    /**
334
     * Loads the Doctrine ORM configuration.
335
     *
336
     * Usage example:
337
     *
338
     *     <doctrine:orm id="mydm" connection="myconn" />
339
     *
340
     * @param array            $config    An array of configuration settings
341
     * @param ContainerBuilder $container A ContainerBuilder instance
342
     */
343
    protected function ormLoad(array $config, ContainerBuilder $container)
344
    {
345
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
346
        $loader->load('orm.xml');
347
348
        $this->entityManagers = array();
349
        foreach (array_keys($config['entity_managers']) as $name) {
350
            $this->entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name);
351
        }
352
        $container->setParameter('doctrine.entity_managers', $this->entityManagers);
353
354
        if (empty($config['default_entity_manager'])) {
355
            $tmp = array_keys($this->entityManagers);
356
            $config['default_entity_manager'] = reset($tmp);
357
        }
358
        $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']);
359
360
        $options = array('auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace');
361
        foreach ($options as $key) {
362
            $container->setParameter('doctrine.orm.'.$key, $config[$key]);
363
        }
364
365
        $container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager']));
366
367
        $config['entity_managers'] = $this->fixManagersAutoMappings($config['entity_managers'], $container->getParameter('kernel.bundles'));
368
369
        $loadPropertyInfoExtractor = interface_exists('Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface')
370
            && class_exists('Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor');
371
372
        foreach ($config['entity_managers'] as $name => $entityManager) {
373
            $entityManager['name'] = $name;
374
            $this->loadOrmEntityManager($entityManager, $container);
375
376
            if ($loadPropertyInfoExtractor) {
377
                $this->loadPropertyInfoExtractor($name, $container);
378
            }
379
        }
380
381
        if ($config['resolve_target_entities']) {
382
            $def = $container->findDefinition('doctrine.orm.listeners.resolve_target_entity');
383
            foreach ($config['resolve_target_entities'] as $name => $implementation) {
384
                $def->addMethodCall('addResolveTargetEntity', array(
385
                    $name, $implementation, array(),
386
                ));
387
            }
388
389
            // BC: ResolveTargetEntityListener implements the subscriber interface since
390
            // 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...
391
            if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) {
392
                $def->addTag('doctrine.event_listener', array('event' => 'loadClassMetadata'));
393
            } else {
394
                $def->addTag('doctrine.event_subscriber');
395
            }
396
        }
397
    }
398
399
    /**
400
     * Loads a configured ORM entity manager.
401
     *
402
     * @param array            $entityManager A configured ORM entity manager.
403
     * @param ContainerBuilder $container     A ContainerBuilder instance
404
     */
405
    protected function loadOrmEntityManager(array $entityManager, ContainerBuilder $container)
406
    {
407
        $ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new DefinitionDecorator('doctrine.orm.configuration'));
408
409
        $this->loadOrmEntityManagerMappingInformation($entityManager, $ormConfigDef, $container);
410
        $this->loadOrmCacheDrivers($entityManager, $container);
411
412
        if (isset($entityManager['entity_listener_resolver']) && $entityManager['entity_listener_resolver']) {
413
            $container->setAlias(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $entityManager['entity_listener_resolver']);
414
        } else {
415
            $definition = new Definition('%doctrine.orm.entity_listener_resolver.class%');
416
            $definition->addArgument(new Reference('service_container'));
417
            $container->setDefinition(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $definition);
418
        }
419
420
        $methods = array(
421
            'setMetadataCacheImpl' => new Reference(sprintf('doctrine.orm.%s_metadata_cache', $entityManager['name'])),
422
            'setQueryCacheImpl' => new Reference(sprintf('doctrine.orm.%s_query_cache', $entityManager['name'])),
423
            'setResultCacheImpl' => new Reference(sprintf('doctrine.orm.%s_result_cache', $entityManager['name'])),
424
            'setMetadataDriverImpl' => new Reference('doctrine.orm.'.$entityManager['name'].'_metadata_driver'),
425
            'setProxyDir' => '%doctrine.orm.proxy_dir%',
426
            'setProxyNamespace' => '%doctrine.orm.proxy_namespace%',
427
            'setAutoGenerateProxyClasses' => '%doctrine.orm.auto_generate_proxy_classes%',
428
            'setClassMetadataFactoryName' => $entityManager['class_metadata_factory_name'],
429
            'setDefaultRepositoryClassName' => $entityManager['default_repository_class'],
430
        );
431
        // check for version to keep BC
432
        if (version_compare(Version::VERSION, "2.3.0-DEV") >= 0) {
433
            $methods = array_merge($methods, array(
434
                'setNamingStrategy' => new Reference($entityManager['naming_strategy']),
435
                'setQuoteStrategy' => new Reference($entityManager['quote_strategy']),
436
            ));
437
        }
438
439
        if (version_compare(Version::VERSION, "2.4.0-DEV") >= 0) {
440
            $methods = array_merge($methods, array(
441
                'setEntityListenerResolver' => new Reference(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name'])),
442
            ));
443
        }
444
445
        if (version_compare(Version::VERSION, "2.5.0-DEV") >= 0) {
446
            $listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $entityManager['name']);
447
            $listenerDef = $container->setDefinition($listenerId, new Definition('%doctrine.orm.listeners.attach_entity_listeners.class%'));
448
            $listenerTagParams = array('event' => 'loadClassMetadata');
449
            if (isset($entityManager['connection'])) {
450
                $listenerTagParams['connection'] = $entityManager['connection'];
451
            }
452
            $listenerDef->addTag('doctrine.event_listener', $listenerTagParams);
453
        }
454
455
        if (isset($entityManager['second_level_cache'])) {
456
            $this->loadOrmSecondLevelCache($entityManager, $ormConfigDef, $container);
457
        }
458
459
        if ($entityManager['repository_factory']) {
460
            $methods['setRepositoryFactory'] = new Reference($entityManager['repository_factory']);
461
        }
462
463
        foreach ($methods as $method => $arg) {
464
            $ormConfigDef->addMethodCall($method, array($arg));
465
        }
466
467
        foreach ($entityManager['hydrators'] as $name => $class) {
468
            $ormConfigDef->addMethodCall('addCustomHydrationMode', array($name, $class));
469
        }
470
471
        if (!empty($entityManager['dql'])) {
472
            foreach ($entityManager['dql']['string_functions'] as $name => $function) {
473
                $ormConfigDef->addMethodCall('addCustomStringFunction', array($name, $function));
474
            }
475
            foreach ($entityManager['dql']['numeric_functions'] as $name => $function) {
476
                $ormConfigDef->addMethodCall('addCustomNumericFunction', array($name, $function));
477
            }
478
            foreach ($entityManager['dql']['datetime_functions'] as $name => $function) {
479
                $ormConfigDef->addMethodCall('addCustomDatetimeFunction', array($name, $function));
480
            }
481
        }
482
483
        $enabledFilters = array();
484
        $filtersParameters = array();
485
        foreach ($entityManager['filters'] as $name => $filter) {
486
            $ormConfigDef->addMethodCall('addFilter', array($name, $filter['class']));
487
            if ($filter['enabled']) {
488
                $enabledFilters[] = $name;
489
            }
490
            if ($filter['parameters']) {
491
                $filtersParameters[$name] = $filter['parameters'];
492
            }
493
        }
494
495
        $managerConfiguratorName = sprintf('doctrine.orm.%s_manager_configurator', $entityManager['name']);
496
        $container
497
            ->setDefinition($managerConfiguratorName, new DefinitionDecorator('doctrine.orm.manager_configurator.abstract'))
498
            ->replaceArgument(0, $enabledFilters)
499
            ->replaceArgument(1, $filtersParameters)
500
        ;
501
502
        if (!isset($entityManager['connection'])) {
503
            $entityManager['connection'] = $this->defaultConnection;
504
        }
505
506
        $container
507
            ->setDefinition(sprintf('doctrine.orm.%s_entity_manager', $entityManager['name']), new DefinitionDecorator('doctrine.orm.entity_manager.abstract'))
508
            ->setArguments(array(
509
                new Reference(sprintf('doctrine.dbal.%s_connection', $entityManager['connection'])),
510
                new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])),
511
            ))
512
            ->setConfigurator(array(new Reference($managerConfiguratorName), 'configure'))
513
        ;
514
515
        $container->setAlias(
516
            sprintf('doctrine.orm.%s_entity_manager.event_manager', $entityManager['name']),
517
            new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $entityManager['connection']), false)
518
        );
519
520
        if (isset($entityManager['entity_listeners'])) {
521
            if (!isset($listenerDef)) {
522
                throw new InvalidArgumentException('Entity listeners configuration requires doctrine-orm 2.5.0 or newer');
523
            }
524
525
            $entities = $entityManager['entity_listeners']['entities'];
526
527
            foreach ($entities as $entityListenerClass => $entity) {
528
                foreach ($entity['listeners'] as $listenerClass => $listener) {
529
                    foreach ($listener['events'] as $listenerEvent) {
530
                        $listenerEventName = $listenerEvent['type'];
531
                        $listenerMethod = $listenerEvent['method'];
532
533
                        $listenerDef->addMethodCall('addEntityListener', array(
534
                            $entityListenerClass, $listenerClass, $listenerEventName, $listenerMethod,
535
                        ));
536
                    }
537
                }
538
            }
539
540
        }
541
    }
542
543
    /**
544
     * Loads an ORM entity managers bundle mapping information.
545
     *
546
     * There are two distinct configuration possibilities for mapping information:
547
     *
548
     * 1. Specify a bundle and optionally details where the entity and mapping information reside.
549
     * 2. Specify an arbitrary mapping location.
550
     *
551
     * @example
552
     *
553
     *  doctrine.orm:
554
     *     mappings:
555
     *         MyBundle1: ~
556
     *         MyBundle2: yml
557
     *         MyBundle3: { type: annotation, dir: Entities/ }
558
     *         MyBundle4: { type: xml, dir: Resources/config/doctrine/mapping }
559
     *         MyBundle5:
560
     *             type: yml
561
     *             dir: bundle-mappings/
562
     *             alias: BundleAlias
563
     *         arbitrary_key:
564
     *             type: xml
565
     *             dir: %kernel.root_dir%/../src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entities
566
     *             prefix: DoctrineExtensions\Entities\
567
     *             alias: DExt
568
     *
569
     * In the case of bundles everything is really optional (which leads to autodetection for this bundle) but
570
     * in the mappings key everything except alias is a required argument.
571
     *
572
     * @param array            $entityManager A configured ORM entity manager
573
     * @param Definition       $ormConfigDef  A Definition instance
574
     * @param ContainerBuilder $container     A ContainerBuilder instance
575
     */
576
    protected function loadOrmEntityManagerMappingInformation(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
577
    {
578
        // reset state of drivers and alias map. They are only used by this methods and children.
579
        $this->drivers = array();
580
        $this->aliasMap = array();
581
582
        $this->loadMappingInformation($entityManager, $container);
583
        $this->registerMappingDrivers($entityManager, $container);
584
585
        $ormConfigDef->addMethodCall('setEntityNamespaces', array($this->aliasMap));
586
    }
587
588
    /**
589
     * Loads an ORM second level cache bundle mapping information.
590
     *
591
     * @example
592
     *  entity_managers:
593
     *      default:
594
     *          second_level_cache:
595
     *              region_cache_driver: apc
596
     *              log_enabled: true
597
     *              regions:
598
     *                  my_service_region:
599
     *                      type: service
600
     *                      service : "my_service_region"
601
     *
602
     *                  my_query_region:
603
     *                      lifetime: 300
604
     *                      cache_driver: array
605
     *                      type: filelock
606
     *
607
     *                  my_entity_region:
608
     *                      lifetime: 600
609
     *                      cache_driver:
610
     *                          type: apc
611
     *
612
     * @param array            $entityManager A configured ORM entity manager
613
     * @param Definition       $ormConfigDef  A Definition instance
614
     * @param ContainerBuilder $container     A ContainerBuilder instance
615
     */
616
    protected function loadOrmSecondLevelCache(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
617
    {
618
        if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) {
619
            throw new \InvalidArgumentException('Second-level cache requires doctrine-orm 2.5.0 or newer');
620
        }
621
622
        $driverId = null;
623
        $enabled = $entityManager['second_level_cache']['enabled'];
624
625
        if (isset($entityManager['second_level_cache']['region_cache_driver'])) {
626
            $driverName = 'second_level_cache.region_cache_driver';
627
            $driverMap = $entityManager['second_level_cache']['region_cache_driver'];
628
            $driverId = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
629
        }
630
631
        $configId = sprintf('doctrine.orm.%s_second_level_cache.cache_configuration', $entityManager['name']);
632
        $regionsId = sprintf('doctrine.orm.%s_second_level_cache.regions_configuration', $entityManager['name']);
633
        $driverId = $driverId ?: sprintf('doctrine.orm.%s_second_level_cache.region_cache_driver', $entityManager['name']);
634
        $configDef = $container->setDefinition($configId, new Definition('%doctrine.orm.second_level_cache.cache_configuration.class%'));
635
        $regionsDef = $container->setDefinition($regionsId, new Definition('%doctrine.orm.second_level_cache.regions_configuration.class%'));
636
637
        $slcFactoryId = sprintf('doctrine.orm.%s_second_level_cache.default_cache_factory', $entityManager['name']);
638
        $factoryClass = isset($entityManager['second_level_cache']['factory']) ? $entityManager['second_level_cache']['factory'] : '%doctrine.orm.second_level_cache.default_cache_factory.class%';
639
640
        $definition = new Definition($factoryClass, array(new Reference($regionsId), new Reference($driverId)));
641
642
        $slcFactoryDef = $container
643
            ->setDefinition($slcFactoryId, $definition);
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