Completed
Pull Request — master (#892)
by Andreas
02:15
created

DoctrineExtension::getDefinitionClassname()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 2
nc 2
nop 0
1
<?php
2
3
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
4
5
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
6
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface;
7
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader;
8
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\SymfonyBridgeAdapter;
9
use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory;
10
use Doctrine\ORM\Version;
11
use LogicException;
12
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
13
use Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddleware;
14
use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor;
15
use Symfony\Component\Config\FileLocator;
16
use Symfony\Component\DependencyInjection\Alias;
17
use Symfony\Component\DependencyInjection\ChildDefinition;
18
use Symfony\Component\DependencyInjection\ContainerBuilder;
19
use Symfony\Component\DependencyInjection\Definition;
20
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
21
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
22
use Symfony\Component\DependencyInjection\Reference;
23
use Symfony\Component\Form\AbstractType;
24
use Symfony\Component\Messenger\MessageBusInterface;
25
use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface;
26
27
/**
28
 * DoctrineExtension is an extension for the Doctrine DBAL and ORM library.
29
 */
30
class DoctrineExtension extends AbstractDoctrineExtension
31
{
32
    /** @var string */
33
    private $defaultConnection;
34
35
    /** @var SymfonyBridgeAdapter */
36
    private $adapter;
37
38
    public function __construct(SymfonyBridgeAdapter $adapter = null)
39
    {
40
        $this->adapter = $adapter ?: new SymfonyBridgeAdapter(new CacheProviderLoader(), 'doctrine.orm', 'orm');
41
    }
42
43
    /**
44
     * {@inheritDoc}
45
     */
46
    public function load(array $configs, ContainerBuilder $container)
47
    {
48
        $configuration = $this->getConfiguration($configs, $container);
49
        $config        = $this->processConfiguration($configuration, $configs);
0 ignored issues
show
Bug introduced by
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 48 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...
50
51
        $this->adapter->loadServicesConfiguration($container);
52
53
        if (! empty($config['dbal'])) {
54
            $this->dbalLoad($config['dbal'], $container);
55
        }
56
57
        if (empty($config['orm'])) {
58
            return;
59
        }
60
61
        if (empty($config['dbal'])) {
62
            throw new LogicException('Configuring the ORM layer requires to configure the DBAL layer as well.');
63
        }
64
65
        if (! class_exists(Version::class)) {
66
            throw new LogicException('To configure the ORM layer, you must first install the doctrine/orm package.');
67
        }
68
69
        $this->ormLoad($config['orm'], $container);
70
    }
71
72
    /**
73
     * Loads the DBAL configuration.
74
     *
75
     * Usage example:
76
     *
77
     *      <doctrine:dbal id="myconn" dbname="sfweb" user="root" />
78
     *
79
     * @param array            $config    An array of configuration settings
80
     * @param ContainerBuilder $container A ContainerBuilder instance
81
     */
82
    protected function dbalLoad(array $config, ContainerBuilder $container)
83
    {
84
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
85
        $loader->load('dbal.xml');
86
87
        if (empty($config['default_connection'])) {
88
            $keys                         = array_keys($config['connections']);
89
            $config['default_connection'] = reset($keys);
90
        }
91
92
        $this->defaultConnection = $config['default_connection'];
93
94
        $container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection));
95
        $container->getAlias('database_connection')->setPublic(true);
96
        $container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false));
97
98
        $container->setParameter('doctrine.dbal.connection_factory.types', $config['types']);
99
100
        $connections = [];
101
102
        foreach (array_keys($config['connections']) as $name) {
103
            $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name);
104
        }
105
106
        $container->setParameter('doctrine.connections', $connections);
107
        $container->setParameter('doctrine.default_connection', $this->defaultConnection);
108
109
        foreach ($config['connections'] as $name => $connection) {
110
            $this->loadDbalConnection($name, $connection, $container);
111
        }
112
    }
113
114
    /**
115
     * Loads a configured DBAL connection.
116
     *
117
     * @param string           $name       The name of the connection
118
     * @param array            $connection A dbal connection configuration.
119
     * @param ContainerBuilder $container  A ContainerBuilder instance
120
     */
