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

Configuration   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 728
Duplicated Lines 1.92 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 39
c 2
b 0
f 0
lcom 1
cbo 6
dl 14
loc 728
rs 8.2

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getConfigTreeBuilder() 0 10 1
B addDbalSection() 7 48 6
B configureDbalDriverNode() 0 83 3
A getDbalConnectionsNode() 0 73 1
C addOrmSection() 7 72 12
A getOrmSecondLevelCacheNode() 0 69 1
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_manager_class')->end()
147
                ->scalarNode('shard_choser')->end()
148
                ->scalarNode('shard_choser_service')->end()
149
                ->booleanNode('keep_slave')->end()
150
                ->arrayNode('options')
151
                    ->useAttributeAsKey('key')
152
                    ->prototype('scalar')->end()
153
                ->end()
154
                ->arrayNode('mapping_types')
155
                    ->useAttributeAsKey('name')
156
                    ->prototype('scalar')->end()
157
                ->end()
158
                ->arrayNode('default_table_options')
159
                    ->info("This option is used by the schema-tool and affects generated SQL. Possible keys include 'charset','collate', and 'engine'.")
160
                    ->useAttributeAsKey('name')
161
                    ->prototype('scalar')->end()
162
                ->end()
163
            ->end()
164
        ;
165
166
        $slaveNode = $connectionNode
167
            ->children()
168
                ->arrayNode('slaves')
169
                    ->useAttributeAsKey('name')
170
                    ->prototype('array')
171
        ;
172
        $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...
173
174
        $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...
175
            ->children()
176
                ->arrayNode('shards')
177
                    ->prototype('array')
178
                    ->children()
179
                        ->integerNode('id')
180
                            ->min(1)
181
                            ->isRequired()
182
                        ->end()
183
                    ->end()
184
        ;
185
        $this->configureDbalDriverNode($shardNode);
186
187
        return $node;
188
    }
189
190
    /**
191
     * Adds config keys related to params processed by the DBAL drivers
192
     *
193
     * These keys are available for slave configurations too.
194
     *
195
     * @param ArrayNodeDefinition $node
196
     */
197
    private function configureDbalDriverNode(ArrayNodeDefinition $node)
