Completed
Push — upgrade ( f08820 )
by Simonas
14:39
created

Configuration::getConfigTreeBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 26
rs 9.504
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of the ONGR package.
5
 *
6
 * (c) NFQ Technologies UAB <[email protected]>
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 ONGR\ElasticsearchBundle\DependencyInjection;
13
14
use Psr\Log\LogLevel;
15
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
16
use Symfony\Component\Config\Definition\ConfigurationInterface;
17
18
/**
19
 * This is the class that validates and merges configuration from app/config files.
20
 */
21
class Configuration implements ConfigurationInterface
22
{
23
    /**
24
     * {@inheritdoc}
25
     */
26
    public function getConfigTreeBuilder()
27
    {
28
        $treeBuilder = new TreeBuilder();
29
        $rootNode = $treeBuilder->root('ongr_elasticsearch');
30
31
        $rootNode
32
            ->children()
33
            ->booleanNode('cache')
34
                ->info(
35
                    'Enables cache handler to store metadata and other data to the cache. '.
36
                    'Default value is kernel.debug parameter.'
37
                )
38
            ->end()
39
            ->booleanNode('profiler')
40
                ->info(
41
                    'Enables Symfony profiler for elasticsearch executed queries.'.
42
                    'Default value is kernel.debug parameter. '
43
                )
44
            ->end()
45
            ->append($this->getAnalysisNode())
46
            ->append($this->getManagersNode())
0 ignored issues
show
Deprecated Code introduced by
The method ONGR\ElasticsearchBundle...tion::getManagersNode() has been deprecated with message: Elasticsearch 6 deprecated es types, there will be 1 type per index only, so the managers makes no sense anymore.
Will be removed in v7, use indexes node 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...
47
            ->append($this->getIndexesNode())
48
            ->end();
49
50
        return $treeBuilder;
51
    }
52
53
    /**
54
     * Analysis configuration node.
55
     *
56
     * @return \Symfony\Component\Config\Definition\Builder\NodeDefinition
57
     */
58
    private function getAnalysisNode()
59
    {
60
        $builder = new TreeBuilder();
61
        $node = $builder->root('analysis');
62
63
        $node
64
            ->info('Defines analyzers, tokenizers and filters')
65
            ->addDefaultsIfNotSet()
66
            ->children()
67
                ->arrayNode('tokenizer')
68
                    ->defaultValue([])
69
                    ->prototype('variable')->end()
70
                ->end()
71
                ->arrayNode('filter')
72
                    ->defaultValue([])
73
                    ->prototype('variable')->end()
74
                ->end()
75
                ->arrayNode('analyzer')
76
                    ->defaultValue([])
77
                    ->prototype('variable')->end()
78
                ->end()
79
                ->arrayNode('normalizer')
80
                    ->defaultValue([])
81
                    ->prototype('variable')->end()
82
                ->end()
83
                ->arrayNode('char_filter')
84
                    ->defaultValue([])
85
                    ->prototype('variable')->end()
86
                ->end()
87
            ->end();
88
89
        return $node;
90
    }
91
92
    /**
93
     * Managers configuration node.
94
     *
95
     * @deprecated Elasticsearch 6 deprecated es types, there will be 1 type per index only, so the managers makes no sense anymore.
96
     * Will be removed in v7, use indexes node instead.
97
     *
98
     * @return \Symfony\Component\Config\Definition\Builder\NodeDefinition
99
     */
100
    private function getManagersNode()
101
    {
102
        $builder = new TreeBuilder();
103
        $node = $builder->root('managers');
104
105
        $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...
106
            ->useAttributeAsKey('name')
107
            ->info('Maps managers to connections and bundles')
108
            ->prototype('array')
109
                ->children()
110
                    ->arrayNode('index')
111
                        ->children()
112
                            ->scalarNode('index_name')
113
                                ->isRequired()
114
                                ->info('Sets index name for connection.')
115
                            ->end()
116
                            ->arrayNode('hosts')
117
                                ->info('Defines hosts to connect to.')
118
                                ->defaultValue(['127.0.0.1:9200'])
119
                                ->prototype('scalar')
120
                                ->end()
121
                            ->end()
122
                            ->arrayNode('settings')
123
                                ->defaultValue(
124
                                    [
125
                                        'number_of_replicas' => 0,
126
                                        'number_of_shards' => 1,
127
                                        'refresh_interval' => -1,
128
                                    ]
129
                                )
130
                                ->info('Sets index settings for connection.')
131
                                ->prototype('variable')->end()
132
                            ->end()
133
                        ->end()
134
                    ->end()
135
                    ->integerNode('bulk_size')
136
                        ->min(0)
137
                        ->defaultValue(100)
138
                        ->info(
139
                            'Maximum documents size in the bulk container. ' .
140
                            'When the limit is reached it will auto-commit.'
141
                        )
142
                    ->end()
143
                    ->enumNode('commit_mode')
144
                        ->values(['refresh', 'flush', 'none'])
145
                        ->defaultValue('refresh')
146
                        ->info(
147
                            'The default type of commit for bulk queries.'
148
                        )
149
                    ->end()
150
                    ->arrayNode('logger')
151
                        ->info('Enables elasticsearch queries logging')
152
                        ->addDefaultsIfNotSet()
153
                        ->beforeNormalization()
154
                            ->ifTrue(
155
                                function ($v) {
156
                                    return is_bool($v);
157
                                }
158
                            )
159
                            ->then(
160
                                function ($v) {
161
                                    return ['enabled' => $v];
162
                                }
163
                            )
164
                        ->end()
165
                        ->children()
166
                            ->booleanNode('enabled')
167
                                ->info('enables logging')
168
                                ->defaultFalse()
169
                            ->end()
170
                            ->scalarNode('level')
171
                                ->info('Sets PSR logging level.')
172
                                ->defaultValue(LogLevel::WARNING)
173
                                ->validate()
174
                                ->ifNotInArray((new \ReflectionClass('Psr\Log\LogLevel'))->getConstants())
175
                                    ->thenInvalid('Invalid PSR log level.')
176
                                ->end()
177
                            ->end()
178
                            ->scalarNode('log_file_name')
179
                                ->info('Log filename. The default name is the index name.')
180
                                ->defaultValue(null)
181
                            ->end()
182
                        ->end()
183
                    ->end()
184
                    ->arrayNode('mappings')
185
                        ->info('Maps manager to the bundles. f.e. AppBundle')
186
                        ->prototype('variable')->end()
187
                    ->end()
188
                    ->booleanNode('force_commit')
189
                        ->info('Forces commit to the elasticsearch on kernel terminate event.')
190
                        ->defaultTrue()
191
                    ->end()
192
                ->end()
193
            ->end();
194
195
        return $node;
196
    }
197
198
    /**
199
     * Indexes configuration node.
200
     *
201
     * @return \Symfony\Component\Config\Definition\Builder\NodeDefinition
202
     */
203
    private function getIndexesNode()
204
    {
205
        $builder = new TreeBuilder();
206
        $node = $builder->root('indexes');
207
208
        $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...
209
//            ->isRequired()
210
//            ->requiresAtLeastOneElement()
211
            ->useAttributeAsKey('name')
212
            ->info('Creates index service for the specific document. The configuration key is the index name.')
213
            ->prototype('array')
214
            ->children()
215
                ->arrayNode('hosts')
216
                    ->info('Defines hosts to connect to.')
217
                    ->defaultValue(['127.0.0.1:9200'])
218
                    ->prototype('scalar')->end()
219
                ->end()
220
221
                ->scalarNode('mapping')
222
                    ->info('Namespace of the elasticsearch index/type document class.')
223
                    ->isRequired()
224
                ->end()
225
226
                ->arrayNode('settings')
227
                    ->defaultValue(
228
                        [
229
                            'number_of_replicas' => 0,
230
                            'number_of_shards' => 1,
231
                            'refresh_interval' => -1,
232
                        ]
233
                    )
234
                    ->info('Sets index settings for connection.')
235
                    ->prototype('variable')->end()
236
                ->end()
237
238
                ->booleanNode('force_commit')
239
                    ->info('Forces commit to the elasticsearch on kernel terminate event.')
240
                    ->defaultTrue()
241
                ->end()
242
243
                ->integerNode('bulk_size')
244
                    ->min(0)
245
                    ->defaultValue(100)
246
                    ->info(
247
                        'Maximum documents size in the bulk container. ' .
248
                        'When the limit is reached it will auto-commit.'
249
                    )
250
                ->end()
251
252
                    ->enumNode('commit_mode')
253
                    ->values(['refresh', 'flush', 'none'])
254
                    ->defaultValue('refresh')
255
                    ->info(
256
                        'The default type of commit for bulk queries.'
257
                    )
258
                ->end()
259
260
                ->arrayNode('logger')
261
                    ->info('Enables elasticsearch queries logging')
262
                    ->addDefaultsIfNotSet()
263
                    ->beforeNormalization()
264
                    ->ifTrue(
265
                        function ($v) {
266
                            return is_bool($v);
267
                        }
268
                    )
269
                    ->then(
270
                        function ($v) {
271
                            return ['enabled' => $v];
272
                        }
273
                    )->end()
274
                    ->children()
275
                        ->booleanNode('enabled')
276
                            ->info('enables logging')
277
                            ->defaultFalse()
278
                        ->end()
279
                        ->scalarNode('level')
280
                            ->info('Sets PSR logging level.')
281
                            ->defaultValue(LogLevel::WARNING)
282
                            ->validate()
283
                            ->ifNotInArray((new \ReflectionClass('Psr\Log\LogLevel'))->getConstants())
284
                            ->thenInvalid('Invalid PSR log level.')
285
                        ->end()
286
                    ->end()
287
                    ->scalarNode('log_file_name')
288
                    ->info('Log filename. The default name is the index name.')
289
                    ->defaultValue(null)->end()
290
                    ->end()
291
                ->end()
292
293
            ->end()
294
            ->end()
295
            ->end()
296
297
            ;
298
299
        return $node;
300
    }
301
}
302