121
    protected function loadDbalConnection($name, array $connection, ContainerBuilder $container)
122
    {
123
        $configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new ChildDefinition('doctrine.dbal.connection.configuration'));
124
        $logger        = null;
125
        if ($connection['logging']) {
126
            $logger = new Reference('doctrine.dbal.logger');
127
        }
128
        unset($connection['logging']);
129
        if ($connection['profiling']) {
130
            $profilingLoggerId = 'doctrine.dbal.logger.profiling.' . $name;
131
            $container->setDefinition($profilingLoggerId, new ChildDefinition('doctrine.dbal.logger.profiling'));
132
            $profilingLogger = new Reference($profilingLoggerId);
133
            $container->getDefinition('data_collector.doctrine')->addMethodCall('addLogger', [$name, $profilingLogger]);
134
135
            if ($logger !== null) {
136
                $chainLogger = new ChildDefinition('doctrine.dbal.logger.chain');
137
                $chainLogger->addMethodCall('addLogger', [$profilingLogger]);
138
139
                $loggerId = 'doctrine.dbal.logger.chain.' . $name;
140
                $container->setDefinition($loggerId, $chainLogger);
141
                $logger = new Reference($loggerId);
142
            } else {
143
                $logger = $profilingLogger;
144
            }
145
        }
146
        unset($connection['profiling']);
147
148
        if (isset($connection['auto_commit'])) {
149
            $configuration->addMethodCall('setAutoCommit', [$connection['auto_commit']]);
150
        }
151
152
        unset($connection['auto_commit']);
153
154
        if (isset($connection['schema_filter']) && $connection['schema_filter']) {
155
            $configuration->addMethodCall('setFilterSchemaAssetsExpression', [$connection['schema_filter']]);
156
        }
157
158
        unset($connection['schema_filter']);
159
160
        if ($logger) {
161
            $configuration->addMethodCall('setSQLLogger', [$logger]);
162
        }
163
164
        // event manager
165
        $container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new ChildDefinition('doctrine.dbal.connection.event_manager'));
166
167
        // connection
168
        $options = $this->getConnectionOptions($connection);
169
170
        $def = $container
171
            ->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new ChildDefinition('doctrine.dbal.connection'))
172
            ->setPublic(true)
173
            ->setArguments([
174
                $options,
175
                new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)),
176
                new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)),
177
                $connection['mapping_types'],
178
            ]);
179
180
        // Set class in case "wrapper_class" option was used to assist IDEs
181
        if (isset($options['wrapperClass'])) {
182
            $def->setClass($options['wrapperClass']);
183
        }
184
185
        if (! empty($connection['use_savepoints'])) {
186
            $def->addMethodCall('setNestTransactionsWithSavepoints', [$connection['use_savepoints']]);
187
        }
188
189
        // Create a shard_manager for this connection
190
        if (! isset($options['shards'])) {
191
            return;
192
        }
193
194
        $shardManagerDefinition = new Definition($options['shardManagerClass'], [new Reference(sprintf('doctrine.dbal.%s_connection', $name))]);
195
        $container->setDefinition(sprintf('doctrine.dbal.%s_shard_manager', $name), $shardManagerDefinition);
196
    }
197
198
    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...
