Completed
Push — master ( baba12...502b0f )
by Andreas
08:34
created

Configuration::addDbalSection()   B

Complexity

Conditions 6
Paths 1

Size

Total Lines 51

Duplication

Lines 7
Ratio 13.73 %

Importance

Changes 0
Metric Value
dl 7
loc 51
rs 8.4468
c 0
b 0
f 0
cc 6
nc 1
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
4
5
use Doctrine\ORM\EntityManager;
6
use ReflectionClass;
7
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
8
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
9
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
10
use Symfony\Component\Config\Definition\ConfigurationInterface;
11
use Symfony\Component\DependencyInjection\Exception\LogicException;
12
use const E_USER_DEPRECATED;
13
use function array_key_exists;
14
use function in_array;
15
use function is_array;
16
use function sprintf;
17
use function trigger_error;
18
19
/**
20
 * This class contains the configuration information for the bundle
21
 *
22
 * This information is solely responsible for how the different configuration
23
 * sections are normalized, and merged.
24
 */
25
class Configuration implements ConfigurationInterface
26
{
27
    /** @var bool */
28
    private $debug;
29
30
    /**
31
     * @param bool $debug Whether to use the debug mode
32
     */
33
    public function __construct($debug)
34
    {
35
        $this->debug = (bool) $debug;
36
    }
37
38
    /**
39
     * {@inheritDoc}
40
     */
41
    public function getConfigTreeBuilder() : TreeBuilder
42
    {
43
        $treeBuilder = new TreeBuilder('doctrine');
44
45
        if (method_exists($treeBuilder, 'getRootNode')) {
46
            $rootNode = $treeBuilder->getRootNode();
47
        } else {
48
            // BC layer for symfony/config 4.1 and older
49
            $rootNode = $treeBuilder->root('doctrine');
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Config...der\TreeBuilder::root() has been deprecated with message: since Symfony 4.3, pass the root name to the constructor instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
50
        }
51
52
        $this->addDbalSection($rootNode);
0 ignored issues
show
Compatibility introduced by
$rootNode 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...
53
        $this->addOrmSection($rootNode);
0 ignored issues
show
Compatibility introduced by
$rootNode 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...
54
55
        return $treeBuilder;
56
    }
57
58
    /**
59
     * Add DBAL section to configuration tree
60
     */
61
    private function addDbalSection(ArrayNodeDefinition $node)
62
    {
63
        $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...
64
            ->children()
65
            ->arrayNode('dbal')
66
                ->beforeNormalization()
67
                    ->ifTrue(static function ($v) {
68
                        return is_array($v) && ! array_key_exists('connections', $v) && ! array_key_exists('connection', $v);
69
                    })
70
                    ->then(static function ($v) {
71
                        // Key that should not be rewritten to the connection config
72
                        $excludedKeys = ['default_connection' => true, 'types' => true, 'type' => true];
73
                        $connection   = [];
74 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...
75
                            if (isset($excludedKeys[$key])) {
76
                                continue;
77
                            }
78
                            $connection[$key] = $v[$key];
79
                            unset($v[$key]);
80
                        }
81
                        $v['default_connection'] = isset($v['default_connection']) ? (string) $v['default_connection'] : 'default';
82
                        $v['connections']        = [$v['default_connection'] => $connection];
83
84
                        return $v;
85
                    })
86
                ->end()
87
                ->children()
88
                    ->scalarNode('default_connection')->end()
89
                ->end()
90
                ->fixXmlConfig('type')
91
                ->children()
92
                    ->arrayNode('types')
93
                        ->useAttributeAsKey('name')
94
                        ->prototype('array')
95
                            ->beforeNormalization()
96
                                ->ifString()
97
                                ->then(static function ($v) {
98
                                    return ['class' => $v];
99
                                })
100
                            ->end()
101
                            ->children()
102
                                ->scalarNode('class')->isRequired()->end()
103
                                ->booleanNode('commented')->defaultNull()->end()
104
                            ->end()
105
                        ->end()
106
                    ->end()
107
                ->end()
108
                ->fixXmlConfig('connection')
109
                ->append($this->getDbalConnectionsNode())
110
            ->end();
111
    }
112
113
    /**
114
     * Return the dbal connections node
115
     *
116
     * @return ArrayNodeDefinition
117
     */
118
    private function getDbalConnectionsNode()
