Completed
Pull Request — master (#876)
by Kévin
02:02
created

Configuration   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 702
Duplicated Lines 1.99 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
wmc 44
lcom 1
cbo 6
dl 14
loc 702
rs 8.778
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getConfigTreeBuilder() 0 16 2
B addDbalSection() 7 51 6
B getDbalConnectionsNode() 0 75 2
B configureDbalDriverNode() 0 91 3
C addOrmSection() 7 75 12
A getOrmTargetEntityResolverNode() 0 19 2
B getOrmEntityListenersNode() 0 86 8
B getOrmEntityManagersNode() 0 162 3
A getOrmCacheDriverNode() 0 33 2
A getAutoGenerateModes() 0 23 3

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Configuration often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Configuration, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
4
5
use ReflectionClass;
6
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
7
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
8
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
9
use Symfony\Component\Config\Definition\ConfigurationInterface;
10
11
/**
12
 * This class contains the configuration information for the bundle
13
 *
14
 * This information is solely responsible for how the different configuration
15
 * sections are normalized, and merged.
16
 */
17
class Configuration implements ConfigurationInterface
18
{
19
    /** @var bool */
20
    private $debug;
21
22
    /**
23
     * Constructor
24
     *
25
     * @param bool $debug Whether to use the debug mode
26
     */
27
    public function __construct($debug)
28
    {
29
        $this->debug = (bool) $debug;
30
    }
31
32
    /**
33
     * {@inheritDoc}
34
     */
35
    public function getConfigTreeBuilder()
36
    {
37
        $treeBuilder = new TreeBuilder('doctrine');
0 ignored issues
show
Unused Code introduced by
The call to TreeBuilder::__construct() has too many arguments starting with 'doctrine'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
38
39
        if (method_exists($treeBuilder, 'getRootNode')) {
40
            $rootNode = $treeBuilder->getRootNode();
0 ignored issues
show
Bug introduced by
The method getRootNode() does not seem to exist on object<Symfony\Component...on\Builder\TreeBuilder>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
41
        } else {
42
            // BC layer for symfony/config 4.1 and older
43
            $rootNode = $treeBuilder->root('doctrine');
44
        }
45
46
        $this->addDbalSection($rootNode);
47
        $this->addOrmSection($rootNode);
48
49
        return $treeBuilder;
50
    }
51
52
    /**
53
     * Add DBAL section to configuration tree
54
     */
55
    private function addDbalSection(ArrayNodeDefinition $node)
56
    {
57
        $node
58
            ->children()
59
            ->arrayNode('dbal')
60
                ->beforeNormalization()
61
                    ->ifTrue(static function ($v) {
62
                        return is_array($v) && ! array_key_exists('connections', $v) && ! array_key_exists('connection', $v);
63
                    })
64
                    ->then(static function ($v) {
65
                        // Key that should not be rewritten to the connection config
66
                        $excludedKeys = ['default_connection' => true, 'types' => true, 'type' => true];
67
                        $connection   = [];
68 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...
69
                            if (isset($excludedKeys[$key])) {
70
                                continue;
71
                            }
72
                            $connection[$key] = $v[$key];
73
                            unset($v[$key]);
74
                        }
75
                        $v['default_connection'] = isset($v['default_connection']) ? (string) $v['default_connection'] : 'default';
76
                        $v['connections']        = [$v['default_connection'] => $connection];
77
78
                        return $v;
79
                    })
80
                ->end()
81
                ->children()
82
                    ->scalarNode('default_connection')->end()
83
                ->end()
84
                ->fixXmlConfig('type')
85
                ->children()
86
                    ->arrayNode('types')
87
                        ->useAttributeAsKey('name')
88
                        ->prototype('array')
89
                            ->beforeNormalization()
90
                                ->ifString()
91
                                ->then(static function ($v) {
92
                                    return ['class' => $v];
93
                                })
94
                            ->end()
95
                            ->children()
96
                                ->scalarNode('class')->isRequired()->end()
97
                                ->booleanNode('commented')->defaultTrue()->end()
98
                            ->end()
99
                        ->end()
100
                    ->end()
101
                ->end()
102
                ->fixXmlConfig('connection')
103
                ->append($this->getDbalConnectionsNode())
104
            ->end();
105
    }
106
107
    /**
108
     * Return the dbal connections node
109
     *
110
     * @return ArrayNodeDefinition
111
     */
112
    private function getDbalConnectionsNode()
113
    {
114
        $treeBuilder = new TreeBuilder('connections');
0 ignored issues
show
Unused Code introduced by
The call to TreeBuilder::__construct() has too many arguments starting with 'connections'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
115
116
        if (method_exists($treeBuilder, 'getRootNode')) {
117
            $node = $treeBuilder->getRootNode();
0 ignored issues
show
Bug introduced by
The method getRootNode() does not seem to exist on object<Symfony\Component...on\Builder\TreeBuilder>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
118
        } else {
119
            // BC layer for symfony/config 4.1 and older
120
            $node = $treeBuilder->root('connections');
121
        }
122
123
        /** @var ArrayNodeDefinition $connectionNode */
124
        $connectionNode = $node
125
            ->requiresAtLeastOneElement()
126
            ->useAttributeAsKey('name')
127
            ->prototype('array');
128
129
        $this->configureDbalDriverNode($connectionNode);
130
131
        $connectionNode
132
            ->fixXmlConfig('option')
133
            ->fixXmlConfig('mapping_type')
134
            ->fixXmlConfig('slave')
135
            ->fixXmlConfig('shard')
136
            ->fixXmlConfig('default_table_option')
137
            ->children()
138
                ->scalarNode('driver')->defaultValue('pdo_mysql')->end()
139
                ->scalarNode('platform_service')->end()
140
                ->booleanNode('auto_commit')->end()
141
                ->scalarNode('schema_filter')->end()
142
                ->booleanNode('logging')->defaultValue($this->debug)->end()
143
                ->booleanNode('profiling')->defaultValue($this->debug)->end()
144
                ->scalarNode('server_version')->end()
145
                ->scalarNode('driver_class')->end()
146
                ->scalarNode('wrapper_class')->end()
147
                ->scalarNode('shard_manager_class')->end()
148
                ->scalarNode('shard_choser')->end()
149
                ->scalarNode('shard_choser_service')->end()
150
                ->booleanNode('keep_slave')->end()
151
                ->arrayNode('options')
152
                    ->useAttributeAsKey('key')
153
                    ->prototype('scalar')->end()
154
                ->end()
155
                ->arrayNode('mapping_types')
156
                    ->useAttributeAsKey('name')
157
                    ->prototype('scalar')->end()
158
                ->end()
159
                ->arrayNode('default_table_options')
160
                    ->info("This option is used by the schema-tool and affects generated SQL. Possible keys include 'charset','collate', and 'engine'.")
161
                    ->useAttributeAsKey('name')
162
                    ->prototype('scalar')->end()
163
                ->end()
164
            ->end();
165
166
        $slaveNode = $connectionNode
167
            ->children()
168
                ->arrayNode('slaves')
169
                    ->useAttributeAsKey('name')
170
                    ->prototype('array');
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
        $this->configureDbalDriverNode($shardNode);
184
185
        return $node;
186
    }
187
188
    /**
189
     * Adds config keys related to params processed by the DBAL drivers
190
     *
191
     * These keys are available for slave configurations too.
192
     */
193
    private function configureDbalDriverNode(ArrayNodeDefinition $node)
194
    {
195
        $node
196
            ->children()
197
                ->scalarNode('url')->info('A URL with connection information; any parameter value parsed from this string will override explicitly set parameters')->end()
198
                ->scalarNode('dbname')->end()
199
                ->scalarNode('host')->defaultValue('localhost')->end()
200
                ->scalarNode('port')->defaultNull()->end()
201
                ->scalarNode('user')->defaultValue('root')->end()
202
                ->scalarNode('password')->defaultNull()->end()
203
                ->scalarNode('application_name')->end()
204
                ->scalarNode('charset')->end()
205
                ->scalarNode('path')->end()
206
                ->booleanNode('memory')->end()
207
                ->scalarNode('unix_socket')->info('The unix socket to use for MySQL')->end()
208
                ->booleanNode('persistent')->info('True to use as persistent connection for the ibm_db2 driver')->end()
209
                ->scalarNode('protocol')->info('The protocol to use for the ibm_db2 driver (default to TCPIP if ommited)')->end()
210
                ->booleanNode('service')
211
                    ->info('True to use SERVICE_NAME as connection parameter instead of SID for Oracle')
212
                ->end()
213
                ->scalarNode('servicename')
214
                    ->info(
215
                        'Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter ' .
216
                        'for Oracle depending on the service parameter.'
217
                    )
218
                ->end()
219
                ->scalarNode('sessionMode')
220
                    ->info('The session mode to use for the oci8 driver')
221
                ->end()
222
                ->scalarNode('server')
223
                    ->info('The name of a running database server to connect to for SQL Anywhere.')
224
                ->end()
225
                ->scalarNode('default_dbname')
226
                    ->info(
227
                        'Override the default database (postgres) to connect to for PostgreSQL connexion.'
228
                    )
229
                ->end()
230
                ->scalarNode('sslmode')
231
                    ->info(
232
                        'Determines whether or with what priority a SSL TCP/IP connection will be negotiated with ' .
233
                        'the server for PostgreSQL.'
234
                    )
235
                ->end()
236
                ->scalarNode('sslrootcert')
237
                    ->info(
238
                        'The name of a file containing SSL certificate authority (CA) certificate(s). ' .
239
                        'If the file exists, the server\'s certificate will be verified to be signed by one of these authorities.'
240
                    )
241
                ->end()
242
                ->booleanNode('pooled')->info('True to use a pooled server with the oci8/pdo_oracle driver')->end()
243
                ->booleanNode('MultipleActiveResultSets')->info('Configuring MultipleActiveResultSets for the pdo_sqlsrv driver')->end()
244
                ->booleanNode('use_savepoints')->info('Use savepoints for nested transactions')->end()
245
                ->scalarNode('instancename')
246
                ->info(
247
                    'Optional parameter, complete whether to add the INSTANCE_NAME parameter in the connection.' .
248
                    ' It is generally used to connect to an Oracle RAC server to select the name' .
249
                    ' of a particular instance.'
250
                )
251
                ->end()
252
                ->scalarNode('connectstring')
253
                ->info(
254
                    'Complete Easy Connect connection descriptor, see https://docs.oracle.com/database/121/NETAG/naming.htm.' .
255
                    'When using this option, you will still need to provide the user and password parameters, but the other ' .
256
                    'parameters will no longer be used. Note that when using this parameter, the getHost and getPort methods' .
257
                    ' from Doctrine\DBAL\Connection will no longer function as expected.'
258
                )
259
                ->end()
260
            ->end()
261
            ->beforeNormalization()
262
                ->ifTrue(static function ($v) {
263
                    return ! isset($v['sessionMode']) && isset($v['session_mode']);
264
                })
265
                ->then(static function ($v) {
266
                    $v['sessionMode'] = $v['session_mode'];
267
                    unset($v['session_mode']);
268
269
                    return $v;
270
                })
271
            ->end()
272
            ->beforeNormalization()
273
                ->ifTrue(static function ($v) {
274
                    return ! isset($v['MultipleActiveResultSets']) && isset($v['multiple_active_result_sets']);
275
                })
276
                ->then(static function ($v) {
277
                    $v['MultipleActiveResultSets'] = $v['multiple_active_result_sets'];
278
                    unset($v['multiple_active_result_sets']);
279
280
                    return $v;
281
                })
282
            ->end();
283
    }
284
285
    /**
286
     * Add the ORM section to configuration tree
287
     */
288
    private function addOrmSection(ArrayNodeDefinition $node)
289
    {
290
        $generationModes = $this->getAutoGenerateModes();
291
292
        $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 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...
293
            ->children()
294
                ->arrayNode('orm')
295
                    ->beforeNormalization()
296
                        ->ifTrue(static function ($v) {
297
                            return $v === null || (is_array($v) && ! array_key_exists('entity_managers', $v) && ! array_key_exists('entity_manager', $v));
298
                        })
299
                        ->then(static function ($v) {
300
                            $v = (array) $v;
301
                            // Key that should not be rewritten to the connection config
302
                            $excludedKeys  = [
303
                                'default_entity_manager' => true,
304
                                'auto_generate_proxy_classes' => true,
305
                                'proxy_dir' => true,
306
                                'proxy_namespace' => true,
307
                                'resolve_target_entities' => true,
308
                                'resolve_target_entity' => true,
309
                            ];
310
                            $entityManager = [];
311 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...
312
                                if (isset($excludedKeys[$key])) {
313
                                    continue;
314
                                }
315
                                $entityManager[$key] = $v[$key];
316
                                unset($v[$key]);
317
                            }
318
                            $v['default_entity_manager'] = isset($v['default_entity_manager']) ? (string) $v['default_entity_manager'] : 'default';
319
                            $v['entity_managers']        = [$v['default_entity_manager'] => $entityManager];
320
321
                            return $v;
322
                        })
323
                    ->end()
324
                    ->children()
325
                        ->scalarNode('default_entity_manager')->end()
326
                        ->scalarNode('auto_generate_proxy_classes')->defaultValue(false)
327
                            ->info('Auto generate mode possible values are: "NEVER", "ALWAYS", "FILE_NOT_EXISTS", "EVAL"')
328
                            ->validate()
329
                                ->ifTrue(static function ($v) use ($generationModes) {
330
                                    if (is_int($v) && in_array($v, $generationModes['values']/*array(0, 1, 2, 3)*/)) {
331
                                        return false;
332
                                    }
333
                                    if (is_bool($v)) {
334
                                        return false;
335
                                    }
336
                                    if (is_string($v)) {
337
                                        if (in_array(strtoupper($v), $generationModes['names']/*array('NEVER', 'ALWAYS', 'FILE_NOT_EXISTS', 'EVAL')*/)) {
338
                                            return false;
339
                                        }
340
                                    }
341
342
                                    return true;
343
                                })
344
                                ->thenInvalid('Invalid auto generate mode value %s')
345
                            ->end()
346
                            ->validate()
347
                                ->ifString()
348
                                ->then(static function ($v) {
349
                                    return constant('Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_' . strtoupper($v));
350
                                })
351
                            ->end()
352
                        ->end()
353
                        ->scalarNode('proxy_dir')->defaultValue('%kernel.cache_dir%/doctrine/orm/Proxies')->end()
354
                        ->scalarNode('proxy_namespace')->defaultValue('Proxies')->end()
355
                    ->end()
356
                    ->fixXmlConfig('entity_manager')
357
                    ->append($this->getOrmEntityManagersNode())
358
                    ->fixXmlConfig('resolve_target_entity', 'resolve_target_entities')
359
                    ->append($this->getOrmTargetEntityResolverNode())
360
                ->end()
361
            ->end();
362
    }