199
    {
200
        $options = $connection;
201
202
        if (isset($options['platform_service'])) {
203
            $options['platform'] = new Reference($options['platform_service']);
204
            unset($options['platform_service']);
205
        }
206
        unset($options['mapping_types']);
207
208
        if (isset($options['shard_choser_service'])) {
209
            $options['shard_choser'] = new Reference($options['shard_choser_service']);
210
            unset($options['shard_choser_service']);
211
        }
212
213
        foreach ([
214
            'options' => 'driverOptions',
215
            'driver_class' => 'driverClass',
216
            'wrapper_class' => 'wrapperClass',
217
            'keep_slave' => 'keepSlave',
218
            'shard_choser' => 'shardChoser',
219
            'shard_manager_class' => 'shardManagerClass',
220
            'server_version' => 'serverVersion',
221
            'default_table_options' => 'defaultTableOptions',
222
        ] as $old => $new) {
223
            if (! isset($options[$old])) {
224
                continue;
225
            }
226
227
            $options[$new] = $options[$old];
228
            unset($options[$old]);
229
        }
230
231
        if (! empty($options['slaves']) && ! empty($options['shards'])) {
232
            throw new InvalidArgumentException('Sharding and master-slave connection cannot be used together');
233
        }
234
235
        if (! empty($options['slaves'])) {
236
            $nonRewrittenKeys = [
237
                'driver' => true,
238
                'driverOptions' => true,
239
                'driverClass' => true,
240
                'wrapperClass' => true,
241
                'keepSlave' => true,
242
                'shardChoser' => true,
243
                'platform' => true,
244
                'slaves' => true,
245
                'master' => true,
246
                'shards' => true,
247
                'serverVersion' => true,
248
                // included by safety but should have been unset already
249
                'logging' => true,
250
                'profiling' => true,
251
                'mapping_types' => true,
252
                'platform_service' => true,
253
            ];
254 View Code Duplication
            foreach ($options as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
255
                if (isset($nonRewrittenKeys[$key])) {
256
                    continue;
257
                }
258
                $options['master'][$key] = $value;
259
                unset($options[$key]);
260
            }
261
            if (empty($options['wrapperClass'])) {
262
                // Change the wrapper class only if the user does not already forced using a custom one.
263
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Connections\\MasterSlaveConnection';
264
            }
265
        } else {
266
            unset($options['slaves']);
267
        }
268
269
        if (! empty($options['shards'])) {
270
            $nonRewrittenKeys = [
271
                'driver' => true,
272
                'driverOptions' => true,
273
                'driverClass' => true,
274
                'wrapperClass' => true,
275
                'keepSlave' => true,
276
                'shardChoser' => true,
277
                'platform' => true,
278
                'slaves' => true,
279
                'global' => true,
280
                'shards' => true,
281
                'serverVersion' => true,
282
                // included by safety but should have been unset already
283
                'logging' => true,
284
                'profiling' => true,
285
                'mapping_types' => true,
286
                'platform_service' => true,
287
            ];
288 View Code Duplication
            foreach ($options as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
289
                if (isset($nonRewrittenKeys[$key])) {
290
                    continue;
291
                }
292
                $options['global'][$key] = $value;
293
                unset($options[$key]);
294
            }
295
            if (empty($options['wrapperClass'])) {
296
                // Change the wrapper class only if the user does not already forced using a custom one.
297
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardConnection';
298
            }
299
            if (empty($options['shardManagerClass'])) {
300
                // Change the shard manager class only if the user does not already forced using a custom one.
301
                $options['shardManagerClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardManager';
302
            }
303
        } else {
304
            unset($options['shards']);
305
        }
306
307
        return $options;
308
    }
309
310
    /**
311
     * Loads the Doctrine ORM configuration.
312
     *
313
     * Usage example:
314
     *
315
     *     <doctrine:orm id="mydm" connection="myconn" />
316
     *
317
     * @param array            $config    An array of configuration settings
318
     * @param ContainerBuilder $container A ContainerBuilder instance
319
     */
320
    protected function ormLoad(array $config, ContainerBuilder $container)
321
    {
322
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
323
        $loader->load('orm.xml');
324
325
        $entityManagers = [];
326
        foreach (array_keys($config['entity_managers']) as $name) {
327
            $entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name);
328
        }
329
        $container->setParameter('doctrine.entity_managers', $entityManagers);
330
331
        if (empty($config['default_entity_manager'])) {
332
            $tmp                              = array_keys($entityManagers);
333
            $config['default_entity_manager'] = reset($tmp);
334
        }
335
        $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']);
336
337
        $options = ['auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace'];
338
        foreach ($options as $key) {
339
            $container->setParameter('doctrine.orm.' . $key, $config[$key]);
340
        }
341
342
        $container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager']));
343
        $container->getAlias('doctrine.orm.entity_manager')->setPublic(true);
344
345
        $config['entity_managers'] = $this->fixManagersAutoMappings($config['entity_managers'], $container->getParameter('kernel.bundles'));
346
347
        foreach ($config['entity_managers'] as $name => $entityManager) {
348
            $entityManager['name'] = $name;
349
            $this->loadOrmEntityManager($entityManager, $container);
350
351
            $this->loadPropertyInfoExtractor($name, $container);
352
        }
353
354
        if ($config['resolve_target_entities']) {
355
            $def = $container->findDefinition('doctrine.orm.listeners.resolve_target_entity');
356
            foreach ($config['resolve_target_entities'] as $name => $implementation) {
357
                $def->addMethodCall('addResolveTargetEntity', [
358
                    $name,
359
                    $implementation,
360
                    [],
361
                ]);
362
            }
363
364
            $def->addTag('doctrine.event_subscriber');
365
        }
366
367
        $container->registerForAutoconfiguration(ServiceEntityRepositoryInterface::class)
368
            ->addTag(ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG);
369
370
        // If the Messenger component is installed and the doctrine transaction middleware is available, wire it:
371
        if (! interface_exists(MessageBusInterface::class) || ! class_exists(DoctrineTransactionMiddleware::class)) {
372
            return;
373
        }
374
375
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
376
        $loader->load('messenger.xml');
377
    }
