Completed
Pull Request — master (#531)
by Mike
03:06
created

Configuration   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 651
Duplicated Lines 2.15 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 13
Bugs 3 Features 5
Metric Value
wmc 38
c 13
b 3
f 5
lcom 1
cbo 6
dl 14
loc 651
rs 8.3999

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getConfigTreeBuilder() 0 10 1
B addDbalSection() 7 48 6
A getDbalConnectionsNode() 0 72 1
B configureDbalDriverNode() 0 83 3
C addOrmSection() 7 71 12
A getOrmTargetEntityResolverNode() 0 14 1
C getOrmEntityListenersNode() 0 78 7
B getOrmEntityManagersNode() 0 153 2
B getOrmCacheDriverNode() 0 25 1
A getAutoGenerateModes() 0 21 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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 Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
18
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
19
use Symfony\Component\Config\Definition\ConfigurationInterface;
20
21
/**
22
 * This class contains the configuration information for the bundle
23
 *
24
 * This information is solely responsible for how the different configuration
25
 * sections are normalized, and merged.
26
 *
27
 * @author Christophe Coevoet <[email protected]>
28
 */
29
class Configuration implements ConfigurationInterface
30
{
31
    private $debug;
32
33
    /**
34
     * Constructor
35
     *
36
     * @param Boolean $debug Whether to use the debug mode
37
     */
38
    public function __construct($debug)
39
    {
40
        $this->debug = (Boolean) $debug;
41
    }
42
43
    /**
44
     * {@inheritDoc}
45
     */
46
    public function getConfigTreeBuilder()
47
    {
48
        $treeBuilder = new TreeBuilder();
49
        $rootNode = $treeBuilder->root('doctrine');
50
51
        $this->addDbalSection($rootNode);
52
        $this->addOrmSection($rootNode);
53
54
        return $treeBuilder;
55
    }
56
57
    /**
58
     * Add DBAL section to configuration tree
59
     *
60
     * @param ArrayNodeDefinition $node
61
     */
62
    private function addDbalSection(ArrayNodeDefinition $node)
63
    {
64
        $node
65
            ->children()
66
            ->arrayNode('dbal')
67
                ->beforeNormalization()
68
                    ->ifTrue(function ($v) { return is_array($v) && !array_key_exists('connections', $v) && !array_key_exists('connection', $v); })
69
                    ->then(function ($v) {
70
                        // Key that should not be rewritten to the connection config
71
                        $excludedKeys = array('default_connection' => true, 'types' => true, 'type' => true);
72
                        $connection = array();
73 View Code Duplication
                        foreach ($v 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...
74
                            if (isset($excludedKeys[$key])) {
75
                                continue;
76
                            }
77
                            $connection[$key] = $v[$key];
78
                            unset($v[$key]);
79
                        }
80
                        $v['default_connection'] = isset($v['default_connection']) ? (string) $v['default_connection'] : 'default';
81
                        $v['connections'] = array($v['default_connection'] => $connection);
82
83
                        return $v;
84
                    })
85
                ->end()
86
                ->children()
87
                    ->scalarNode('default_connection')->end()
88
                ->end()
89
                ->fixXmlConfig('type')
90
                ->children()
91
                    ->arrayNode('types')
92
                        ->useAttributeAsKey('name')
93
                        ->prototype('array')
94
                            ->beforeNormalization()
95
                                ->ifString()
96
                                ->then(function ($v) { return array('class' => $v); })
97
                            ->end()
98
                            ->children()
99
                                ->scalarNode('class')->isRequired()->end()
100
                                ->booleanNode('commented')->defaultTrue()->end()
101
                            ->end()
102
                        ->end()
103
                    ->end()
104
                ->end()
105
                ->fixXmlConfig('connection')
106
                ->append($this->getDbalConnectionsNode())
107
            ->end()
108
        ;
109
    }
110
111
    /**
112
     * Return the dbal connections node
113
     *
114
     * @return ArrayNodeDefinition
115
     */
116
    private function getDbalConnectionsNode()
117
    {
118
        $treeBuilder = new TreeBuilder();
119
        $node = $treeBuilder->root('connections');
120
121
        /** @var $connectionNode ArrayNodeDefinition */
122
        $connectionNode = $node
123
            ->requiresAtLeastOneElement()
124
            ->useAttributeAsKey('name')
125
            ->prototype('array')
126
        ;
127
128
        $this->configureDbalDriverNode($connectionNode);
129
130
        $connectionNode
131
            ->fixXmlConfig('option')
132
            ->fixXmlConfig('mapping_type')
133
            ->fixXmlConfig('slave')
134
            ->fixXmlConfig('shard')
135
            ->fixXmlConfig('default_table_option')
136
            ->children()
137
                ->scalarNode('driver')->defaultValue('pdo_mysql')->end()
138
                ->scalarNode('platform_service')->end()
139
                ->booleanNode('auto_commit')->end()
140
                ->scalarNode('schema_filter')->end()
141
                ->booleanNode('logging')->defaultValue($this->debug)->end()
142
                ->booleanNode('profiling')->defaultValue($this->debug)->end()
143
                ->scalarNode('server_version')->end()
144
                ->scalarNode('driver_class')->end()
145
                ->scalarNode('wrapper_class')->end()
146
                ->scalarNode('shard_choser')->end()
147
                ->scalarNode('shard_choser_service')->end()
148
                ->booleanNode('keep_slave')->end()
149
                ->arrayNode('options')
150
                    ->useAttributeAsKey('key')
151
                    ->prototype('scalar')->end()
152
                ->end()
153
                ->arrayNode('mapping_types')
154
                    ->useAttributeAsKey('name')
155
                    ->prototype('scalar')->end()
156
                ->end()
157
                ->arrayNode('default_table_options')
158
                    ->info("This option is used by the schema-tool and affects generated SQL. Possible keys include 'charset','collate', and 'engine'.")
159
                    ->useAttributeAsKey('name')
160
                    ->prototype('scalar')->end()
161
                ->end()
162
            ->end()
163
        ;
164
165
        $slaveNode = $connectionNode
166
            ->children()
167
                ->arrayNode('slaves')
168
                    ->useAttributeAsKey('name')
169
                    ->prototype('array')
170
        ;
171
        $this->configureDbalDriverNode($slaveNode);
0 ignored issues
show
Compatibility introduced by
$slaveNode of type object<Symfony\Component...Builder\NodeDefinition> is not a sub-type of object<Symfony\Component...er\ArrayNodeDefinition>. It seems like you assume a child class of the class Symfony\Component\Config...\Builder\NodeDefinition to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
172
173
        $shardNode = $connectionNode
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Config...\Builder\NodeDefinition as the method children() does only exist in the following sub-classes of Symfony\Component\Config...\Builder\NodeDefinition: Symfony\Component\Config...der\ArrayNodeDefinition. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
174
            ->children()
175
                ->arrayNode('shards')
176
                    ->prototype('array')
177
                    ->children()
178
                        ->integerNode('id')
179
                            ->min(1)
180
                            ->isRequired()
181
                        ->end()
182
                    ->end()
183
        ;
184
        $this->configureDbalDriverNode($shardNode);
185
186
        return $node;
187
    }
188
189
    /**
190
     * Adds config keys related to params processed by the DBAL drivers
191
     *
192
     * These keys are available for slave configurations too.
193
     *
194
     * @param ArrayNodeDefinition $node
195
     */
196
    private function configureDbalDriverNode(ArrayNodeDefinition $node)
197
    {
198
        $node
199
            ->children()
200
                ->scalarNode('url')->info('A URL with connection information; any parameter value parsed from this string will override explicitly set parameters')->end()
201
                ->scalarNode('dbname')->end()
202
                ->scalarNode('host')->defaultValue('localhost')->end()
203
                ->scalarNode('port')->defaultNull()->end()
204
                ->scalarNode('user')->defaultValue('root')->end()
205
                ->scalarNode('password')->defaultNull()->end()
206
                ->scalarNode('application_name')->end()
207
                ->scalarNode('charset')->end()
208
                ->scalarNode('path')->end()
209
                ->booleanNode('memory')->end()
210
                ->scalarNode('unix_socket')->info('The unix socket to use for MySQL')->end()
211
                ->booleanNode('persistent')->info('True to use as persistent connection for the ibm_db2 driver')->end()
212
                ->scalarNode('protocol')->info('The protocol to use for the ibm_db2 driver (default to TCPIP if ommited)')->end()
213
                ->booleanNode('service')
214
                    ->info('True to use SERVICE_NAME as connection parameter instead of SID for Oracle')
215
                ->end()
216
                ->scalarNode('servicename')
217
                    ->info(
218
                        'Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter '.
219
                        'for Oracle depending on the service parameter.'
220
                    )
221
                ->end()
222
                ->scalarNode('sessionMode')
223
                    ->info('The session mode to use for the oci8 driver')
224
                ->end()
225
                ->scalarNode('server')
226
                    ->info('The name of a running database server to connect to for SQL Anywhere.')
227
                ->end()
228
                ->scalarNode('sslmode')
229
                    ->info(
230
                        'Determines whether or with what priority a SSL TCP/IP connection will be negotiated with '.
231
                        'the server for PostgreSQL.'
232
                    )
233
                ->end()
234
                ->scalarNode('sslrootcert')
235
                    ->info(
236
                        'The name of a file containing SSL certificate authority (CA) certificate(s). '.
237
                        'If the file exists, the server\'s certificate will be verified to be signed by one of these authorities.'
238
                    )
239
                ->end()
240
                ->booleanNode('pooled')->info('True to use a pooled server with the oci8/pdo_oracle driver')->end()
241
                ->booleanNode('MultipleActiveResultSets')->info('Configuring MultipleActiveResultSets for the pdo_sqlsrv driver')->end()
242
                ->booleanNode('use_savepoints')->info('Use savepoints for nested transactions')->end()
243
                ->scalarNode('instancename')
244
                ->info(
245
                    'Optional parameter, complete whether to add the INSTANCE_NAME parameter in the connection.'.
246
                    ' It is generally used to connect to an Oracle RAC server to select the name'.
247
                    ' of a particular instance.'
248
                )
249
                ->end()
250
                ->scalarNode('connectstring')
251
                ->info(
252
                    'Complete Easy Connect connection descriptor, see https://docs.oracle.com/database/121/NETAG/naming.htm.'.
253
                    'When using this option, you will still need to provide the user and password parameters, but the other '.
254
                    'parameters will no longer be used. Note that when using this parameter, the getHost and getPort methods'.
255
                    ' from Doctrine\DBAL\Connection will no longer function as expected.'
256
                )
257
                ->end()
258
            ->end()
259
            ->beforeNormalization()
260
                ->ifTrue(function ($v) {return !isset($v['sessionMode']) && isset($v['session_mode']);})
261
                ->then(function ($v) {
262
                    $v['sessionMode'] = $v['session_mode'];
263
                    unset($v['session_mode']);
264
265
                    return $v;
266
                })
267
            ->end()
268
            ->beforeNormalization()
269
                ->ifTrue(function ($v) {return !isset($v['MultipleActiveResultSets']) && isset($v['multiple_active_result_sets']);})
270
                ->then(function ($v) {
271
                    $v['MultipleActiveResultSets'] = $v['multiple_active_result_sets'];
272
                    unset($v['multiple_active_result_sets']);
273
274
                    return $v;
275
                })
276
            ->end()
277
        ;
278
    }
279
280
    /**
281
     * Add the ORM section to configuration tree
282
     *
283
     * @param ArrayNodeDefinition $node
284
     */
285
    private function addOrmSection(ArrayNodeDefinition $node)
286
    {
287
        $generationModes = $this->getAutoGenerateModes();
288
289
        $node
290
            ->children()
291
                ->arrayNode('orm')
292
                    ->beforeNormalization()
293
                        ->ifTrue(function ($v) { return null === $v || (is_array($v) && !array_key_exists('entity_managers', $v) && !array_key_exists('entity_manager', $v)); })
294
                        ->then(function ($v) {
295
                            $v = (array) $v;
296
                            // Key that should not be rewritten to the connection config
297
                            $excludedKeys = array(
298
                                'default_entity_manager' => true, 'auto_generate_proxy_classes' => true,
299
                                'proxy_dir' => true, 'proxy_namespace' => true, 'resolve_target_entities' => true,
300
                                'resolve_target_entity' => true,
301
                            );
302
                            $entityManager = array();
303 View Code Duplication
                            foreach ($v 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...
304
                                if (isset($excludedKeys[$key])) {
305
                                    continue;
306
                                }
307
                                $entityManager[$key] = $v[$key];
308
                                unset($v[$key]);
309
                            }
310
                            $v['default_entity_manager'] = isset($v['default_entity_manager']) ? (string) $v['default_entity_manager'] : 'default';
311
                            $v['entity_managers'] = array($v['default_entity_manager'] => $entityManager);
312
313
                            return $v;
314
                        })
315
                    ->end()
316
                    ->children()
317
                        ->scalarNode('default_entity_manager')->end()
318
                        ->scalarNode('auto_generate_proxy_classes')->defaultValue(false)
319
                            ->info('Auto generate mode possible values are: "NEVER", "ALWAYS", "FILE_NOT_EXISTS", "EVAL"')
320
                            ->validate()
321
                                ->ifTrue(function ($v) use ($generationModes) {
322
                                    if (is_int($v) && in_array($v, $generationModes['values']/*array(0, 1, 2, 3)*/)) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
77% 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...
323
                                        return false;
324
                                    }
325
                                    if (is_bool($v)) {
326
                                        return false;
327
                                    }
328
                                    if (is_string($v)) {
329
                                        if (in_array(strtoupper($v), $generationModes['names']/*array('NEVER', 'ALWAYS', 'FILE_NOT_EXISTS', 'EVAL')*/)) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
77% 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...
330
                                            return false;
331
                                        }
332
                                    }
333
334
                                    return true;
335
                                })
336
                                ->thenInvalid('Invalid auto generate mode value %s')
337
                            ->end()
338
                            ->validate()
339
                                ->ifString()
340
                                ->then(function ($v) {
341
                                    return constant('Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_'.strtoupper($v));
342
                                })
343
                            ->end()
344
                        ->end()
345
                        ->scalarNode('proxy_dir')->defaultValue('%kernel.cache_dir%/doctrine/orm/Proxies')->end()
346
                        ->scalarNode('proxy_namespace')->defaultValue('Proxies')->end()
347
                    ->end()
348
                    ->fixXmlConfig('entity_manager')
349
                    ->append($this->getOrmEntityManagersNode())
350
                    ->fixXmlConfig('resolve_target_entity', 'resolve_target_entities')
351
                    ->append($this->getOrmTargetEntityResolverNode())
352
                ->end()
353
            ->end()
354
        ;
355
    }
