Completed
Pull Request — master (#695)
by
unknown
09:10
created

DoctrineExtension::loadCacheDriver()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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