Completed
Pull Request — master (#528)
by
unknown
03:31
created

Configuration::getConfigTreeBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 10
rs 9.4285
cc 1
eloc 6
nc 1
nop 0
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
            ->end()
244
            ->beforeNormalization()
245
                ->ifTrue(function ($v) {return !isset($v['sessionMode']) && isset($v['session_mode']);})
246
                ->then(function ($v) {
247
                    $v['sessionMode'] = $v['session_mode'];
248
                    unset($v['session_mode']);
249
250
                    return $v;
251
                })
252
            ->end()
253
            ->beforeNormalization()
254
                ->ifTrue(function ($v) {return !isset($v['MultipleActiveResultSets']) && isset($v['multiple_active_result_sets']);})
255
                ->then(function ($v) {
256
                    $v['MultipleActiveResultSets'] = $v['multiple_active_result_sets'];
257
                    unset($v['multiple_active_result_sets']);
258
259
                    return $v;
260
                })
261
            ->end()
262
        ;
263
    }
264
265
    /**
266
     * Add the ORM section to configuration tree
267
     *
268
     * @param ArrayNodeDefinition $node
269
     */
270
    private function addOrmSection(ArrayNodeDefinition $node)
271
    {
272
        $generationModes = $this->getAutoGenerateModes();
273
274
        $node
275
            ->children()
276
                ->arrayNode('orm')
277
                    ->beforeNormalization()
278
                        ->ifTrue(function ($v) { return null === $v || (is_array($v) && !array_key_exists('entity_managers', $v) && !array_key_exists('entity_manager', $v)); })
279
                        ->then(function ($v) {
280
                            $v = (array) $v;
281
                            // Key that should not be rewritten to the connection config
282
                            $excludedKeys = array(
283
                                'default_entity_manager' => true, 'auto_generate_proxy_classes' => true,
284
                                'proxy_dir' => true, 'proxy_namespace' => true, 'resolve_target_entities' => true,
285
                                'resolve_target_entity' => true,
286
                            );
287
                            $entityManager = array();
288 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...
289
                                if (isset($excludedKeys[$key])) {
290
                                    continue;
291
                                }
292
                                $entityManager[$key] = $v[$key];
293
                                unset($v[$key]);
294
                            }
295
                            $v['default_entity_manager'] = isset($v['default_entity_manager']) ? (string) $v['default_entity_manager'] : 'default';
296
                            $v['entity_managers'] = array($v['default_entity_manager'] => $entityManager);
297
298
                            return $v;
299
                        })
300
                    ->end()
301
                    ->children()
302
                        ->scalarNode('default_entity_manager')->end()
303
                        ->scalarNode('auto_generate_proxy_classes')->defaultValue(false)
304
                            ->info('Auto generate mode possible values are: "NEVER", "ALWAYS", "FILE_NOT_EXISTS", "EVAL"')
305
                            ->validate()
306
                                ->ifTrue(function ($v) use ($generationModes) {
307
                                    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...
308
                                        return false;
309
                                    }
310
                                    if (is_bool($v)) {
311
                                        return false;
312
                                    }
313
                                    if (is_string($v)) {
314
                                        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...
315
                                            return false;
316
                                        }
317
                                    }
318
319
                                    return true;
320
                                })
321
                                ->thenInvalid('Invalid auto generate mode value %s')
322
                            ->end()
323
                            ->validate()
324
                                ->ifString()
325
                                ->then(function ($v) {
326
                                    return constant('Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_'.strtoupper($v));
327
                                })
328
                            ->end()
329
                        ->end()
330
                        ->scalarNode('proxy_dir')->defaultValue('%kernel.cache_dir%/doctrine/orm/Proxies')->end()
331
                        ->scalarNode('proxy_namespace')->defaultValue('Proxies')->end()
332
                    ->end()
333
                    ->fixXmlConfig('entity_manager')
334
                    ->append($this->getOrmEntityManagersNode())
335
                    ->fixXmlConfig('resolve_target_entity', 'resolve_target_entities')