198
    {
199
        $node
200
            ->children()
201
                ->scalarNode('url')->info('A URL with connection information; any parameter value parsed from this string will override explicitly set parameters')->end()
202
                ->scalarNode('dbname')->end()
203
                ->scalarNode('host')->defaultValue('localhost')->end()
204
                ->scalarNode('port')->defaultNull()->end()
205
                ->scalarNode('user')->defaultValue('root')->end()
206
                ->scalarNode('password')->defaultNull()->end()
207
                ->scalarNode('application_name')->end()
208
                ->scalarNode('charset')->end()
209
                ->scalarNode('path')->end()
210
                ->booleanNode('memory')->end()
211
                ->scalarNode('unix_socket')->info('The unix socket to use for MySQL')->end()
212
                ->booleanNode('persistent')->info('True to use as persistent connection for the ibm_db2 driver')->end()
213
                ->scalarNode('protocol')->info('The protocol to use for the ibm_db2 driver (default to TCPIP if ommited)')->end()
214
                ->booleanNode('service')
215
                    ->info('True to use SERVICE_NAME as connection parameter instead of SID for Oracle')
216
                ->end()
217
                ->scalarNode('servicename')
218
                    ->info(
219
                        'Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter '.
220
                        'for Oracle depending on the service parameter.'
221
                    )
222
                ->end()
223
                ->scalarNode('sessionMode')
224
                    ->info('The session mode to use for the oci8 driver')
225
                ->end()
226
                ->scalarNode('server')
227
                    ->info('The name of a running database server to connect to for SQL Anywhere.')
228
                ->end()
229
                ->scalarNode('sslmode')
230
                    ->info(
231
                        'Determines whether or with what priority a SSL TCP/IP connection will be negotiated with '.
232
                        'the server for PostgreSQL.'
233
                    )
234
                ->end()
235
                ->scalarNode('sslrootcert')
236
                    ->info(
237
                        'The name of a file containing SSL certificate authority (CA) certificate(s). '.
238
                        'If the file exists, the server\'s certificate will be verified to be signed by one of these authorities.'
239
                    )
240
                ->end()
241
                ->booleanNode('pooled')->info('True to use a pooled server with the oci8/pdo_oracle driver')->end()
242
                ->booleanNode('MultipleActiveResultSets')->info('Configuring MultipleActiveResultSets for the pdo_sqlsrv driver')->end()
243
                ->booleanNode('use_savepoints')->info('Use savepoints for nested transactions')->end()
244
                ->scalarNode('instancename')
245
                ->info(
246
                    'Optional parameter, complete whether to add the INSTANCE_NAME parameter in the connection.'.
247
                    ' It is generally used to connect to an Oracle RAC server to select the name'.
248
                    ' of a particular instance.'
249
                )
250
                ->end()
251
                ->scalarNode('connectstring')
252
                ->info(
253
                    'Complete Easy Connect connection descriptor, see https://docs.oracle.com/database/121/NETAG/naming.htm.'.
254
                    'When using this option, you will still need to provide the user and password parameters, but the other '.
255
                    'parameters will no longer be used. Note that when using this parameter, the getHost and getPort methods'.
256
                    ' from Doctrine\DBAL\Connection will no longer function as expected.'
257
                )
258
                ->end()
259
            ->end()
260
            ->beforeNormalization()
261
                ->ifTrue(function ($v) {return !isset($v['sessionMode']) && isset($v['session_mode']);})
262
                ->then(function ($v) {
263
                    $v['sessionMode'] = $v['session_mode'];
264
                    unset($v['session_mode']);
265
266
                    return $v;
267
                })
268
            ->end()
269
            ->beforeNormalization()
270
                ->ifTrue(function ($v) {return !isset($v['MultipleActiveResultSets']) && isset($v['multiple_active_result_sets']);})
271
                ->then(function ($v) {
272
                    $v['MultipleActiveResultSets'] = $v['multiple_active_result_sets'];
273
                    unset($v['multiple_active_result_sets']);
274
275
                    return $v;
276
                })
277
            ->end()
278
        ;
279
    }
280
281
    /**
282
     * Add the ORM section to configuration tree
283
     *
284
     * @param ArrayNodeDefinition $node
285
     */
286
    private function addOrmSection(ArrayNodeDefinition $node)
287
    {
288
        $generationModes = $this->getAutoGenerateModes();
289
290
        $node
291
            ->children()
292
                ->arrayNode('orm')
293
                    ->beforeNormalization()
294
                        ->ifTrue(function ($v) { return null === $v || (is_array($v) && !array_key_exists('entity_managers', $v) && !array_key_exists('entity_manager', $v)); })
295
                        ->then(function ($v) {
296
                            $v = (array) $v;
297
                            // Key that should not be rewritten to the connection config
298
                            $excludedKeys = array(
299
                                'default_entity_manager' => true, 'auto_generate_proxy_classes' => true,
300
                                'proxy_dir' => true, 'proxy_namespace' => true, 'resolve_target_entities' => true,
301
                                'resolve_target_entity' => true,
302
                            );
303
                            $entityManager = array();
304 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...
305
                                if (isset($excludedKeys[$key])) {
306
                                    continue;
307
                                }
308
                                $entityManager[$key] = $v[$key];
309
                                unset($v[$key]);
310
                            }
311
                            $v['default_entity_manager'] = isset($v['default_entity_manager']) ? (string) $v['default_entity_manager'] : 'default';
312
                            $v['entity_managers'] = array($v['default_entity_manager'] => $entityManager);
313
314
                            return $v;
315
                        })
316
                    ->end()
317
                    ->children()
318
                        ->scalarNode('default_entity_manager')->end()
319
                        ->scalarNode('auto_generate_proxy_classes')->defaultValue(false)