356
357
    /**
358
     * Return ORM target entity resolver node
359
     *
360
     * @return \Symfony\Component\Config\Definition\Builder\NodeDefinition
361
     */
362
    private function getOrmTargetEntityResolverNode()
363
    {
364
        $treeBuilder = new TreeBuilder();
365
        $node = $treeBuilder->root('resolve_target_entities');
366
367
        $node
368
            ->useAttributeAsKey('interface')
369
            ->prototype('scalar')
370
                ->cannotBeEmpty()
371
            ->end()
372
        ;
373
374
        return $node;
375
    }
376
377
    /**
378
     * Return ORM entity listener node
379
     *
380
     * @return \Symfony\Component\Config\Definition\Builder\NodeDefinition
381
     */
382
    private function getOrmEntityListenersNode()
383
    {
384
        $builder = new TreeBuilder();
385
        $node = $builder->root('entity_listeners');
386
        $normalizer = function ($mappings) {
387
            $entities = array();
388
389
            foreach ($mappings as $entityClass => $mapping) {
390
                $listeners = array();
391
392
                foreach ($mapping as $listenerClass => $listenerEvent) {
393
                    $events = array();
394
395
                    foreach ($listenerEvent as $eventType => $eventMapping) {
396
                        if ($eventMapping === null) {
397
                            $eventMapping = array(null);
398
                        }
399
400
                        foreach ($eventMapping as $method) {
401
                            $events[] = array(
402
                               'type' => $eventType,
403
                               'method' => $method,
404
                            );
405
                        }
406
                    }
407
408
                    $listeners[] = array(
409
                        'class' => $listenerClass,
410
                        'event' => $events,
411
                    );
412
                }
413
414
                $entities[] = array(
415
                    'class' => $entityClass,
416
                    'listener' => $listeners,
417
                );
418
            }
419
420
            return array('entities' => $entities);
421
        };
422
423
        $node
424
            ->beforeNormalization()
425
                // Yaml normalization
426
                ->ifTrue(function ($v) { return is_array(reset($v)) && is_string(key(reset($v))); })
427
                ->then($normalizer)
428
            ->end()
429
            ->fixXmlConfig('entity', 'entities')
430
            ->children()
431
                ->arrayNode('entities')
432
                    ->useAttributeAsKey('class')
433
                    ->prototype('array')
434
                        ->fixXmlConfig('listener')
435
                        ->children()
436
                            ->arrayNode('listeners')
437
                                ->useAttributeAsKey('class')
438
                                ->prototype('array')
439
                                    ->fixXmlConfig('event')
440
                                    ->children()
441
                                        ->arrayNode('events')
442
                                            ->prototype('array')
443
                                                ->children()
444
                                                    ->scalarNode('type')->end()
445
                                                    ->scalarNode('method')->defaultNull()->end()
446
                                                ->end()
447
                                            ->end()
448
                                        ->end()
449
                                    ->end()
450
                                ->end()
451
                            ->end()
452
                        ->end()
453
                    ->end()
454
                ->end()
455
            ->end()
456
        ;
457
458
        return $node;
459
    }
