Completed
Pull Request — master (#1483)
by
unknown
04:21
created

Configuration::createTreeBuilderNode()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 5
cts 6
cp 0.8333
rs 9.8333
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2.0185
1
<?php
2
3
/*
4
 * This file is part of the FOSElasticaBundle package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace FOS\ElasticaBundle\DependencyInjection;
13
14
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
15
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
16
use Symfony\Component\Config\Definition\ConfigurationInterface;
17
18
class Configuration implements ConfigurationInterface
19
{
20
    const SUPPORTED_DRIVERS = ['orm', 'mongodb', 'phpcr'];
21
22
    /**
23
     * If the kernel is running in debug mode.
24
     *
25
     * @var bool
26
     */
27
    private $debug;
28
29 30
    public function __construct($debug)
30
    {
31 30
        $this->debug = $debug;
32 30
    }
33
34
    /**
35
     * Generates the configuration tree.
36
     *
37
     * @return TreeBuilder
38
     */
39 30
    public function getConfigTreeBuilder()
40
    {
41 30
        $treeBuilder = new TreeBuilder('fos_elastica');
42
43 30
        if (method_exists($treeBuilder, 'getRootNode')) {
44 30
            $rootNode = $treeBuilder->getRootNode();
45
        } else {
46
            // BC layer for symfony/config 4.1 and older
47
            $rootNode = $treeBuilder->root('fos_elastica');
48
        }
49
50 30
        $this->addClientsSection($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...
51 30
        $this->addIndexesSection($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...
52
53
        $rootNode
54 30
            ->children()
55 30
                ->scalarNode('default_client')
56 30
                    ->info('Defaults to the first client defined')
57 30
                ->end()
58 30
                ->scalarNode('default_index')
59 30
                    ->info('Defaults to the first index defined')
60 30
                ->end()
61 30
                ->scalarNode('default_manager')->defaultValue('orm')->end()
62 30
                ->arrayNode('serializer')
63 30
                    ->treatNullLike([])
64 30
                    ->children()
65 30
                        ->scalarNode('callback_class')->defaultValue('FOS\ElasticaBundle\Serializer\Callback')->end()
66 30
                        ->scalarNode('serializer')->defaultValue('serializer')->end()
67 30
                    ->end()
68 30
                ->end()
69 30
            ->end()
70
        ;
71
72 30
        return $treeBuilder;
73
    }
74
75
    /**
76
     * Returns the array node used for "dynamic_templates".
77
     */
78 30
    public function getDynamicTemplateNode()
79
    {
80 30
        $node = $this->createTreeBuilderNode('dynamic_templates');
81
82
        $node
83 30
            ->prototype('array')
84 30
                ->prototype('array')
85 30
                    ->children()
86 30
                        ->scalarNode('match')->end()
87 30
                        ->scalarNode('unmatch')->end()
88 30
                        ->scalarNode('match_mapping_type')->end()
89 30
                        ->scalarNode('path_match')->end()
90 30
                        ->scalarNode('path_unmatch')->end()
91 30
                        ->scalarNode('match_pattern')->end()
92 30
                        ->arrayNode('mapping')
93 30
                            ->prototype('variable')
94 30
                                ->treatNullLike([])
95 30
                            ->end()
96 30
                        ->end()
97 30
                    ->end()
98 30
                ->end()
99 30
            ->end()
100
        ;
101
102 30
        return $node;
103
    }
104
105
    /**
106
     * Returns the array node used for "types".
107
     */
108 30
    protected function getTypesNode()
109
    {
110 30
        $node = $this->createTreeBuilderNode('types');
111
112
        $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...
113 30
            ->useAttributeAsKey('name')
114 30
            ->prototype('array')
115 30
                ->treatNullLike([])
116 30
                ->beforeNormalization()
117 30
                ->ifNull()
118 30
                    ->thenEmptyArray()
119 30
                ->end()
120
                // Support multiple dynamic_template formats to match the old bundle style
121
                // and the way ElasticSearch expects them
122 30
                ->beforeNormalization()
123 30
                ->ifTrue(function ($v) {
124 21
                    return isset($v['dynamic_templates']);
125 30
                })
126 30
                ->then(function ($v) {
127 3
                    $dt = [];
128 3
                    foreach ($v['dynamic_templates'] as $key => $type) {
129 3
                        if (is_int($key)) {
130 3
                            $dt[] = $type;
131
                        } else {
132 3
                            $dt[][$key] = $type;
133
                        }
134
                    }
135
136 3
                    $v['dynamic_templates'] = $dt;
137
138 3
                    return $v;
139 30
                })
140 30
                ->end()
141 30
                ->children()
142 30
                    ->booleanNode('date_detection')->end()
143 30
                    ->arrayNode('dynamic_date_formats')->prototype('scalar')->end()->end()
144 30
                    ->scalarNode('analyzer')->end()
145 30
                    ->booleanNode('numeric_detection')->end()
146 30
                    ->scalarNode('dynamic')->end()
147 30
                    ->variableNode('indexable_callback')->end()
148 30
                    ->append($this->getPersistenceNode())
149 30
                    ->append($this->getSerializerNode())
150 30
                ->end()
151 30
                ->append($this->getIdNode())
152 30
                ->append($this->getPropertiesNode())
153 30
                ->append($this->getDynamicTemplateNode())
154 30
                ->append($this->getSourceNode())
155 30
                ->append($this->getRoutingNode())
156 30
                ->append($this->getParentNode())
157 30
                ->append($this->getAllNode())
158 30
            ->end()
159
        ;
160
161 30
        return $node;
162
    }
163
164
    /**
165
     * Returns the array node used for "properties".
166
     */
167 30
    protected function getPropertiesNode()
168
    {
169 30
        $node = $this->createTreeBuilderNode('properties');
170
171
        $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...
172 30
            ->useAttributeAsKey('name')
173 30
            ->prototype('variable')
174 30
                ->treatNullLike([]);
175
176 30
        return $node;
177
    }
178
179
    /**
180
     * Returns the array node used for "_id".
181
     */
182 30
    protected function getIdNode()
183
    {
184 30
        $node = $this->createTreeBuilderNode('_id');
185
186
        $node
187 30
            ->children()
188 30
            ->scalarNode('path')->end()
189 30
            ->end()
190
        ;
191
192 30
        return $node;
193
    }
194
195
    /**
196
     * Returns the array node used for "_source".
197
     */
198 30
    protected function getSourceNode()
199
    {
200 30
        $node = $this->createTreeBuilderNode('_source');
201
202
        $node
203 30
            ->children()
204 30
                ->arrayNode('excludes')
205 30
                    ->useAttributeAsKey('name')
206 30
                    ->prototype('scalar')->end()
207 30
                ->end()
208 30
                ->arrayNode('includes')
209 30
                    ->useAttributeAsKey('name')
210 30
                    ->prototype('scalar')->end()
211 30
                ->end()
212 30
                ->scalarNode('compress')->end()
213 30
                ->scalarNode('compress_threshold')->end()
214 30
                ->scalarNode('enabled')->defaultTrue()->end()
215 30
            ->end()
216
        ;
217
218 30
        return $node;
219
    }
220
221
    /**
222
     * Returns the array node used for "_routing".
223
     */
224 30
    protected function getRoutingNode()
225
    {
226 30
        $node = $this->createTreeBuilderNode('_routing');
227
228
        $node
229 30
            ->children()
230 30
                ->scalarNode('required')->end()
231 30
                ->scalarNode('path')->end()
232 30
            ->end()
233
        ;
234
235 30
        return $node;
236
    }
237
238
    /**
239
     * Returns the array node used for "_parent".
240
     */
241 30
    protected function getParentNode()
242
    {
243 30
        $node = $this->createTreeBuilderNode('_parent');
244
245
        $node
246 30
            ->children()
247 30
                ->scalarNode('type')->end()
248 30
                ->scalarNode('property')->defaultValue(null)->end()
249 30
                ->scalarNode('identifier')->defaultValue('id')->end()
250 30
            ->end()
251
        ;
252
253 30
        return $node;
254
    }
255
256
    /**
257
     * Returns the array node used for "_all".
258
     */
259 30
    protected function getAllNode()
260
    {
261 30
        $node = $this->createTreeBuilderNode('_all');
262
263
        $node
264 30
            ->children()
265 30
            ->scalarNode('enabled')->defaultValue(true)->end()
266 30
            ->scalarNode('analyzer')->end()
267 30
            ->end()
268
        ;
269
270 30
        return $node;
271
    }
272
273
    /**
274
     * @return ArrayNodeDefinition|\Symfony\Component\Config\Definition\Builder\NodeDefinition
275
     */
276 30
    protected function getPersistenceNode()
277
    {
278 30
        $node = $this->createTreeBuilderNode('persistence');
279
280
        $node
281 30
            ->validate()
282 30
                ->ifTrue(function ($v) {
283 17
                    return isset($v['driver']) && 'orm' !== $v['driver'] && !empty($v['elastica_to_model_transformer']['hints']);
284 30
                })
285 30
                    ->thenInvalid('Hints are only supported by the "orm" driver')
286 30
            ->end()
287 30
            ->children()
288 30
                ->scalarNode('driver')
289 30
                    ->defaultValue('orm')
290 30
                    ->validate()
291 30
                    ->ifNotInArray(self::SUPPORTED_DRIVERS)
292 30
                        ->thenInvalid('The driver %s is not supported. Please choose one of '.json_encode(self::SUPPORTED_DRIVERS))
293 30
                    ->end()
294 30
                ->end()
295 30
                ->scalarNode('model')->defaultValue(null)->end()
296 30
                ->scalarNode('repository')->end()
297 30
                ->scalarNode('identifier')->defaultValue('id')->end()
298 30
                ->arrayNode('provider')
299 30
                    ->addDefaultsIfNotSet()
300 30
                    ->children()
301 30
                        ->scalarNode('batch_size')->defaultValue(100)->end()
302 30
                        ->scalarNode('clear_object_manager')->defaultTrue()->end()
303 30
                        ->scalarNode('debug_logging')
304 30
                            ->defaultValue($this->debug)
305 30
                            ->treatNullLike(true)
306 30
                        ->end()
307 30
                        ->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
308 30
                        ->scalarNode('service')->end()
309 30
                    ->end()
310 30
                ->end()
311 30
                ->arrayNode('listener')
312 30
                    ->addDefaultsIfNotSet()
313 30
                    ->children()
314 30
                        ->booleanNode('enabled')->defaultTrue()->end()
315 30
                        ->scalarNode('insert')->defaultTrue()->end()
316 30
                        ->scalarNode('update')->defaultTrue()->end()
317 30
                        ->scalarNode('delete')->defaultTrue()->end()
318 30
                        ->scalarNode('flush')->defaultTrue()->end()
319 30
                        ->booleanNode('defer')->defaultFalse()->end()
320 30
                        ->scalarNode('logger')
321 30
                            ->defaultFalse()
322 30
                            ->treatNullLike('fos_elastica.logger')
323 30
                            ->treatTrueLike('fos_elastica.logger')
324 30
                        ->end()
325 30
                        ->scalarNode('service')->end()
326 30
                    ->end()
327 30
                ->end()
328 30
                ->arrayNode('finder')
329 30
                    ->addDefaultsIfNotSet()
330 30
                    ->children()
331 30
                        ->scalarNode('service')->end()
332 30
                    ->end()
333 30
                ->end()
334 30
                ->arrayNode('elastica_to_model_transformer')
335 30
                    ->addDefaultsIfNotSet()
336 30
                    ->children()
337 30
                        ->arrayNode('hints')
338 30
                            ->prototype('array')
339 30
                                ->children()
340 30
                                    ->scalarNode('name')->end()
341 30
                                    ->scalarNode('value')->end()
342 30
                                ->end()
343 30
                            ->end()
344 30
                        ->end()
345 30
                        ->booleanNode('hydrate')->defaultTrue()->end()
346 30
                        ->booleanNode('ignore_missing')
347 30
                            ->defaultFalse()
348 30
                            ->info('Silently ignore results returned from Elasticsearch without corresponding persistent object.')
349 30
                        ->end()
350 30
                        ->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
351 30
                        ->scalarNode('service')->end()
352 30
                    ->end()
353 30
                ->end()
354 30
                ->arrayNode('model_to_elastica_transformer')
355 30
                    ->addDefaultsIfNotSet()
356 30
                    ->children()
357 30
                        ->scalarNode('service')->end()
358 30
                    ->end()
359 30
                ->end()
360 30
                ->arrayNode('persister')
361 30
                    ->addDefaultsIfNotSet()
362 30
                    ->children()
363 30
                        ->enumNode('refresh')
364 30
                            ->treatTrueLike('true')
365 30
                            ->treatFalseLike('false')
366 30
                            ->values(['true', 'wait_for', 'false'])
367 30
                        ->end()
368 30
                        ->scalarNode('service')->end()
369 30
                    ->end()
370 30
                ->end()
371 30
            ->end();
372
373 30
        return $node;
374
    }
375
376
    /**
377
     * @return ArrayNodeDefinition|\Symfony\Component\Config\Definition\Builder\NodeDefinition
378
     */
379 30
    protected function getSerializerNode()
380
    {
381 30
        $node = $this->createTreeBuilderNode('serializer');
382
383
        $node
384 30
            ->addDefaultsIfNotSet()
385 30
            ->children()
386 30
                ->arrayNode('groups')
387 30
                    ->treatNullLike([])
388 30
                    ->prototype('scalar')->end()
389 30
                ->end()
390 30
                ->scalarNode('version')->end()
391 30
                ->booleanNode('serialize_null')
392 30
                    ->defaultFalse()
393 30
                ->end()
394 30
            ->end();
395
396 30
        return $node;
397
    }
398
399
    /**
400
     * Adds the configuration for the "clients" key.
401
     */
402 30
    private function addClientsSection(ArrayNodeDefinition $rootNode)
403
    {
404
        $rootNode
405 30
            ->fixXmlConfig('client')
406 30
            ->children()
407 30
                ->arrayNode('clients')
408 30
                    ->useAttributeAsKey('id')
409 30
                    ->prototype('array')
410 30
                        ->performNoDeepMerging()
411
                        // Elastica names its properties with camel case, support both
412 30
                        ->beforeNormalization()
413 30
                        ->ifTrue(function ($v) {
414 29
                            return isset($v['connection_strategy']);
415 30
                        })
416 30
                        ->then(function ($v) {
417 3
                            $v['connectionStrategy'] = $v['connection_strategy'];
418 3
                            unset($v['connection_strategy']);
419
420 3
                            return $v;
421 30
                        })
422 30
                        ->end()
423
                        // If there is no connections array key defined, assume a single connection.
424 30
                        ->beforeNormalization()
425 30
                        ->ifTrue(function ($v) {
426 29
                            return is_array($v) && !array_key_exists('connections', $v);
427 30
                        })
428 30
                        ->then(function ($v) {
429
                            return [
430 29
                                'connections' => [$v],
431
                            ];
432 30
                        })
433 30
                        ->end()
434 30
                        ->children()
435 30
                            ->arrayNode('connections')
436 30
                                ->requiresAtLeastOneElement()
437 30
                                ->prototype('array')
438 30
                                    ->fixXmlConfig('header')
439 30
                                    ->children()
440 30
                                        ->scalarNode('url')
441 30
                                            ->validate()
442 30
                                                ->ifTrue(function ($url) {
443 17
                                                    return $url && '/' !== substr($url, -1);
444 30
                                                })
445 30
                                                ->then(function ($url) {
446 17
                                                    return $url.'/';
447 30
                                                })
448 30
                                            ->end()
449 30
                                        ->end()
450 30
                                        ->scalarNode('username')->end()
451 30
                                        ->scalarNode('password')->end()
452 30
                                        ->scalarNode('host')->end()
453 30
                                        ->scalarNode('port')->end()
454 30
                                        ->scalarNode('proxy')->end()
455 30
                                        ->scalarNode('aws_access_key_id')->end()
456 30
                                        ->scalarNode('aws_secret_access_key')->end()
457 30
                                        ->scalarNode('aws_region')->end()
458 30
                                        ->scalarNode('aws_session_token')->end()
459 30
                                        ->booleanNode('ssl')->defaultValue(false)->end()
460 30
                                        ->scalarNode('logger')
461 30
                                            ->defaultValue($this->debug ? 'fos_elastica.logger' : false)
462 30
                                            ->treatNullLike('fos_elastica.logger')
463 30
                                            ->treatTrueLike('fos_elastica.logger')
464 30
                                        ->end()
465 30
                                        ->booleanNode('compression')->defaultValue(false)->end()
466 30
                                        ->arrayNode('headers')
467 30
                                            ->normalizeKeys(false)
468 30
                                            ->useAttributeAsKey('name')
469 30
                                            ->prototype('scalar')->end()
470 30
                                        ->end()
471 30
                                        ->arrayNode('curl')
472 30
                                            ->useAttributeAsKey(CURLOPT_SSL_VERIFYPEER)
473 30
                                            ->prototype('boolean')->end()
474 30
                                        ->end()
475 30
                                        ->scalarNode('transport')->end()
476 30
                                        ->scalarNode('timeout')->end()
477 30
                                        ->scalarNode('connectTimeout')->end()
478 30
                                        ->scalarNode('retryOnConflict')
479 30
                                            ->defaultValue(0)
480 30
                                        ->end()
481 30
                                    ->end()
482 30
                                ->end()
483 30
                            ->end()
484 30
                            ->scalarNode('timeout')->end()
485 30
                            ->scalarNode('connectTimeout')->end()
486 30
                            ->scalarNode('headers')->end()
487 30
                            ->scalarNode('connectionStrategy')->defaultValue('Simple')->end()
488 30
                        ->end()
489 30
                    ->end()
490 30
                ->end()
491 30
            ->end()
492
        ;
493 30
    }
494
495
    /**
496
     * Adds the configuration for the "indexes" key.
497
     */
498 30
    private function addIndexesSection(ArrayNodeDefinition $rootNode)
499
    {
500
        $rootNode
501 30
            ->fixXmlConfig('index')
502 30
            ->children()
503 30
                ->arrayNode('indexes')
504 30
                    ->useAttributeAsKey('name')
505 30
                    ->prototype('array')
506 30
                        ->children()
507 30
                            ->scalarNode('index_name')
508 30
                                ->info('Defaults to the name of the index, but can be modified if the index name is different in ElasticSearch')
509 30
                            ->end()
510 30
                            ->booleanNode('use_alias')->defaultValue(false)->end()
511 30
                            ->scalarNode('client')->end()
512 30
                            ->scalarNode('finder')
513 30
                                ->treatNullLike(true)
514 30
                                ->defaultFalse()
515 30
                            ->end()
516 30
                            ->arrayNode('type_prototype')
517 30
                                ->children()
518 30
                                    ->scalarNode('analyzer')->end()
519 30
                                    ->append($this->getPersistenceNode())
520 30
                                    ->append($this->getSerializerNode())
521 30
                                ->end()
522 30
                            ->end()
523 30
                            ->variableNode('settings')->defaultValue([])->end()
524 30
                        ->end()
525 30
                        ->append($this->getTypesNode())
526 30
                    ->end()
527 30
                ->end()
528 30
            ->end()
529
        ;
530 30
    }
531
532
    /**
533
     * @return ArrayNodeDefinition|\Symfony\Component\Config\Definition\Builder\NodeDefinition
534
     */
535 30
    private function createTreeBuilderNode($name)
536
    {
537 30
        $builder = new TreeBuilder($name);
538
539 30
        if (method_exists($builder, 'getRootNode')) {
540 30
            $node = $builder->getRootNode();
541
        } else {
542
            // BC layer for symfony/config 4.1 and older
543
            $node = $builder->root($name);
544
        }
545
546 30
        return $node;
547
    }
548
}
549