378
379
    /**
380
     * Loads a configured ORM entity manager.
381
     *
382
     * @param array            $entityManager A configured ORM entity manager.
383
     * @param ContainerBuilder $container     A ContainerBuilder instance
384
     */
385
    protected function loadOrmEntityManager(array $entityManager, ContainerBuilder $container)
386
    {
387
        $ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new ChildDefinition('doctrine.orm.configuration'));
388
389
        $this->loadOrmEntityManagerMappingInformation($entityManager, $ormConfigDef, $container);
390
        $this->loadOrmCacheDrivers($entityManager, $container);
391
392
        if (isset($entityManager['entity_listener_resolver']) && $entityManager['entity_listener_resolver']) {
393
            $container->setAlias(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $entityManager['entity_listener_resolver']);
394
        } else {
395
            $definition = new Definition('%doctrine.orm.entity_listener_resolver.class%');
396
            $definition->addArgument(new Reference('service_container'));
397
            $container->setDefinition(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $definition);
398
        }
399
400
        $methods = [
401
            'setMetadataCacheImpl' => new Reference(sprintf('doctrine.orm.%s_metadata_cache', $entityManager['name'])),
402
            'setQueryCacheImpl' => new Reference(sprintf('doctrine.orm.%s_query_cache', $entityManager['name'])),
403
            'setResultCacheImpl' => new Reference(sprintf('doctrine.orm.%s_result_cache', $entityManager['name'])),
404
            'setMetadataDriverImpl' => new Reference('doctrine.orm.' . $entityManager['name'] . '_metadata_driver'),
405
            'setProxyDir' => '%doctrine.orm.proxy_dir%',
406
            'setProxyNamespace' => '%doctrine.orm.proxy_namespace%',
407
            'setAutoGenerateProxyClasses' => '%doctrine.orm.auto_generate_proxy_classes%',
408
            'setClassMetadataFactoryName' => $entityManager['class_metadata_factory_name'],
409
            'setDefaultRepositoryClassName' => $entityManager['default_repository_class'],
410
            'setNamingStrategy' => new Reference($entityManager['naming_strategy']),
411
            'setQuoteStrategy' => new Reference($entityManager['quote_strategy']),
412
            'setEntityListenerResolver' => new Reference(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name'])),
413
        ];
414
415
        $listenerId        = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $entityManager['name']);
416
        $listenerDef       = $container->setDefinition($listenerId, new Definition('%doctrine.orm.listeners.attach_entity_listeners.class%'));
417
        $listenerTagParams = ['event' => 'loadClassMetadata'];