363
364
    /**
365
     * Return ORM target entity resolver node
366
     *
367
     * @return NodeDefinition
368
     */
369
    private function getOrmTargetEntityResolverNode()
370
    {
371
        $treeBuilder = new TreeBuilder('resolve_target_entities');
0 ignored issues
show
Unused Code introduced by
The call to TreeBuilder::__construct() has too many arguments starting with 'resolve_target_entities'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
372
373
        if (method_exists($treeBuilder, 'getRootNode')) {
374
            $node = $treeBuilder->getRootNode();
0 ignored issues
show
Bug introduced by
The method getRootNode() does not seem to exist on object<Symfony\Component...on\Builder\TreeBuilder>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
375
        } else {
376
            // BC layer for symfony/config 4.1 and older
377
            $node = $treeBuilder->root('resolve_target_entities');
378
        }
379
380
        $node
381
            ->useAttributeAsKey('interface')
382
            ->prototype('scalar')
383
                ->cannotBeEmpty()
384
            ->end();
385
386
        return $node;
387
    }
388
389
    /**
390
     * Return ORM entity listener node
391
     *
392
     * @return NodeDefinition
393
     */
394
    private function getOrmEntityListenersNode()
395
    {
396
        $treeBuilder = new TreeBuilder('entity_listeners');
0 ignored issues
show
Unused Code introduced by
The call to TreeBuilder::__construct() has too many arguments starting with 'entity_listeners'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
397
398
        if (method_exists($treeBuilder, 'getRootNode')) {
399
            $node = $treeBuilder->getRootNode();
0 ignored issues
show
Bug introduced by
The method getRootNode() does not seem to exist on object<Symfony\Component...on\Builder\TreeBuilder>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
400
        } else {
401
            // BC layer for symfony/config 4.1 and older
402
            $node = $treeBuilder->root('entity_listeners');
403
        }
404
405
        $normalizer = static function ($mappings) {
406
            $entities = [];
407
408
            foreach ($mappings as $entityClass => $mapping) {
409
                $listeners = [];
410
411
                foreach ($mapping as $listenerClass => $listenerEvent) {
412
                    $events = [];
413
414
                    foreach ($listenerEvent as $eventType => $eventMapping) {
415
                        if ($eventMapping === null) {
416
                            $eventMapping = [null];
417
                        }
418
419
                        foreach ($eventMapping as $method) {
420
                            $events[] = [
421
                                'type' => $eventType,
422
                                'method' => $method,
423
                            ];
424
                        }
425
                    }
426
427
                    $listeners[] = [
428
                        'class' => $listenerClass,
429
                        'event' => $events,
430
                    ];
431
                }
432
433
                $entities[] = [
434
                    'class' => $entityClass,
435
                    'listener' => $listeners,
436
                ];
437
            }
438
439
            return ['entities' => $entities];
440
        };
441
442
        $node
443
            ->beforeNormalization()
444
                // Yaml normalization
445
                ->ifTrue(static function ($v) {
446
                    return is_array(reset($v)) && is_string(key(reset($v)));
447
                })
448
                ->then($normalizer)
449
            ->end()
450
            ->fixXmlConfig('entity', 'entities')
451
            ->children()
452
                ->arrayNode('entities')
453
                    ->useAttributeAsKey('class')
454
                    ->prototype('array')
455
                        ->fixXmlConfig('listener')
456
                        ->children()
457
                            ->arrayNode('listeners')
458
                                ->useAttributeAsKey('class')
459
                                ->prototype('array')
460
                                    ->fixXmlConfig('event')
461
                                    ->children()
462
                                        ->arrayNode('events')
463
                                            ->prototype('array')
464
                                                ->children()
465
                                                    ->scalarNode('type')->end()
466
                                                    ->scalarNode('method')->defaultNull()->end()
467
                                                ->end()
468
                                            ->end()
469
                                        ->end()
470
                                    ->end()
471
                                ->end()
472
                            ->end()
473
                        ->end()
474
                    ->end()
475
                ->end()
476
            ->end();
477
478
        return $node;
479
    }
