Completed
Push — master ( dba66b...b8eb92 )
by Fabien
03:50
created

DoctrineExtension::ormLoad()   F

Complexity

Conditions 14
Paths 1152

Size

Total Lines 79
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 79
rs 2.1004
c 0
b 0
f 0
cc 14
eloc 41
nc 1152
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\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
18
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface;
19
use Doctrine\ORM\Version;
20
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
21
use Symfony\Component\DependencyInjection\Alias;
22
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
23
use Symfony\Component\DependencyInjection\ContainerBuilder;
24
use Symfony\Component\DependencyInjection\Definition;
25
use Symfony\Component\DependencyInjection\DefinitionDecorator;
26
use Symfony\Component\DependencyInjection\ChildDefinition;
27
use Symfony\Component\DependencyInjection\Reference;
28
use Symfony\Component\DependencyInjection\ServiceLocator;
29
use Symfony\Component\Form\AbstractType;
30
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
31
use Symfony\Bridge\Doctrine\Form\Type\DoctrineType;
32
use Symfony\Component\Config\FileLocator;
33
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\SymfonyBridgeAdapter;
34
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader;
35
36
/**
37
 * DoctrineExtension is an extension for the Doctrine DBAL and ORM library.
38
 *
39
 * @author Jonathan H. Wage <[email protected]>
40
 * @author Fabien Potencier <[email protected]>
41
 * @author Benjamin Eberlei <[email protected]>
42
 * @author Fabio B. Silva <[email protected]>
43
 * @author Kinn Coelho Julião <[email protected]>
44
 */
45
class DoctrineExtension extends AbstractDoctrineExtension
46
{
47
    /**
48
     * @var string
49
     */
50
    private $defaultConnection;
51
52
    /**
53
     * @var SymfonyBridgeAdapter
54
     */
55
    private $adapter;
56
57
    /**
58
     * @param SymfonyBridgeAdapter $adapter
0 ignored issues
show
Documentation introduced by
Should the type for parameter $adapter not be null|SymfonyBridgeAdapter?

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

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

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

Loading history...
59
     */
60
    public function __construct(SymfonyBridgeAdapter $adapter = null)
61
    {
62
        $this->adapter = $adapter ?: new SymfonyBridgeAdapter(new CacheProviderLoader(), 'doctrine.orm', 'orm');
63
    }
64
65
    /**
66
     * {@inheritDoc}
67
     */
68
    public function load(array $configs, ContainerBuilder $container)
69
    {
70
        $configuration = $this->getConfiguration($configs, $container);
71
        $config = $this->processConfiguration($configuration, $configs);
0 ignored issues
show
Bug introduced by
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 70 can be null; however, Symfony\Component\Depend...:processConfiguration() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

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