320
                            ->info('Auto generate mode possible values are: "NEVER", "ALWAYS", "FILE_NOT_EXISTS", "EVAL"')
321
                            ->validate()
322
                                ->ifTrue(function ($v) use ($generationModes) {
323
                                    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...
324
                                        return false;
325
                                    }
326
                                    if (is_bool($v)) {
327
                                        return false;
328
                                    }
329
                                    if (is_string($v)) {
330
                                        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...
331
                                            return false;
332
                                        }
333
                                    }
334
335
                                    return true;
336
                                })
337
                                ->thenInvalid('Invalid auto generate mode value %s')
338
                            ->end()
339
                            ->validate()
340
                                ->ifString()
341
                                ->then(function ($v) {
342
                                    return constant('Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_'.strtoupper($v));
343
                                })
344
                            ->end()
345
                        ->end()
346
                        ->scalarNode('proxy_dir')->defaultValue('%kernel.cache_dir%/doctrine/orm/Proxies')->end()
347
                        ->scalarNode('proxy_namespace')->defaultValue('Proxies')->end()
348
                    ->end()
349
                    ->fixXmlConfig('entity_manager')
350
                    ->append($this->getOrmEntityManagersNode())
351
                    ->fixXmlConfig('resolve_target_entity', 'resolve_target_entities')
352
                    ->append($this->getOrmTargetEntityResolverNode())
353
                    ->append($this->getOrmSecondLevelCacheNode())
354
                ->end()
355
            ->end()
356
        ;
357
    }
358
    
359
    /**
360
     * Return ORM second level node
361
     *
362
     * @return \Symfony\Component\Config\Definition\Builder\NodeDefinition
363
     */
364
    private function getOrmSecondLevelCacheNode()
365
    {
366
        $treeBuilder = new TreeBuilder();
367
        $node = $treeBuilder->root('second_level_cache');
368
369
        $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...
370
            ->info('Global second level cache defaults (for all entity managers).')
371
            ->children()
372
                ->arrayNode('default_cache_factory')
373
                    ->addDefaultsIfNotSet()
374
                    ->children()
375
                        ->scalarNode('class')
376
                            ->defaultValue('Doctrine\ORM\Cache\DefaultCacheFactory')
377
                        ->end()
378
                    ->end()
379
                ->end()
380
                ->arrayNode('default_region')
381
                    ->addDefaultsIfNotSet()
382
                    ->children()
383
                        ->scalarNode('class')
384
                            ->defaultValue('Doctrine\ORM\Cache\Region\DefaultRegion')
385
                        ->end()
386
                    ->end()
387
                ->end()
388
                ->arrayNode('filelock_region')
389
                    ->addDefaultsIfNotSet()
390
                    ->children()
391
                        ->scalarNode('class')
392
                            ->defaultValue('Doctrine\ORM\Cache\Region\FileLockRegion')
393
                        ->end()
394
                    ->end()
395
                ->end()
396
                ->arrayNode('logger_chain')
397
                    ->addDefaultsIfNotSet()
398
                    ->children()
399
                        ->scalarNode('class')
400
                            ->defaultValue('Doctrine\ORM\Cache\Logging\CacheLoggerChain')
401
                        ->end()
402
                    ->end()
403
                ->end()
404
                ->arrayNode('logger_statistics')
405
                    ->addDefaultsIfNotSet()
406
                    ->children()
407
                        ->scalarNode('class')
408
                            ->defaultValue('Doctrine\ORM\Cache\Logging\StatisticsCacheLogger')
409
                        ->end()
410
                    ->end()
411
                ->end()
412
                ->arrayNode('cache_configuration')
413
                    ->addDefaultsIfNotSet()
414
                    ->children()
415
                        ->scalarNode('class')
416
                            ->defaultValue('Doctrine\ORM\Cache\CacheConfiguration')
417
                        ->end()
418
                    ->end()
419
                ->end()
420
                ->arrayNode('regions_configuration')
421
                    ->addDefaultsIfNotSet()
422
                    ->children()
423
                        ->scalarNode('class')
424
                            ->defaultValue('Doctrine\ORM\Cache\RegionsConfiguration')
425
                        ->end()
426
                    ->end()
427
                ->end()
428
            ->end()
429
        ;
430
431
        return $node;
432
    }
