Completed
Push — master ( 736518...788a3c )
by Tobias
12:42 queued 10:54
created

Configuration::getConfigTreeBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 35
ccs 29
cts 30
cp 0.9667
rs 9.36
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the BazingaGeocoderBundle package.
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license    MIT License
11
 */
12
13
namespace Bazinga\GeocoderBundle\DependencyInjection;
14
15
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
16
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
17
use Symfony\Component\Config\Definition\ConfigurationInterface;
18
19
/**
20
 * @author William Durand <[email protected]>
21
 */
22
class Configuration implements ConfigurationInterface
23
{
24
    /**
25
     * Whether to use the debug mode.
26
     *
27
     * @see https://github.com/doctrine/DoctrineBundle/blob/v1.5.2/DependencyInjection/Configuration.php#L31-L41
28
     *
29
     * @var bool
30
     */
31
    private $debug;
32
33
    /**
34
     * @param bool $debug
35
     */
36 30
    public function __construct($debug)
37
    {
38 30
        $this->debug = (bool) $debug;
39 30
    }
40
41
    /**
42
     * Proxy to get root node for Symfony < 4.2.
43
     *
44
     * @param TreeBuilder $treeBuilder
45
     * @param string      $name
46
     *
47
     * @return NodeDefinition
48
     */
49 30
    protected function getRootNode(TreeBuilder $treeBuilder, string $name)
50
    {
51 30
        if (\method_exists($treeBuilder, 'getRootNode')) {
52 30
            return $treeBuilder->getRootNode();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $treeBuilder->getRootNode(); (Symfony\Component\Config...\Builder\NodeDefinition) is incompatible with the return type documented by Bazinga\GeocoderBundle\D...figuration::getRootNode of type Bazinga\GeocoderBundle\D...njection\NodeDefinition.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
53
        } else {
54
            return $treeBuilder->root($name);
0 ignored issues
show
Bug introduced by
The method root() does not seem to exist on object<Symfony\Component...on\Builder\TreeBuilder>.

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

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

Loading history...
55
        }
56
    }
57
58
    /**
59
     * Generates the configuration tree builder.
60
     *
61
     * @return TreeBuilder The tree builder
62
     */
63 30
    public function getConfigTreeBuilder()
64
    {
65 30
        $treeBuilder = new TreeBuilder('bazinga_geocoder');
66
67 30
        $this->getRootNode($treeBuilder, 'bazinga_geocoder')
68 30
            ->children()
69 30
            ->append($this->getProvidersNode())
70 30
            ->arrayNode('profiling')
71 30
                ->addDefaultsIfNotSet()
72 30
                ->treatFalseLike(['enabled' => false])
73 30
                ->treatTrueLike(['enabled' => true])
74 30
                ->treatNullLike(['enabled' => $this->debug])
75 30
                ->info('Extend the debug profiler with information about requests.')
76 30
                ->children()
77 30
                    ->booleanNode('enabled')
78 30
                        ->info('Turn the toolbar on or off. Defaults to kernel debug mode.')
79 30
                        ->defaultValue($this->debug)
80 30
                    ->end()
81 30
                ->end()
82 30
            ->end()
83 30
            ->arrayNode('fake_ip')
84 30
                ->beforeNormalization()
85 30
                ->ifString()
86
                    ->then(function ($value) {
87
                        return ['ip' => $value];
88 30
                    })
89 30
                ->end()
90 30
                ->canBeEnabled()
91 30
                ->children()
92 30
                    ->scalarNode('ip')->defaultNull()->end()
93 30
                ->end()
94 30
            ->end();
95
96 30
        return $treeBuilder;
97
    }
98
99
    /**
100
     * @return ArrayNodeDefinition
101
     */
102 30
    private function getProvidersNode()
103
    {
104 30
        $treeBuilder = new TreeBuilder('providers');
105
106 30
        return $this->getRootNode($treeBuilder, 'providers')
107 30
            ->requiresAtLeastOneElement()
108 30
            ->useAttributeAsKey('name')
109 30
            ->prototype('array')
110 30
            ->fixXmlConfig('plugin')
111 30
                ->children()
112 30
                    ->scalarNode('factory')->isRequired()->cannotBeEmpty()->end()
113 30
                    ->variableNode('options')->defaultValue([])->end()
114 30
                    ->scalarNode('cache')->defaultNull()->end()
115 30
                    ->scalarNode('cache_lifetime')->defaultNull()->end()
116 30
                    ->scalarNode('cache_precision')
117 30
                        ->defaultNull()
118 30
                        ->info('Precision of the coordinates to cache.')
119 30
                        ->end()
120 30
                    ->scalarNode('limit')->defaultNull()->end()
121 30
                    ->scalarNode('locale')->defaultNull()->end()
122 30
                    ->scalarNode('logger')->defaultNull()->end()
123 30
                    ->arrayNode('aliases')
124 30
                        ->prototype('scalar')->end()
125 30
                    ->end()
126 30
                    ->append($this->createClientPluginNode())
127 30
                ->end()
128 30
            ->end();
129
    }
130
131
    /**
132
     * Create plugin node of a client.
133
     *
134
     * @return ArrayNodeDefinition The plugin node
135
     */
136 30
    private function createClientPluginNode()
137
    {
138 30
        $builder = new TreeBuilder('plugins');
139 30
        $node = $this->getRootNode($builder, 'plugins');
140
141
        /** @var ArrayNodeDefinition $pluginList */
142
        $pluginList = $node
143 30
            ->info('A list of plugin service ids. The order is important.')
144 30
            ->prototype('array')
145
        ;
146
        $pluginList
147
            // support having just a service id in the list
148 30
            ->beforeNormalization()
149
                ->always(function ($plugin) {
150 2
                    if (is_string($plugin)) {
151
                        return [
152 2
                            'reference' => [
153
                                'enabled' => true,
154 2
                                'id' => $plugin,
155
                            ],
156
                        ];
157
                    }
158
159 1
                    return $plugin;
160 30
                })
161 30
            ->end()
162
        ;
163
164
        $pluginList
165 30
            ->children()
166 30
                ->arrayNode('reference')
167 30
                    ->canBeEnabled()
168 30
                    ->info('Reference to a plugin service')
169 30
                    ->children()
170 30
                        ->scalarNode('id')
171 30
                            ->info('Service id of a plugin')
172 30
                            ->isRequired()
173 30
                            ->cannotBeEmpty()
174 30
                        ->end()
175 30
                    ->end()
176 30
                ->end()
177 30
            ->end()
178 30
        ->end();
179
180 30
        return $node;
181
    }
182
}
183