336
                    ->append($this->getOrmTargetEntityResolverNode())
337
                ->end()
338
            ->end()
339
        ;
340
    }
341
342
    /**
343
     * Return ORM target entity resolver node
344
     *
345
     * @return \Symfony\Component\Config\Definition\Builder\NodeDefinition
346
     */
347
    private function getOrmTargetEntityResolverNode()
348
    {
349
        $treeBuilder = new TreeBuilder();
350
        $node = $treeBuilder->root('resolve_target_entities');
351
352
        $node
353
            ->useAttributeAsKey('interface')
354
            ->prototype('scalar')
355
                ->cannotBeEmpty()
356
            ->end()
357
        ;
358
359
        return $node;
360
    }
361
362
    /**
363
     * Return ORM entity listener node
364
     *
365
     * @return \Symfony\Component\Config\Definition\Builder\NodeDefinition
366
     */
367
    private function getOrmEntityListenersNode()
368
    {
369
        $builder = new TreeBuilder();
370
        $node = $builder->root('entity_listeners');
371
        $normalizer = function ($mappings) {
372
            $entities = array();
373
374
            foreach ($mappings as $entityClass => $mapping) {
375
                $listeners = array();
376
377
                foreach ($mapping as $listenerClass => $listenerEvent) {
378
                    $events = array();
379
380
                    foreach ($listenerEvent as $eventType => $eventMapping) {
381
                        if ($eventMapping === null) {
382
                            $eventMapping = array(null);
383
                        }
384
385
                        foreach ($eventMapping as $method) {
386
                            $events[] = array(
387
                               'type' => $eventType,
388
                               'method' => $method,
389
                            );
390
                        }
391
                    }
392
393
                    $listeners[] = array(
394
                        'class' => $listenerClass,
395
                        'event' => $events,
396
                    );
397
                }
398
399
                $entities[] = array(
400
                    'class' => $entityClass,
401
                    'listener' => $listeners,
402
                );
403
            }
404
405
            return array('entities' => $entities);
406
        };
407
408
        $node
409
            ->beforeNormalization()
410
                // Yaml normalization
411
                ->ifTrue(function ($v) { return is_array(reset($v)) && is_string(key(reset($v))); })
412
                ->then($normalizer)
413
            ->end()
414
            ->fixXmlConfig('entity', 'entities')
415
            ->children()
416
                ->arrayNode('entities')
417
                    ->useAttributeAsKey('class')
418
                    ->prototype('array')
419
                        ->fixXmlConfig('listener')
420
                        ->children()
421
                            ->arrayNode('listeners')
422
                                ->useAttributeAsKey('class')
423
                                ->prototype('array')
424
                                    ->fixXmlConfig('event')
425
                                    ->children()
426
                                        ->arrayNode('events')
427
                                            ->prototype('array')
428
                                                ->children()
429
                                                    ->scalarNode('type')->end()
430
                                                    ->scalarNode('method')->defaultNull()->end()
431
                                                ->end()
432
                                            ->end()
433
                                        ->end()
434
                                    ->end()
435
                                ->end()
436
                            ->end()
437
                        ->end()
438
                    ->end()
439
                ->end()
440
            ->end()
441
        ;
442
443
        return $node;
444
    }
445
446
    /**
447
     * Return ORM entity manager node
448
     *
449
     * @return ArrayNodeDefinition
450
     */
451
    private function getOrmEntityManagersNode()