433
434
    /**
435
     * Return ORM target entity resolver node
436
     *
437
     * @return \Symfony\Component\Config\Definition\Builder\NodeDefinition
438
     */
439
    private function getOrmTargetEntityResolverNode()
440
    {
441
        $treeBuilder = new TreeBuilder();
442
        $node = $treeBuilder->root('resolve_target_entities');
443
444
        $node
445
            ->useAttributeAsKey('interface')
446
            ->prototype('scalar')
447
                ->cannotBeEmpty()
448
            ->end()
449
        ;
450
451
        return $node;
452
    }
453
454
    /**
455
     * Return ORM entity listener node
456
     *
457
     * @return \Symfony\Component\Config\Definition\Builder\NodeDefinition
458
     */
459
    private function getOrmEntityListenersNode()
460
    {
461
        $builder = new TreeBuilder();
462
        $node = $builder->root('entity_listeners');
463
        $normalizer = function ($mappings) {
464
            $entities = array();
465
466
            foreach ($mappings as $entityClass => $mapping) {
467
                $listeners = array();
468
469
                foreach ($mapping as $listenerClass => $listenerEvent) {
470
                    $events = array();
471
472
                    foreach ($listenerEvent as $eventType => $eventMapping) {
473
                        if ($eventMapping === null) {
474
                            $eventMapping = array(null);
475
                        }
476
477
                        foreach ($eventMapping as $method) {
478
                            $events[] = array(
479
                               'type' => $eventType,
480
                               'method' => $method,
481
                            );
482
                        }
483
                    }
484
485
                    $listeners[] = array(
486
                        'class' => $listenerClass,
487
                        'event' => $events,
488
                    );
489
                }
490
491
                $entities[] = array(
492
                    'class' => $entityClass,
493
                    'listener' => $listeners,
494
                );
495
            }
496
497
            return array('entities' => $entities);
498
        };
499
500
        $node
501
            ->beforeNormalization()
502
                // Yaml normalization
503
                ->ifTrue(function ($v) { return is_array(reset($v)) && is_string(key(reset($v))); })
504
                ->then($normalizer)
505
            ->end()
506
            ->fixXmlConfig('entity', 'entities')
507
            ->children()
508
                ->arrayNode('entities')
509
                    ->useAttributeAsKey('class')
510
                    ->prototype('array')
511
                        ->fixXmlConfig('listener')
512
                        ->children()
513
                            ->arrayNode('listeners')
514
                                ->useAttributeAsKey('class')
515
                                ->prototype('array')
516
                                    ->fixXmlConfig('event')
517
                                    ->children()
518
                                        ->arrayNode('events')
519
                                            ->prototype('array')
520
                                                ->children()
521
                                                    ->scalarNode('type')->end()
522
                                                    ->scalarNode('method')->defaultNull()->end()
523
                                                ->end()
524
                                            ->end()
525
                                        ->end()
526
                                    ->end()
527
                                ->end()
528
                            ->end()
529
                        ->end()
530
                    ->end()
531
                ->end()
532
            ->end()
533
        ;
534
535
        return $node;
536
    }
537
538
    /**
539
     * Return ORM entity manager node
540
     *
541
     * @return ArrayNodeDefinition
542
     */
543
    private function getOrmEntityManagersNode()