418
        if (isset($entityManager['connection'])) {
419
            $listenerTagParams['connection'] = $entityManager['connection'];
420
        }
421
        $listenerDef->addTag('doctrine.event_listener', $listenerTagParams);
422
423
        if (isset($entityManager['second_level_cache'])) {
424
            $this->loadOrmSecondLevelCache($entityManager, $ormConfigDef, $container);
425
        }
426
427
        if ($entityManager['repository_factory']) {
428
            $methods['setRepositoryFactory'] = new Reference($entityManager['repository_factory']);
429
        }
430
431
        foreach ($methods as $method => $arg) {
432
            $ormConfigDef->addMethodCall($method, [$arg]);
433
        }
434
435
        foreach ($entityManager['hydrators'] as $name => $class) {
436
            $ormConfigDef->addMethodCall('addCustomHydrationMode', [$name, $class]);
437
        }
438
439
        if (! empty($entityManager['dql'])) {
440
            foreach ($entityManager['dql']['string_functions'] as $name => $function) {
441
                $ormConfigDef->addMethodCall('addCustomStringFunction', [$name, $function]);
442
            }
443
            foreach ($entityManager['dql']['numeric_functions'] as $name => $function) {
444
                $ormConfigDef->addMethodCall('addCustomNumericFunction', [$name, $function]);
445
            }
446
            foreach ($entityManager['dql']['datetime_functions'] as $name => $function) {
447
                $ormConfigDef->addMethodCall('addCustomDatetimeFunction', [$name, $function]);
448
            }
449
        }
450
451
        $enabledFilters    = [];
452
        $filtersParameters = [];
453
        foreach ($entityManager['filters'] as $name => $filter) {
454
            $ormConfigDef->addMethodCall('addFilter', [$name, $filter['class']]);
455
            if ($filter['enabled']) {
456
                $enabledFilters[] = $name;
457
            }
458
            if (! $filter['parameters']) {
459
                continue;
460
            }
461
462
            $filtersParameters[$name] = $filter['parameters'];
463
        }
464
465
        $managerConfiguratorName = sprintf('doctrine.orm.%s_manager_configurator', $entityManager['name']);
466
        $container
467
            ->setDefinition($managerConfiguratorName, new ChildDefinition('doctrine.orm.manager_configurator.abstract'))
468
            ->replaceArgument(0, $enabledFilters)
469
            ->replaceArgument(1, $filtersParameters);
470
471
        if (! isset($entityManager['connection'])) {
472
            $entityManager['connection'] = $this->defaultConnection;
473
        }
474
475
        $container
476
            ->setDefinition(sprintf('doctrine.orm.%s_entity_manager', $entityManager['name']), new ChildDefinition('doctrine.orm.entity_manager.abstract'))
477
            ->setPublic(true)
478
            ->setArguments([
479
                new Reference(sprintf('doctrine.dbal.%s_connection', $entityManager['connection'])),
480
                new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])),
481
            ])
482
            ->setConfigurator([new Reference($managerConfiguratorName), 'configure']);
483
484
        $container->setAlias(
485
            sprintf('doctrine.orm.%s_entity_manager.event_manager', $entityManager['name']),
486
            new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $entityManager['connection']), false)
487
        );
488
489
        if (! isset($entityManager['entity_listeners'])) {
490
            return;
491
        }
492
493
        if (! isset($listenerDef)) {
494
            throw new InvalidArgumentException('Entity listeners configuration requires doctrine-orm 2.5.0 or newer');
495
        }
496
497
        $entities = $entityManager['entity_listeners']['entities'];
498
499
        foreach ($entities as $entityListenerClass => $entity) {
500
            foreach ($entity['listeners'] as $listenerClass => $listener) {
501
                foreach ($listener['events'] as $listenerEvent) {
502
                    $listenerEventName = $listenerEvent['type'];
503
                    $listenerMethod    = $listenerEvent['method'];
504
505
                    $listenerDef->addMethodCall('addEntityListener', [
506
                        $entityListenerClass,
507
                        $listenerClass,
508
                        $listenerEventName,
509
                        $listenerMethod,
510
                    ]);
511
                }
512
            }
513
        }