119
    {
120
        $treeBuilder = new TreeBuilder('connections');
121
122
        if (method_exists($treeBuilder, 'getRootNode')) {
123
            $node = $treeBuilder->getRootNode();
124
        } else {
125
            // BC layer for symfony/config 4.1 and older
126
            $node = $treeBuilder->root('connections');
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Config...der\TreeBuilder::root() has been deprecated with message: since Symfony 4.3, pass the root name to the constructor instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

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

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
407
        }
408
409
        $node
0 ignored issues
show
Bug introduced by
The method useAttributeAsKey() does not exist on Symfony\Component\Config...\Builder\NodeDefinition. Did you maybe mean attribute()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
410
            ->useAttributeAsKey('interface')
411
            ->prototype('scalar')
412
                ->cannotBeEmpty()
413
            ->end();
414
415
        return $node;
416
    }
417
418
    /**
419
     * Return ORM entity listener node
420
     *
421
     * @return NodeDefinition
422
     */
423
    private function getOrmEntityListenersNode()
424
    {
425
        $treeBuilder = new TreeBuilder('entity_listeners');
426
427
        if (method_exists($treeBuilder, 'getRootNode')) {
428
            $node = $treeBuilder->getRootNode();
429
        } else {
430
            // BC layer for symfony/config 4.1 and older
431
            $node = $treeBuilder->root('entity_listeners');
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Config...der\TreeBuilder::root() has been deprecated with message: since Symfony 4.3, pass the root name to the constructor instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
432
        }
433
434
        $normalizer = static function ($mappings) {
435
            $entities = [];
436
437
            foreach ($mappings as $entityClass => $mapping) {
438
                $listeners = [];
439
440
                foreach ($mapping as $listenerClass => $listenerEvent) {
441
                    $events = [];
442
443
                    foreach ($listenerEvent as $eventType => $eventMapping) {
444
                        if ($eventMapping === null) {
445
                            $eventMapping = [null];
446
                        }
447
448
                        foreach ($eventMapping as $method) {
449
                            $events[] = [
450
                                'type' => $eventType,
451
                                'method' => $method,
452
                            ];
453
                        }
454
                    }
455
456
                    $listeners[] = [
457
                        'class' => $listenerClass,
458
                        'event' => $events,
459
                    ];
460
                }
461
462
                $entities[] = [
463
                    'class' => $entityClass,
464
                    'listener' => $listeners,
465
                ];
466
            }
467
468
            return ['entities' => $entities];
469
        };
470
471
        $node
472
            ->beforeNormalization()
473
                // Yaml normalization
474
                ->ifTrue(static function ($v) {
475
                    return is_array(reset($v)) && is_string(key(reset($v)));
476
                })
477
                ->then($normalizer)
478
            ->end()
479
            ->fixXmlConfig('entity', 'entities')
480
            ->children()
481
                ->arrayNode('entities')
482
                    ->useAttributeAsKey('class')
483
                    ->prototype('array')
484
                        ->fixXmlConfig('listener')
485
                        ->children()
486
                            ->arrayNode('listeners')
487
                                ->useAttributeAsKey('class')
488
                                ->prototype('array')
489
                                    ->fixXmlConfig('event')
490
                                    ->children()
491
                                        ->arrayNode('events')
492
                                            ->prototype('array')
493
                                                ->children()
494
                                                    ->scalarNode('type')->end()
495
                                                    ->scalarNode('method')->defaultNull()->end()
496
                                                ->end()
497
                                            ->end()
498
                                        ->end()
499
                                    ->end()
500
                                ->end()
501
                            ->end()
502
                        ->end()
503
                    ->end()
504
                ->end()
505
            ->end();
506
507
        return $node;
508
    }
509
510
    /**
511
     * Return ORM entity manager node
512
     *
513
     * @return ArrayNodeDefinition
514
     */
515
    private function getOrmEntityManagersNode()