544
    {
545
        $treeBuilder = new TreeBuilder();
546
        $node = $treeBuilder->root('entity_managers');
547
548
        $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...
549
            ->requiresAtLeastOneElement()
550
            ->useAttributeAsKey('name')
551
            ->prototype('array')
552
                ->addDefaultsIfNotSet()
553
                ->append($this->getOrmCacheDriverNode('query_cache_driver'))
554
                ->append($this->getOrmCacheDriverNode('metadata_cache_driver'))
555
                ->append($this->getOrmCacheDriverNode('result_cache_driver'))
556
                ->append($this->getOrmEntityListenersNode())
557
                ->children()
558
                    ->scalarNode('connection')->end()
559
                    ->scalarNode('class_metadata_factory_name')->defaultValue('Doctrine\ORM\Mapping\ClassMetadataFactory')->end()
560
                    ->scalarNode('default_repository_class')->defaultValue('Doctrine\ORM\EntityRepository')->end()
561
                    ->scalarNode('auto_mapping')->defaultFalse()->end()
562
                    ->scalarNode('naming_strategy')->defaultValue('doctrine.orm.naming_strategy.default')->end()
563
                    ->scalarNode('quote_strategy')->defaultValue('doctrine.orm.quote_strategy.default')->end()
564
                    ->scalarNode('entity_listener_resolver')->defaultNull()->end()
565
                    ->scalarNode('repository_factory')->defaultNull()->end()
566
                ->end()
567
                ->children()
568
                    ->arrayNode('second_level_cache')
569
                        ->children()
570
                            ->append($this->getOrmCacheDriverNode('region_cache_driver'))
571
                            ->scalarNode('region_lock_lifetime')->defaultValue(60)->end()
572
                            ->booleanNode('log_enabled')->defaultValue($this->debug)->end()
573
                            ->scalarNode('region_lifetime')->defaultValue(0)->end()
574
                            ->booleanNode('enabled')->defaultValue(true)->end()
575
                            ->scalarNode('factory')->end()
576
                        ->end()
577
                        ->fixXmlConfig('region')
578
                        ->children()
579
                            ->arrayNode('regions')
580
                                ->useAttributeAsKey('name')
581
                                ->prototype('array')
582
                                    ->children()
583
                                        ->append($this->getOrmCacheDriverNode('cache_driver'))
584
                                        ->scalarNode('lock_path')->defaultValue('%kernel.cache_dir%/doctrine/orm/slc/filelock')->end()
585
                                        ->scalarNode('lock_lifetime')->defaultValue(60)->end()
586
                                        ->scalarNode('type')->defaultValue('default')->end()
587
                                        ->scalarNode('lifetime')->defaultValue(0)->end()
588
                                        ->scalarNode('service')->end()
589
                                        ->scalarNode('name')->end()
590
                                    ->end()
591
                                ->end()
592
                            ->end()
593
                        ->end()
594
                        ->fixXmlConfig('logger')
595
                        ->children()
596
                            ->arrayNode('loggers')
597
                                ->useAttributeAsKey('name')
598
                                ->prototype('array')
599
                                    ->children()
600
                                        ->scalarNode('name')->end()
601
                                        ->scalarNode('service')->end()
602
                                    ->end()
603
                                ->end()
604
                            ->end()
605
                        ->end()
606
                    ->end()
607
                ->end()
608
                ->fixXmlConfig('hydrator')
609
                ->children()
610
                    ->arrayNode('hydrators')
611
                        ->useAttributeAsKey('name')
612
                        ->prototype('scalar')->end()
613
                    ->end()
614
                ->end()
615
                ->fixXmlConfig('mapping')
616
                ->children()
617
                    ->arrayNode('mappings')
618
                        ->useAttributeAsKey('name')
619
                        ->prototype('array')
620
                            ->beforeNormalization()
621
                                ->ifString()
622
                                ->then(function ($v) { return array('type' => $v); })
623
                            ->end()
624
                            ->treatNullLike(array())
625
                            ->treatFalseLike(array('mapping' => false))
626
                            ->performNoDeepMerging()
627
                            ->children()
628
                                ->scalarNode('mapping')->defaultValue(true)->end()
629
                                ->scalarNode('type')->end()
630
                                ->scalarNode('dir')->end()
631
                                ->scalarNode('alias')->end()
632
                                ->scalarNode('prefix')->end()
633
                                ->booleanNode('is_bundle')->end()
634
                            ->end()
635
                        ->end()
636
                    ->end()
637
                    ->arrayNode('dql')
638
                        ->fixXmlConfig('string_function')
639
                        ->fixXmlConfig('numeric_function')
640
                        ->fixXmlConfig('datetime_function')
641
                        ->children()
642
                            ->arrayNode('string_functions')
643
                                ->useAttributeAsKey('name')
644
                                ->prototype('scalar')->end()
645
                            ->end()
646
                            ->arrayNode('numeric_functions')
647
                                ->useAttributeAsKey('name')
648
                                ->prototype('scalar')->end()
649
                            ->end()
650
                            ->arrayNode('datetime_functions')
651
                                ->useAttributeAsKey('name')
652
                                ->prototype('scalar')->end()
653
                            ->end()
654
                        ->end()
655
                    ->end()
656
                ->end()
657
                ->fixXmlConfig('filter')
658
                ->children()
659
                    ->arrayNode('filters')
660
                        ->info('Register SQL Filters in the entity manager')
661
                        ->useAttributeAsKey('name')
662
                        ->prototype('array')
663
                            ->beforeNormalization()
664
                                ->ifString()
665
                                ->then(function ($v) { return array('class' => $v); })
666
                            ->end()
667
                            ->beforeNormalization()
668
                                // The content of the XML node is returned as the "value" key so we need to rename it
669
                                ->ifTrue(function ($v) {
670
                                    return is_array($v) && isset($v['value']);
671
                                })
672
                                ->then(function ($v) {
673
                                    $v['class'] = $v['value'];
674
                                    unset($v['value']);
675
676
                                    return $v;
677
                                })
678
                            ->end()
679
                            ->fixXmlConfig('parameter')
680
                            ->children()
681
                                ->scalarNode('class')->isRequired()->end()
682
                                ->booleanNode('enabled')->defaultFalse()->end()
683
                                ->arrayNode('parameters')
684
                                    ->useAttributeAsKey('name')
685
                                    ->prototype('variable')->end()
686
                                ->end()
687
                            ->end()
688
                        ->end()
689
                    ->end()
690
                ->end()
691
            ->end()
692
        ;
693
694
        return $node;
695
    }