452
    {
453
        $treeBuilder = new TreeBuilder();
454
        $node = $treeBuilder->root('entity_managers');
455
456
        $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...
457
            ->requiresAtLeastOneElement()
458
            ->useAttributeAsKey('name')
459
            ->prototype('array')
460
                ->addDefaultsIfNotSet()
461
                ->append($this->getOrmCacheDriverNode('query_cache_driver'))
462
                ->append($this->getOrmCacheDriverNode('metadata_cache_driver'))
463
                ->append($this->getOrmCacheDriverNode('result_cache_driver'))
464
                ->append($this->getOrmEntityListenersNode())
465
                ->children()
466
                    ->scalarNode('connection')->end()
467
                    ->scalarNode('class_metadata_factory_name')->defaultValue('Doctrine\ORM\Mapping\ClassMetadataFactory')->end()
468
                    ->scalarNode('default_repository_class')->defaultValue('Doctrine\ORM\EntityRepository')->end()
469
                    ->scalarNode('auto_mapping')->defaultFalse()->end()
470
                    ->scalarNode('naming_strategy')->defaultValue('doctrine.orm.naming_strategy.default')->end()
471
                    ->scalarNode('quote_strategy')->defaultValue('doctrine.orm.quote_strategy.default')->end()
472
                    ->scalarNode('entity_listener_resolver')->defaultNull()->end()
473
                    ->scalarNode('repository_factory')->defaultNull()->end()
474
                ->end()
475
                ->children()
476
                    ->arrayNode('second_level_cache')
477
                        ->children()
478
                            ->append($this->getOrmCacheDriverNode('region_cache_driver'))
479
                            ->scalarNode('region_lock_lifetime')->defaultValue(60)->end()
480
                            ->booleanNode('log_enabled')->defaultValue($this->debug)->end()
481
                            ->scalarNode('region_lifetime')->defaultValue(0)->end()
482
                            ->booleanNode('enabled')->defaultValue(true)->end()
483
                            ->scalarNode('factory')->end()
484
                        ->end()
485
                        ->fixXmlConfig('region')
486
                        ->children()
487
                            ->arrayNode('regions')
488
                                ->useAttributeAsKey('name')
489
                                ->prototype('array')
490
                                    ->children()
491
                                        ->append($this->getOrmCacheDriverNode('cache_driver'))
492
                                        ->scalarNode('lock_path')->defaultValue('%kernel.cache_dir%/doctrine/orm/slc/filelock')->end()
493
                                        ->scalarNode('lock_lifetime')->defaultValue(60)->end()
494
                                        ->scalarNode('type')->defaultValue('default')->end()
495
                                        ->scalarNode('lifetime')->defaultValue(0)->end()
496
                                        ->scalarNode('service')->end()
497
                                        ->scalarNode('name')->end()
498
                                    ->end()
499
                                ->end()
500
                            ->end()
501
                        ->end()
502
                        ->fixXmlConfig('logger')
503
                        ->children()
504
                            ->arrayNode('loggers')
505
                                ->useAttributeAsKey('name')
506
                                ->prototype('array')
507
                                    ->children()
508
                                        ->scalarNode('name')->end()
509
                                        ->scalarNode('service')->end()
510
                                    ->end()
511
                                ->end()
512
                            ->end()
513
                        ->end()
514
                    ->end()
515
                ->end()
516
                ->fixXmlConfig('hydrator')
517
                ->children()
518
                    ->arrayNode('hydrators')
519
                        ->useAttributeAsKey('name')
520
                        ->prototype('scalar')->end()
521
                    ->end()
522
                ->end()
523
                ->fixXmlConfig('mapping')
524
                ->children()
525
                    ->arrayNode('mappings')
526
                        ->useAttributeAsKey('name')
527
                        ->prototype('array')
528
                            ->beforeNormalization()
529
                                ->ifString()
530
                                ->then(function ($v) { return array('type' => $v); })
531
                            ->end()
532
                            ->treatNullLike(array())
533
                            ->treatFalseLike(array('mapping' => false))
534
                            ->performNoDeepMerging()
535
                            ->children()
536
                                ->scalarNode('mapping')->defaultValue(true)->end()
537
                                ->scalarNode('type')->end()
538
                                ->scalarNode('dir')->end()
539
                                ->scalarNode('alias')->end()
540
                                ->scalarNode('prefix')->end()
541
                                ->booleanNode('is_bundle')->end()
542
                            ->end()
543
                        ->end()
544
                    ->end()
545
                    ->arrayNode('dql')
546
                        ->fixXmlConfig('string_function')
547
                        ->fixXmlConfig('numeric_function')
548
                        ->fixXmlConfig('datetime_function')
549
                        ->children()
550
                            ->arrayNode('string_functions')
551
                                ->useAttributeAsKey('name')
552
                                ->prototype('scalar')->end()
553
                            ->end()
554
                            ->arrayNode('numeric_functions')
555
                                ->useAttributeAsKey('name')
556
                                ->prototype('scalar')->end()
557
                            ->end()
558
                            ->arrayNode('datetime_functions')
559
                                ->useAttributeAsKey('name')
560
                                ->prototype('scalar')->end()
561
                            ->end()
562
                        ->end()
563
                    ->end()
564
                ->end()
565
                ->fixXmlConfig('filter')
566
                ->children()
567
                    ->arrayNode('filters')
568
                        ->info('Register SQL Filters in the entity manager')
569
                        ->useAttributeAsKey('name')
570
                        ->prototype('array')
571
                            ->beforeNormalization()
572
                                ->ifString()
573
                                ->then(function ($v) { return array('class' => $v); })
574
                            ->end()
575
                            ->beforeNormalization()
576
                                // The content of the XML node is returned as the "value" key so we need to rename it
577
                                ->ifTrue(function ($v) {
578
                                    return is_array($v) && isset($v['value']);
579
                                })
580
                                ->then(function ($v) {
581
                                    $v['class'] = $v['value'];
582
                                    unset($v['value']);
583
584
                                    return $v;
585
                                })
586
                            ->end()
587
                            ->fixXmlConfig('parameter')
588
                            ->children()
589
                                ->scalarNode('class')->isRequired()->end()
590
                                ->booleanNode('enabled')->defaultFalse()->end()
591
                                ->arrayNode('parameters')
592
                                    ->useAttributeAsKey('name')
593
                                    ->prototype('variable')->end()
594
                                ->end()
595
                            ->end()
596
                        ->end()
597
                    ->end()
598
                ->end()
599
            ->end()
600
        ;
601
602
        return $node;
603
    }