460
461
    /**
462
     * Return ORM entity manager node
463
     *
464
     * @return ArrayNodeDefinition
465
     */
466
    private function getOrmEntityManagersNode()
467
    {
468
        $treeBuilder = new TreeBuilder();
469
        $node = $treeBuilder->root('entity_managers');
470
471
        $node
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Config...\Builder\NodeDefinition as the method addDefaultsIfNotSet() does only exist in the following sub-classes of Symfony\Component\Config...\Builder\NodeDefinition: Symfony\Component\Config...der\ArrayNodeDefinition. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
472
            ->requiresAtLeastOneElement()
473
            ->useAttributeAsKey('name')
474
            ->prototype('array')
475
                ->addDefaultsIfNotSet()
476
                ->append($this->getOrmCacheDriverNode('query_cache_driver'))
477
                ->append($this->getOrmCacheDriverNode('metadata_cache_driver'))
478
                ->append($this->getOrmCacheDriverNode('result_cache_driver'))
479
                ->append($this->getOrmEntityListenersNode())
480
                ->children()
481
                    ->scalarNode('connection')->end()
482
                    ->scalarNode('class_metadata_factory_name')->defaultValue('Doctrine\ORM\Mapping\ClassMetadataFactory')->end()
483
                    ->scalarNode('default_repository_class')->defaultValue('Doctrine\ORM\EntityRepository')->end()
484
                    ->scalarNode('auto_mapping')->defaultFalse()->end()
485
                    ->scalarNode('naming_strategy')->defaultValue('doctrine.orm.naming_strategy.default')->end()
486
                    ->scalarNode('quote_strategy')->defaultValue('doctrine.orm.quote_strategy.default')->end()
487
                    ->scalarNode('entity_listener_resolver')->defaultNull()->end()
488
                    ->scalarNode('repository_factory')->defaultNull()->end()
489
                ->end()
490
                ->children()
491
                    ->arrayNode('second_level_cache')
492
                        ->children()
493
                            ->append($this->getOrmCacheDriverNode('region_cache_driver'))
494
                            ->scalarNode('region_lock_lifetime')->defaultValue(60)->end()
495
                            ->booleanNode('log_enabled')->defaultValue($this->debug)->end()
496
                            ->scalarNode('region_lifetime')->defaultValue(0)->end()
497
                            ->booleanNode('enabled')->defaultValue(true)->end()
498
                            ->scalarNode('factory')->end()
499
                        ->end()
500
                        ->fixXmlConfig('region')
501
                        ->children()
502
                            ->arrayNode('regions')
503
                                ->useAttributeAsKey('name')
504
                                ->prototype('array')
505
                                    ->children()
506
                                        ->append($this->getOrmCacheDriverNode('cache_driver'))
507
                                        ->scalarNode('lock_path')->defaultValue('%kernel.cache_dir%/doctrine/orm/slc/filelock')->end()
508
                                        ->scalarNode('lock_lifetime')->defaultValue(60)->end()
509
                                        ->scalarNode('type')->defaultValue('default')->end()
510
                                        ->scalarNode('lifetime')->defaultValue(0)->end()
511
                                        ->scalarNode('service')->end()
512
                                        ->scalarNode('name')->end()
513
                                    ->end()
514
                                ->end()
515
                            ->end()
516
                        ->end()
517
                        ->fixXmlConfig('logger')
518
                        ->children()
519
                            ->arrayNode('loggers')
520
                                ->useAttributeAsKey('name')
521
                                ->prototype('array')
522
                                    ->children()
523
                                        ->scalarNode('name')->end()
524
                                        ->scalarNode('service')->end()
525
                                    ->end()
526
                                ->end()
527
                            ->end()
528
                        ->end()
529
                    ->end()
530
                ->end()
531
                ->fixXmlConfig('hydrator')
532
                ->children()
533
                    ->arrayNode('hydrators')
534
                        ->useAttributeAsKey('name')
535
                        ->prototype('scalar')->end()
536
                    ->end()
537
                ->end()
538
                ->fixXmlConfig('mapping')
539
                ->children()
540
                    ->arrayNode('mappings')
541
                        ->useAttributeAsKey('name')
542
                        ->prototype('array')
543
                            ->beforeNormalization()
544
                                ->ifString()
545
                                ->then(function ($v) { return array('type' => $v); })
546
                            ->end()
547
                            ->treatNullLike(array())
548
                            ->treatFalseLike(array('mapping' => false))
549
                            ->performNoDeepMerging()
550
                            ->children()
551
                                ->scalarNode('mapping')->defaultValue(true)->end()
552
                                ->scalarNode('type')->end()
553
                                ->scalarNode('dir')->end()
554
                                ->scalarNode('alias')->end()
555
                                ->scalarNode('prefix')->end()
556
                                ->booleanNode('is_bundle')->end()
557
                            ->end()
558
                        ->end()
559
                    ->end()
560
                    ->arrayNode('dql')
561
                        ->fixXmlConfig('string_function')
562
                        ->fixXmlConfig('numeric_function')
563
                        ->fixXmlConfig('datetime_function')
564
                        ->children()
565
                            ->arrayNode('string_functions')
566
                                ->useAttributeAsKey('name')
567
                                ->prototype('scalar')->end()
568
                            ->end()
569
                            ->arrayNode('numeric_functions')
570
                                ->useAttributeAsKey('name')
571
                                ->prototype('scalar')->end()
572
                            ->end()
573
                            ->arrayNode('datetime_functions')
574
                                ->useAttributeAsKey('name')
575
                                ->prototype('scalar')->end()
576
                            ->end()
577
                        ->end()
578
                    ->end()
579
                ->end()
580
                ->fixXmlConfig('filter')
581
                ->children()
582
                    ->arrayNode('filters')
583
                        ->info('Register SQL Filters in the entity manager')
584
                        ->useAttributeAsKey('name')
585
                        ->prototype('array')
586
                            ->beforeNormalization()
587
                                ->ifString()
588
                                ->then(function ($v) { return array('class' => $v); })
589
                            ->end()
590
                            ->beforeNormalization()
591
                                // The content of the XML node is returned as the "value" key so we need to rename it
592
                                ->ifTrue(function ($v) {
593
                                    return is_array($v) && isset($v['value']);
594
                                })
595
                                ->then(function ($v) {
596
                                    $v['class'] = $v['value'];
597
                                    unset($v['value']);
598
599
                                    return $v;
600
                                })
601
                            ->end()
602
                            ->fixXmlConfig('parameter')
603
                            ->children()
604
                                ->scalarNode('class')->isRequired()->end()
605
                                ->booleanNode('enabled')->defaultFalse()->end()
606
                                ->arrayNode('parameters')
607
                                    ->useAttributeAsKey('name')
608
                                    ->prototype('variable')->end()
609
                                ->end()
610
                            ->end()
611
                        ->end()
612
                    ->end()
613
                ->end()
614
            ->end()
615
        ;
616
617
        return $node;
618
    }