696
697
    /**
698
     * Return a ORM cache driver node for an given entity manager
699
     *
700
     * @param string $name
701
     *
702
     * @return ArrayNodeDefinition
703
     */
704
    private function getOrmCacheDriverNode($name)
705
    {
706
        $treeBuilder = new TreeBuilder();
707
        $node = $treeBuilder->root($name);
708
709
        $node
710
            ->addDefaultsIfNotSet()
711
            ->beforeNormalization()
712
                ->ifString()
713
                ->then(function ($v) { return array('type' => $v); })
714
            ->end()
715
            ->children()
716
                ->scalarNode('type')->defaultValue('array')->end()
717
                ->scalarNode('host')->end()
718
                ->scalarNode('port')->end()
719
                ->scalarNode('instance_class')->end()
720
                ->scalarNode('class')->end()
721
                ->scalarNode('id')->end()
722
                ->scalarNode('namespace')->defaultNull()->end()
723
                ->scalarNode('cache_provider')->defaultNull()->end()
724
            ->end()
725
        ;
726
727
        return $node;
728
    }
729
730
    /**
731
     * Find proxy auto generate modes for their names and int values
732
     *
733
     * @return array
734
     */
735
    private function getAutoGenerateModes()
736
    {
737
        $constPrefix = 'AUTOGENERATE_';
738
        $prefixLen = strlen($constPrefix);
739
        $refClass = new \ReflectionClass('Doctrine\Common\Proxy\AbstractProxyFactory');
740
        $constsArray = $refClass->getConstants();
741
        $namesArray = array();
742
        $valuesArray = array();
743
744
        foreach ($constsArray as $key => $value) {
745
            if (strpos($key, $constPrefix) === 0) {
746
                $namesArray[] = substr($key, $prefixLen);
747
                $valuesArray[] = (int) $value;
748
            }
749
        }
750
751
        return array(
752
            'names' => $namesArray,
753
            'values' => $valuesArray,
754
        );
755
    }
756
}
757