514
    }
515
516
    /**
517
     * Loads an ORM entity managers bundle mapping information.
518
     *
519
     * There are two distinct configuration possibilities for mapping information:
520
     *
521
     * 1. Specify a bundle and optionally details where the entity and mapping information reside.
522
     * 2. Specify an arbitrary mapping location.
523
     *
524
     * @param array            $entityManager A configured ORM entity manager
525
     * @param Definition       $ormConfigDef  A Definition instance
526
     * @param ContainerBuilder $container     A ContainerBuilder instance
527
     *
528
     * @example
529
     *
530
     *  doctrine.orm:
531
     *     mappings:
532
     *         MyBundle1: ~
533
     *         MyBundle2: yml
534
     *         MyBundle3: { type: annotation, dir: Entities/ }
535
     *         MyBundle4: { type: xml, dir: Resources/config/doctrine/mapping }
536
     *         MyBundle5:
537
     *             type: yml
538
     *             dir: bundle-mappings/
539
     *             alias: BundleAlias
540
     *         arbitrary_key:
541
     *             type: xml
542
     *             dir: %kernel.root_dir%/../src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entities
543
     *             prefix: DoctrineExtensions\Entities\
544
     *             alias: DExt
545
     *
546
     * In the case of bundles everything is really optional (which leads to autodetection for this bundle) but
547
     * in the mappings key everything except alias is a required argument.
548
     */