480
481
    /**
482
     * Return ORM entity manager node
483
     *
484
     * @return ArrayNodeDefinition
485
     */
486
    private function getOrmEntityManagersNode()
487
    {
488
        $treeBuilder = new TreeBuilder('entity_managers');
0 ignored issues
show
Unused Code introduced by
The call to TreeBuilder::__construct() has too many arguments starting with 'entity_managers'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
489
490
        if (method_exists($treeBuilder, 'getRootNode')) {
491
            $node = $treeBuilder->getRootNode();
0 ignored issues
show
Bug introduced by
The method getRootNode() does not seem to exist on object<Symfony\Component...on\Builder\TreeBuilder>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
492
        } else {
493
            // BC layer for symfony/config 4.1 and older
494
            $node = $treeBuilder->root('entity_managers');
495
        }
496
497
        $node
498
            ->requiresAtLeastOneElement()
499
            ->useAttributeAsKey('name')
500
            ->prototype('array')
501
                ->addDefaultsIfNotSet()
502
                ->append($this->getOrmCacheDriverNode('query_cache_driver'))
503
                ->append($this->getOrmCacheDriverNode('metadata_cache_driver'))
504
                ->append($this->getOrmCacheDriverNode('result_cache_driver'))
505
                ->append($this->getOrmEntityListenersNode())
506
                ->children()
507
                    ->scalarNode('connection')->end()
508
                    ->scalarNode('class_metadata_factory_name')->defaultValue('Doctrine\ORM\Mapping\ClassMetadataFactory')->end()
509
                    ->scalarNode('default_repository_class')->defaultValue('Doctrine\ORM\EntityRepository')->end()
510
                    ->scalarNode('auto_mapping')->defaultFalse()->end()
511
                    ->scalarNode('naming_strategy')->defaultValue('doctrine.orm.naming_strategy.default')->end()
512
                    ->scalarNode('quote_strategy')->defaultValue('doctrine.orm.quote_strategy.default')->end()
513
                    ->scalarNode('entity_listener_resolver')->defaultNull()->end()
514
                    ->scalarNode('repository_factory')->defaultValue('doctrine.orm.container_repository_factory')->end()
515
                ->end()
516
                ->children()
517
                    ->arrayNode('second_level_cache')
518
                        ->children()
519
                            ->append($this->getOrmCacheDriverNode('region_cache_driver'))
520
                            ->scalarNode('region_lock_lifetime')->defaultValue(60)->end()
521
                            ->booleanNode('log_enabled')->defaultValue($this->debug)->end()
522
                            ->scalarNode('region_lifetime')->defaultValue(0)->end()
523
                            ->booleanNode('enabled')->defaultValue(true)->end()
524
                            ->scalarNode('factory')->end()
525
                        ->end()
526
                        ->fixXmlConfig('region')
527
                        ->children()
528
                            ->arrayNode('regions')
529
                                ->useAttributeAsKey('name')
530
                                ->prototype('array')
531
                                    ->children()
532
                                        ->append($this->getOrmCacheDriverNode('cache_driver'))
533
                                        ->scalarNode('lock_path')->defaultValue('%kernel.cache_dir%/doctrine/orm/slc/filelock')->end()
534
                                        ->scalarNode('lock_lifetime')->defaultValue(60)->end()
535
                                        ->scalarNode('type')->defaultValue('default')->end()
536
                                        ->scalarNode('lifetime')->defaultValue(0)->end()
537
                                        ->scalarNode('service')->end()
538
                                        ->scalarNode('name')->end()
539
                                    ->end()
540
                                ->end()
541
                            ->end()
542
                        ->end()
543
                        ->fixXmlConfig('logger')
544
                        ->children()
545
                            ->arrayNode('loggers')
546
                                ->useAttributeAsKey('name')
547
                                ->prototype('array')
548
                                    ->children()
549
                                        ->scalarNode('name')->end()
550
                                        ->scalarNode('service')->end()
551
                                    ->end()
552
                                ->end()
553
                            ->end()
554
                        ->end()
555
                    ->end()
556
                ->end()
557
                ->fixXmlConfig('hydrator')
558
                ->children()
559
                    ->arrayNode('hydrators')
560
                        ->useAttributeAsKey('name')
561
                        ->prototype('scalar')->end()
562
                    ->end()
563
                ->end()
564
                ->fixXmlConfig('mapping')
565
                ->children()
566
                    ->arrayNode('mappings')
567
                        ->useAttributeAsKey('name')
568
                        ->prototype('array')
569
                            ->beforeNormalization()
570
                                ->ifString()
571
                                ->then(static function ($v) {
572
                                    return ['type' => $v];
573
                                })
574
                            ->end()
575
                            ->treatNullLike([])
576
                            ->treatFalseLike(['mapping' => false])
577
                            ->performNoDeepMerging()
578
                            ->children()
579
                                ->scalarNode('mapping')->defaultValue(true)->end()
580
                                ->scalarNode('type')->end()
581
                                ->scalarNode('dir')->end()
582
                                ->scalarNode('alias')->end()
583
                                ->scalarNode('prefix')->end()
584
                                ->booleanNode('is_bundle')->end()
585
                            ->end()
586
                        ->end()
587
                    ->end()
588
                    ->arrayNode('dql')
589
                        ->fixXmlConfig('string_function')
590
                        ->fixXmlConfig('numeric_function')
591
                        ->fixXmlConfig('datetime_function')
592
                        ->children()
593
                            ->arrayNode('string_functions')
594
                                ->useAttributeAsKey('name')
595
                                ->prototype('scalar')->end()
596
                            ->end()
597
                            ->arrayNode('numeric_functions')
598
                                ->useAttributeAsKey('name')
599
                                ->prototype('scalar')->end()
600
                            ->end()
601
                            ->arrayNode('datetime_functions')
602
                                ->useAttributeAsKey('name')
603
                                ->prototype('scalar')->end()
604
                            ->end()
605
                        ->end()
606
                    ->end()
607
                ->end()
608
                ->fixXmlConfig('filter')
609
                ->children()
610
                    ->arrayNode('filters')
611
                        ->info('Register SQL Filters in the entity manager')
612
                        ->useAttributeAsKey('name')
613
                        ->prototype('array')
614
                            ->beforeNormalization()
615
                                ->ifString()
616
                                ->then(static function ($v) {
617
                                    return ['class' => $v];
618
                                })
619
                            ->end()
620
                            ->beforeNormalization()
621
                                // The content of the XML node is returned as the "value" key so we need to rename it
622
                                ->ifTrue(static function ($v) {
623
                                    return is_array($v) && isset($v['value']);
624
                                })
625
                                ->then(static function ($v) {
626
                                    $v['class'] = $v['value'];
627
                                    unset($v['value']);
628
629
                                    return $v;
630
                                })
631
                            ->end()
632
                            ->fixXmlConfig('parameter')
633
                            ->children()
634
                                ->scalarNode('class')->isRequired()->end()
635
                                ->booleanNode('enabled')->defaultFalse()->end()
636
                                ->arrayNode('parameters')
637
                                    ->useAttributeAsKey('name')
638
                                    ->prototype('variable')->end()
639
                                ->end()
640
                            ->end()
641
                        ->end()
642
                    ->end()
643
                ->end()
644
            ->end();
645
646
        return $node;
647
    }
