Completed
Pull Request — master (#684)
by Guilh
02:44 queued 42s
created

DoctrineExtension::load()   C

Complexity

Conditions 7
Paths 16

Size

Total Lines 37
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

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