549
    protected function loadOrmEntityManagerMappingInformation(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
550
    {
551
        // reset state of drivers and alias map. They are only used by this methods and children.
552
        $this->drivers  = [];
553
        $this->aliasMap = [];
554
555
        $this->loadMappingInformation($entityManager, $container);
556
        $this->registerMappingDrivers($entityManager, $container);
557
558
        $ormConfigDef->addMethodCall('setEntityNamespaces', [$this->aliasMap]);
559
    }
560
561
    /**
562
     * Loads an ORM second level cache bundle mapping information.
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
     * @example
569
     *  entity_managers:
570
     *      default:
571
     *          second_level_cache:
572
     *              region_cache_driver: apc
573
     *              log_enabled: true
574
     *              regions:
575
     *                  my_service_region:
576
     *                      type: service
577
     *                      service : "my_service_region"
578
     *
579
     *                  my_query_region:
580
     *                      lifetime: 300
581
     *                      cache_driver: array
582
     *                      type: filelock
583
     *
584
     *                  my_entity_region:
585
     *                      lifetime: 600
586
     *                      cache_driver:
587
     *                          type: apc
588
     */
589
    protected function loadOrmSecondLevelCache(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
590
    {
591
        $driverId = null;
592
        $enabled  = $entityManager['second_level_cache']['enabled'];
593
594
        if (isset($entityManager['second_level_cache']['region_cache_driver'])) {
595
            $driverName = 'second_level_cache.region_cache_driver';
596
            $driverMap  = $entityManager['second_level_cache']['region_cache_driver'];
597
            $driverId   = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
598
        }
599
600
        $configId   = sprintf('doctrine.orm.%s_second_level_cache.cache_configuration', $entityManager['name']);
601
        $regionsId  = sprintf('doctrine.orm.%s_second_level_cache.regions_configuration', $entityManager['name']);
602
        $driverId   = $driverId ?: sprintf('doctrine.orm.%s_second_level_cache.region_cache_driver', $entityManager['name']);
603
        $configDef  = $container->setDefinition($configId, new Definition('%doctrine.orm.second_level_cache.cache_configuration.class%'));
604
        $regionsDef = $container->setDefinition($regionsId, new Definition('%doctrine.orm.second_level_cache.regions_configuration.class%'));
605
606
        $slcFactoryId = sprintf('doctrine.orm.%s_second_level_cache.default_cache_factory', $entityManager['name']);
607
        $factoryClass = isset($entityManager['second_level_cache']['factory']) ? $entityManager['second_level_cache']['factory'] : '%doctrine.orm.second_level_cache.default_cache_factory.class%';
608
609
        $definition = new Definition($factoryClass, [new Reference($regionsId), new Reference($driverId)]);
610
611
        $slcFactoryDef = $container
612
            ->setDefinition($slcFactoryId, $definition);
613
614
        if (isset($entityManager['second_level_cache']['regions'])) {
615
            foreach ($entityManager['second_level_cache']['regions'] as $name => $region) {
616
                $regionRef  = null;
617
                $regionType = $region['type'];
618
619
                if ($regionType === 'service') {
620
                    $regionId  = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
621
                    $regionRef = new Reference($region['service']);
622
623
                    $container->setAlias($regionId, new Alias($region['service'], false));
624
                }
625
626
                if ($regionType === 'default' || $regionType === 'filelock') {
627
                    $regionId   = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
628
                    $driverName = sprintf('second_level_cache.region.%s_driver', $name);
629
                    $driverMap  = $region['cache_driver'];
630
                    $driverId   = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
631
                    $regionRef  = new Reference($regionId);
632
633
                    $container
634
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.default_region.class%'))
635
                        ->setArguments([$name, new Reference($driverId), $region['lifetime']]);
636
                }
637
638
                if ($regionType === 'filelock') {
639
                    $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s_filelock', $entityManager['name'], $name);
640
641
                    $container
642
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.filelock_region.class%'))
643
                        ->setArguments([$regionRef, $region['lock_path'], $region['lock_lifetime']]);
644
645
                    $regionRef = new Reference($regionId);
646
                    $regionsDef->addMethodCall('getLockLifetime', [$name, $region['lock_lifetime']]);
647
                }
648
649
                $regionsDef->addMethodCall('setLifetime', [$name, $region['lifetime']]);
650
                $slcFactoryDef->addMethodCall('setRegion', [$regionRef]);
651
            }
652
        }
653
654
        if ($entityManager['second_level_cache']['log_enabled']) {
655
            $loggerChainId   = sprintf('doctrine.orm.%s_second_level_cache.logger_chain', $entityManager['name']);
656
            $loggerStatsId   = sprintf('doctrine.orm.%s_second_level_cache.logger_statistics', $entityManager['name']);
657
            $loggerChaingDef = $container->setDefinition($loggerChainId, new Definition('%doctrine.orm.second_level_cache.logger_chain.class%'));
658
            $loggerStatsDef  = $container->setDefinition($loggerStatsId, new Definition('%doctrine.orm.second_level_cache.logger_statistics.class%'));
659
660
            $loggerChaingDef->addMethodCall('setLogger', ['statistics', $loggerStatsDef]);
661
            $configDef->addMethodCall('setCacheLogger', [$loggerChaingDef]);
662
663
            foreach ($entityManager['second_level_cache']['loggers'] as $name => $logger) {
664
                $loggerId  = sprintf('doctrine.orm.%s_second_level_cache.logger.%s', $entityManager['name'], $name);
665
                $loggerRef = new Reference($logger['service']);
666
667
                $container->setAlias($loggerId, new Alias($logger['service'], false));
668
                $loggerChaingDef->addMethodCall('setLogger', [$name, $loggerRef]);
669
            }
670
        }
671
672
        $configDef->addMethodCall('setCacheFactory', [$slcFactoryDef]);
673
        $configDef->addMethodCall('setRegionsConfiguration', [$regionsDef]);
674
        $ormConfigDef->addMethodCall('setSecondLevelCacheEnabled', [$enabled]);
675
        $ormConfigDef->addMethodCall('setSecondLevelCacheConfiguration', [$configDef]);
676
    }
677
678
    /**
679
     * {@inheritDoc}
680
     */
681
    protected function getObjectManagerElementName($name)
682
    {
683
        return 'doctrine.orm.' . $name;
684
    }
685
686
    protected function getMappingObjectDefaultName()
687
    {
688
        return 'Entity';
689
    }
690
691
    /**
692
     * {@inheritDoc}
693
     */
694
    protected function getMappingResourceConfigDirectory()
695
    {
696
        return 'Resources/config/doctrine';
697
    }
698
699
    /**
700
     * {@inheritDoc}
701
     */
702
    protected function getMappingResourceExtension()
703
    {
704
        return 'orm';
705
    }
706
707
    /**
708
     * {@inheritDoc}
709
     */
710
    protected function loadCacheDriver($driverName, $entityManagerName, array $driverMap, ContainerBuilder $container)
711
    {
712
        if (! empty($driverMap['cache_provider'])) {
713
            $aliasId   = $this->getObjectManagerElementName(sprintf('%s_%s', $entityManagerName, $driverName));
714
            $serviceId = sprintf('doctrine_cache.providers.%s', $driverMap['cache_provider']);
715
716
            $container->setAlias($aliasId, new Alias($serviceId, false));
717
718
            return $aliasId;
719
        }
720
721
        return $this->adapter->loadCacheDriver($driverName, $entityManagerName, $driverMap, $container);
722
    }
723
724
    /**
725
     * Loads a configured entity managers cache drivers.
726
     *
727
     * @param array            $entityManager A configured ORM entity manager.
728
     * @param ContainerBuilder $container     A ContainerBuilder instance
729
     */
730
    protected function loadOrmCacheDrivers(array $entityManager, ContainerBuilder $container)
731
    {
732
        $this->loadCacheDriver('metadata_cache', $entityManager['name'], $entityManager['metadata_cache_driver'], $container);
733
        $this->loadCacheDriver('result_cache', $entityManager['name'], $entityManager['result_cache_driver'], $container);
734
        $this->loadCacheDriver('query_cache', $entityManager['name'], $entityManager['query_cache_driver'], $container);
735
    }
736
737
    /**
738
     * Loads a property info extractor for each defined entity manager.
739
     *
740
     * @param string $entityManagerName
741
     */
742
    private function loadPropertyInfoExtractor($entityManagerName, ContainerBuilder $container)
743
    {
744
        $propertyExtractorDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.property_info_extractor', $entityManagerName), DoctrineExtractor::class);
745
        if (interface_exists(PropertyInitializableExtractorInterface::class)) {
746
            $argumentId = sprintf('doctrine.orm.%s_entity_manager', $entityManagerName);
747
        } else {
748
            $argumentId = sprintf('doctrine.orm.%s_entity_manager.metadata_factory', $entityManagerName);
749
750
            $metadataFactoryDefinition = $container->register($argumentId, ClassMetadataFactory::class);
751
            $metadataFactoryDefinition->setFactory([
752
                new Reference(sprintf('doctrine.orm.%s_entity_manager', $entityManagerName)),
753
                'getMetadataFactory',
754
            ]);
755
            $metadataFactoryDefinition->setPublic(false);
756
        }
757
758
        $propertyExtractorDefinition->addArgument(new Reference($argumentId));
759
760
        $propertyExtractorDefinition->addTag('property_info.list_extractor', ['priority' => -1001]);
761
        $propertyExtractorDefinition->addTag('property_info.type_extractor', ['priority' => -999]);
762
    }
763
764
    /**
765
     * @param array  $objectManager
766
     * @param string $cacheName
767
     */
768
    public function loadObjectManagerCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName)
769
    {
770
        $this->loadCacheDriver($cacheName, $objectManager['name'], $objectManager[$cacheName . '_driver'], $container);
771
    }
772
773
    /**
774
     * {@inheritDoc}
775
     */
776
    public function getXsdValidationBasePath()
777
    {
778
        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...
779
    }
780
781
    /**
782
     * {@inheritDoc}
783
     */
784
    public function getNamespace()
785
    {
786
        return 'http://symfony.com/schema/dic/doctrine';
787
    }
788
789
    /**
790
     * {@inheritDoc}
791
     */
792
    public function getConfiguration(array $config, ContainerBuilder $container)
793
    {
794
        return new Configuration($container->getParameter('kernel.debug'));
795
    }
796
}
797