604
605
    /**
606
     * Return a ORM cache driver node for an given entity manager
607
     *
608
     * @param string $name
609
     *
610
     * @return ArrayNodeDefinition
611
     */
612
    private function getOrmCacheDriverNode($name)
613
    {
614
        $treeBuilder = new TreeBuilder();
615
        $node = $treeBuilder->root($name);
616
617
        $node
618
            ->addDefaultsIfNotSet()
619
            ->beforeNormalization()
620
                ->ifString()
621
                ->then(function ($v) { return array('type' => $v); })
622
            ->end()
623
            ->children()
624
                ->scalarNode('type')->defaultValue('array')->end()
625
                ->scalarNode('host')->end()
626
                ->scalarNode('port')->end()
627
                ->scalarNode('instance_class')->end()
628
                ->scalarNode('class')->end()
629
                ->scalarNode('id')->end()
630
                ->scalarNode('namespace')->defaultNull()->end()
631
                ->scalarNode('cache_provider')->defaultNull()->end()
632
            ->end()
633
        ;
634
635
        return $node;
636
    }
637
638
    /**
639
     * Find proxy auto generate modes for their names and int values
640
     *
641
     * @return array
642
     */
643
    private function getAutoGenerateModes()
644
    {
645
        $constPrefix = 'AUTOGENERATE_';
646
        $prefixLen = strlen($constPrefix);
647
        $refClass = new \ReflectionClass('Doctrine\Common\Proxy\AbstractProxyFactory');
648
        $constsArray = $refClass->getConstants();
649
        $namesArray = array();
650
        $valuesArray = array();
651
652
        foreach ($constsArray as $key => $value) {
653
            if (strpos($key, $constPrefix) === 0) {
654
                $namesArray[] = substr($key, $prefixLen);
655
                $valuesArray[] = (int) $value;
656
            }
657
        }
658
659
        return array(
660
            'names' => $namesArray,
661
            'values' => $valuesArray,
662
        );
663
    }
664
}
665