516
    {
517
        $treeBuilder = new TreeBuilder('entity_managers');
518
519
        if (method_exists($treeBuilder, 'getRootNode')) {
520
            $node = $treeBuilder->getRootNode();
521
        } else {
522
            // BC layer for symfony/config 4.1 and older
523
            $node = $treeBuilder->root('entity_managers');
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Config...der\TreeBuilder::root() has been deprecated with message: since Symfony 4.3, pass the root name to the constructor instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
524
        }
525
526
        $node
527
            ->requiresAtLeastOneElement()
528
            ->useAttributeAsKey('name')
529
            ->prototype('array')
530
                ->addDefaultsIfNotSet()
531
                ->append($this->getOrmCacheDriverNode('query_cache_driver'))
532
                ->append($this->getOrmCacheDriverNode('metadata_cache_driver'))
533
                ->append($this->getOrmCacheDriverNode('result_cache_driver'))
534
                ->append($this->getOrmEntityListenersNode())
535
                ->children()
536
                    ->scalarNode('connection')->end()
537
                    ->scalarNode('class_metadata_factory_name')->defaultValue('Doctrine\ORM\Mapping\ClassMetadataFactory')->end()
538
                    ->scalarNode('default_repository_class')->defaultValue('Doctrine\ORM\EntityRepository')->end()
539
                    ->scalarNode('auto_mapping')->defaultFalse()->end()
540
                    ->scalarNode('naming_strategy')->defaultValue('doctrine.orm.naming_strategy.default')->end()
541
                    ->scalarNode('quote_strategy')->defaultValue('doctrine.orm.quote_strategy.default')->end()
542
                    ->scalarNode('entity_listener_resolver')->defaultNull()->end()
543
                    ->scalarNode('repository_factory')->defaultValue('doctrine.orm.container_repository_factory')->end()
544
                ->end()
545
                ->children()
546
                    ->arrayNode('second_level_cache')
547
                        ->children()
548
                            ->append($this->getOrmCacheDriverNode('region_cache_driver'))
549
                            ->scalarNode('region_lock_lifetime')->defaultValue(60)->end()
550
                            ->booleanNode('log_enabled')->defaultValue($this->debug)->end()
551
                            ->scalarNode('region_lifetime')->defaultValue(0)->end()
552
                            ->booleanNode('enabled')->defaultValue(true)->end()
553
                            ->scalarNode('factory')->end()
554
                        ->end()
555
                        ->fixXmlConfig('region')
556
                        ->children()
557
                            ->arrayNode('regions')
558
                                ->useAttributeAsKey('name')
559
                                ->prototype('array')
560
                                    ->children()
561
                                        ->append($this->getOrmCacheDriverNode('cache_driver'))
562
                                        ->scalarNode('lock_path')->defaultValue('%kernel.cache_dir%/doctrine/orm/slc/filelock')->end()
563
                                        ->scalarNode('lock_lifetime')->defaultValue(60)->end()
564
                                        ->scalarNode('type')->defaultValue('default')->end()
565
                                        ->scalarNode('lifetime')->defaultValue(0)->end()
566
                                        ->scalarNode('service')->end()
567
                                        ->scalarNode('name')->end()
568
                                    ->end()
569
                                ->end()
570
                            ->end()
571
                        ->end()
572
                        ->fixXmlConfig('logger')
573
                        ->children()
574
                            ->arrayNode('loggers')
575
                                ->useAttributeAsKey('name')
576
                                ->prototype('array')
577
                                    ->children()
578
                                        ->scalarNode('name')->end()
579
                                        ->scalarNode('service')->end()
580
                                    ->end()
581
                                ->end()
582
                            ->end()
583
                        ->end()
584
                    ->end()
585
                ->end()
586
                ->fixXmlConfig('hydrator')
587
                ->children()
588
                    ->arrayNode('hydrators')
589
                        ->useAttributeAsKey('name')
590
                        ->prototype('scalar')->end()
591
                    ->end()
592
                ->end()
593
                ->fixXmlConfig('mapping')
594
                ->children()
595
                    ->arrayNode('mappings')
596
                        ->useAttributeAsKey('name')
597
                        ->prototype('array')
598
                            ->beforeNormalization()
599
                                ->ifString()
600
                                ->then(static function ($v) {
601
                                    return ['type' => $v];
602
                                })
603
                            ->end()
604
                            ->treatNullLike([])
605
                            ->treatFalseLike(['mapping' => false])
606
                            ->performNoDeepMerging()
607
                            ->children()
608
                                ->scalarNode('mapping')->defaultValue(true)->end()
609
                                ->scalarNode('type')->end()
610
                                ->scalarNode('dir')->end()
611
                                ->scalarNode('alias')->end()
612
                                ->scalarNode('prefix')->end()
613
                                ->booleanNode('is_bundle')->end()
614
                            ->end()
615
                        ->end()
616
                    ->end()
617
                    ->arrayNode('dql')
618
                        ->fixXmlConfig('string_function')
619
                        ->fixXmlConfig('numeric_function')
620
                        ->fixXmlConfig('datetime_function')
621
                        ->children()
622
                            ->arrayNode('string_functions')
623
                                ->useAttributeAsKey('name')
624
                                ->prototype('scalar')->end()
625
                            ->end()
626
                            ->arrayNode('numeric_functions')
627
                                ->useAttributeAsKey('name')
628
                                ->prototype('scalar')->end()
629
                            ->end()
630
                            ->arrayNode('datetime_functions')
631
                                ->useAttributeAsKey('name')
632
                                ->prototype('scalar')->end()
633
                            ->end()
634
                        ->end()
635
                    ->end()
636
                ->end()
637
                ->fixXmlConfig('filter')
638
                ->children()
639
                    ->arrayNode('filters')
640
                        ->info('Register SQL Filters in the entity manager')
641
                        ->useAttributeAsKey('name')
642
                        ->prototype('array')
643
                            ->beforeNormalization()
644
                                ->ifString()
645
                                ->then(static function ($v) {
646
                                    return ['class' => $v];
647
                                })
648
                            ->end()
649
                            ->beforeNormalization()
650
                                // The content of the XML node is returned as the "value" key so we need to rename it
651
                                ->ifTrue(static function ($v) {
652
                                    return is_array($v) && isset($v['value']);
653
                                })
654
                                ->then(static function ($v) {
655
                                    $v['class'] = $v['value'];
656
                                    unset($v['value']);
657
658
                                    return $v;
659
                                })
660
                            ->end()
661
                            ->fixXmlConfig('parameter')
662
                            ->children()
663
                                ->scalarNode('class')->isRequired()->end()
664
                                ->booleanNode('enabled')->defaultFalse()->end()
665
                                ->arrayNode('parameters')
666
                                    ->useAttributeAsKey('name')
667
                                    ->prototype('variable')->end()
668
                                ->end()
669
                            ->end()
670
                        ->end()
671
                    ->end()
672
                ->end()
673
            ->end();
674
675
        return $node;
676
    }
