Completed
Pull Request — master (#596)
by
unknown
02:40
created

DoctrineExtension::loadOrmSecondLevelCache()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 2
eloc 3
nc 2
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\Reference;
25
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
26
use Symfony\Component\Config\FileLocator;
27
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\SymfonyBridgeAdapter;
28
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader;
29
30
/**
31
 * DoctrineExtension is an extension for the Doctrine DBAL and ORM library.
32
 *
33
 * @author Jonathan H. Wage <[email protected]>
34
 * @author Fabien Potencier <[email protected]>
35
 * @author Benjamin Eberlei <[email protected]>
36
 * @author Fabio B. Silva <[email protected]>
37
 * @author Kinn Coelho Julião <[email protected]>
38
 */
39
class DoctrineExtension extends AbstractDoctrineExtension
40
{
41
    /**
42
     * @var string
43
     */
44
    private $defaultConnection;
45
46
    /**
47
     * @var array
48
     */
49
    private $entityManagers;
50
51
    /**
52
     * @var SymfonyBridgeAdapter
53
     */
54
    private $adapter;
55
56
    /**
57
     * @param SymfonyBridgeAdapter $adapter
0 ignored issues
show
Documentation introduced by
Should the type for parameter $adapter not be null|SymfonyBridgeAdapter?

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

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

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

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

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
71
72
        $this->adapter->loadServicesConfiguration($container);
73
74
        if (!empty($config['dbal'])) {
75
            $this->dbalLoad($config['dbal'], $container);
76
        }
77
78
        if (!empty($config['orm'])) {
79
            if (empty($config['dbal'])) {
80
                throw new \LogicException('Configuring the ORM layer requires to configure the DBAL layer as well.');
81
            }
82
83
            if (!class_exists('Doctrine\ORM\Version')) {
84
                throw new \LogicException('To configure the ORM layer, you must first install the doctrine/orm package.');
85
            }
86
87
            $this->ormLoad($config['orm'], $container);
88
        }
89
90
        $this->addClassesToCompile(array(
91
            'Doctrine\\Common\\Annotations\\DocLexer',
92
            'Doctrine\\Common\\Annotations\\FileCacheReader',
93
            'Doctrine\\Common\\Annotations\\PhpParser',
94
            'Doctrine\\Common\\Annotations\\Reader',
95
            'Doctrine\\Common\\Lexer',
96
            'Doctrine\\Common\\Persistence\\ConnectionRegistry',
97
            'Doctrine\\Common\\Persistence\\Proxy',
98
            'Doctrine\\Common\\Util\\ClassUtils',
99
            'Doctrine\\Bundle\\DoctrineBundle\\Registry',
100
        ));
101
    }
102
103
    /**
104
     * Loads the DBAL configuration.
105
     *
106
     * Usage example:
107
     *
108
     *      <doctrine:dbal id="myconn" dbname="sfweb" user="root" />
109
     *
110
     * @param array            $config    An array of configuration settings
111
     * @param ContainerBuilder $container A ContainerBuilder instance
112
     */
113
    protected function dbalLoad(array $config, ContainerBuilder $container)
114
    {
115
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
116
        $loader->load('dbal.xml');
117
118
        if (empty($config['default_connection'])) {
119
            $keys = array_keys($config['connections']);
120
            $config['default_connection'] = reset($keys);
121
        }
122
123
        $this->defaultConnection = $config['default_connection'];
124
125
        $container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection));
126
        $container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false));
127
128
        $container->setParameter('doctrine.dbal.connection_factory.types', $config['types']);
129
130
        $connections = array();
131
132
        foreach (array_keys($config['connections']) as $name) {
133
            $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name);
134
        }
135
136
        $container->setParameter('doctrine.connections', $connections);
137
        $container->setParameter('doctrine.default_connection', $this->defaultConnection);
138
139
        $def = $container->getDefinition('doctrine.dbal.connection');
140
        if (method_exists($def, 'setFactory')) {
141
            // to be inlined in dbal.xml when dependency on Symfony DependencyInjection is bumped to 2.6
142
            $def->setFactory(array(new Reference('doctrine.dbal.connection_factory'), 'createConnection'));
143
        } else {
144
            // to be removed when dependency on Symfony DependencyInjection is bumped to 2.6
145
            $def->setFactoryService('doctrine.dbal.connection_factory');
0 ignored issues
show
Bug introduced by
The method setFactoryService() does not exist on Symfony\Component\DependencyInjection\Definition. Did you maybe mean setFactory()?

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

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

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

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

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

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

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

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

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

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

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

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