619
620
    /**
621
     * Return a ORM cache driver node for an given entity manager
622
     *
623
     * @param string $name
624
     *
625
     * @return ArrayNodeDefinition
626
     */
627
    private function getOrmCacheDriverNode($name)
628
    {
629
        $treeBuilder = new TreeBuilder();
630
        $node = $treeBuilder->root($name);
631
632
        $node
633
            ->addDefaultsIfNotSet()
634
            ->beforeNormalization()
635
                ->ifString()
636
                ->then(function ($v) { return array('type' => $v); })
637
            ->end()
638
            ->children()
639
                ->scalarNode('type')->defaultValue('array')->end()
640
                ->scalarNode('host')->end()
641
                ->scalarNode('port')->end()
642
                ->scalarNode('instance_class')->end()
643
                ->scalarNode('class')->end()
644
                ->scalarNode('id')->end()
645
                ->scalarNode('namespace')->defaultNull()->end()
646
                ->scalarNode('cache_provider')->defaultNull()->end()
647
            ->end()
648
        ;
649
650
        return $node;
651
    }
652
653
    /**
654
     * Find proxy auto generate modes for their names and int values
655
     *
656
     * @return array
657
     */
658
    private function getAutoGenerateModes()
659
    {
660
        $constPrefix = 'AUTOGENERATE_';
661
        $prefixLen = strlen($constPrefix);
662
        $refClass = new \ReflectionClass('Doctrine\Common\Proxy\AbstractProxyFactory');
663
        $constsArray = $refClass->getConstants();
664
        $namesArray = array();
665
        $valuesArray = array();
666
667
        foreach ($constsArray as $key => $value) {
668
            if (strpos($key, $constPrefix) === 0) {
669
                $namesArray[] = substr($key, $prefixLen);
670
                $valuesArray[] = (int) $value;
671
            }
672
        }
673
674
        return array(
675
            'names' => $namesArray,
676
            'values' => $valuesArray,
677
        );
678
    }
679
}
680