677
678
    /**
679
     * Return a ORM cache driver node for an given entity manager
680
     *
681
     * @param string $name
682
     *
683
     * @return ArrayNodeDefinition
684
     */
685
    private function getOrmCacheDriverNode($name)
686
    {
687
        $treeBuilder = new TreeBuilder($name);
688
689
        if (method_exists($treeBuilder, 'getRootNode')) {
690
            $node = $treeBuilder->getRootNode();
691
        } else {
692
            // BC layer for symfony/config 4.1 and older
693
            $node = $treeBuilder->root($name);
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Config...der\TreeBuilder::root() has been deprecated with message: since Symfony 4.3, pass the root name to the constructor instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
694
        }
695
696
        $node
697
            ->addDefaultsIfNotSet()
698
            ->beforeNormalization()
699
                ->ifString()
700
                ->then(static function ($v) : array {
701
                    return ['type' => $v];
702
                })
703
            ->end()
704
            ->beforeNormalization()
705
                ->ifTrue(static function ($v) : bool {
706
                    return is_array($v) && array_key_exists('cache_provider', $v);
707
                })
708
                ->then(static function ($v) : array {
709
                    return ['type' => 'provider'] + $v;
710
                })
711
            ->end()
712
            ->children()
713
                ->scalarNode('type')
714
                    ->defaultNull()
715
                    ->beforeNormalization()
716
                        ->ifNotInArray([null, 'pool', 'service'])
717
                        ->then(static function ($v) use ($name) {
718
                            @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
719
                                sprintf(
720
                                    'Using the "%s" type for cache "%s" is deprecated since DoctrineBundle 1.12 and will be dropped in 2.0. Please use the "service" or "pool" types exclusively.',
721
                                    $v,
722
                                    $name
723
                                ),
724
                                E_USER_DEPRECATED
725
                            );
726
727
                            return $v;
728
                        })
729
                    ->end()
730
                ->end()
731
                ->scalarNode('id')->end()
732
                ->scalarNode('pool')->end()
733
                ->scalarNode('host')->setDeprecated()->end()
734
                ->scalarNode('port')->setDeprecated()->end()
735
                ->scalarNode('database')->setDeprecated()->end()
736
                ->scalarNode('instance_class')->setDeprecated()->end()
737
                ->scalarNode('class')->setDeprecated()->end()
738
                ->scalarNode('namespace')->defaultNull()->setDeprecated()->end()
739
                ->scalarNode('cache_provider')->defaultNull()->setDeprecated()->end()
740
            ->end();
741
742
        return $node;
743
    }
744
745
    /**
746
     * Find proxy auto generate modes for their names and int values
747
     *
748
     * @return array
749
     */
750
    private function getAutoGenerateModes()
751
    {
752
        $constPrefix = 'AUTOGENERATE_';
753
        $prefixLen   = strlen($constPrefix);
754
        $refClass    = new ReflectionClass('Doctrine\Common\Proxy\AbstractProxyFactory');
755
        $constsArray = $refClass->getConstants();
756
        $namesArray  = [];
757
        $valuesArray = [];
758
759
        foreach ($constsArray as $key => $value) {
760
            if (strpos($key, $constPrefix) !== 0) {
761
                continue;
762
            }
763
764
            $namesArray[]  = substr($key, $prefixLen);
765
            $valuesArray[] = (int) $value;
766
        }
767
768
        return [
769
            'names' => $namesArray,
770
            'values' => $valuesArray,
771
        ];
772
    }
773
}
774