648
649
    /**
650
     * Return a ORM cache driver node for an given entity manager
651
     *
652
     * @param string $name
653
     *
654
     * @return ArrayNodeDefinition
655
     */
656
    private function getOrmCacheDriverNode($name)
657
    {
658
        $treeBuilder = new TreeBuilder($name);
0 ignored issues
show
Unused Code introduced by
The call to TreeBuilder::__construct() has too many arguments starting with $name.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
659
660
        if (method_exists($treeBuilder, 'getRootNode')) {
661
            $node = $treeBuilder->getRootNode();
0 ignored issues
show
Bug introduced by
The method getRootNode() does not seem to exist on object<Symfony\Component...on\Builder\TreeBuilder>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
662
        } else {
663
            // BC layer for symfony/config 4.1 and older
664
            $node = $treeBuilder->root($name);
665
        }
666
667
        $node
668
            ->addDefaultsIfNotSet()
669
            ->beforeNormalization()
670
                ->ifString()
671
                ->then(static function ($v) {
672
                    return ['type' => $v];
673
                })
674
            ->end()
675
            ->children()
676
                ->scalarNode('type')->defaultValue('array')->end()
677
                ->scalarNode('host')->end()
678
                ->scalarNode('port')->end()
679
                ->scalarNode('database')->end()
680
                ->scalarNode('instance_class')->end()
681
                ->scalarNode('class')->end()
682
                ->scalarNode('id')->end()
683
                ->scalarNode('namespace')->defaultNull()->end()
684
                ->scalarNode('cache_provider')->defaultNull()->end()
685
            ->end();
686
687
        return $node;
688
    }
689
690
    /**
691
     * Find proxy auto generate modes for their names and int values
692
     *
693
     * @return array
694
     */
695
    private function getAutoGenerateModes()
696
    {
697
        $constPrefix = 'AUTOGENERATE_';
698
        $prefixLen   = strlen($constPrefix);
699
        $refClass    = new ReflectionClass('Doctrine\Common\Proxy\AbstractProxyFactory');
700
        $constsArray = $refClass->getConstants();
701
        $namesArray  = [];
702
        $valuesArray = [];
703
704
        foreach ($constsArray as $key => $value) {
705
            if (strpos($key, $constPrefix) !== 0) {
706
                continue;
707
            }
708
709
            $namesArray[]  = substr($key, $prefixLen);
710
            $valuesArray[] = (int) $value;
711
        }
712
713
        return [
714
            'names' => $namesArray,
715
            'values' => $valuesArray,
716
        ];
717
    }
718
}
719