Completed
Push — master ( fdba2f...3b8c9c )
by Peter
15s queued 11s
created

Configuration::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * GpsLab component.
6
 *
7
 * @author    Peter Gribanov <[email protected]>
8
 * @copyright Copyright (c) 2017, Peter Gribanov
9
 * @license   http://opensource.org/licenses/MIT
10
 */
11
12
namespace GpsLab\Bundle\GeoIP2Bundle\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
    private const URL = 'https://download.maxmind.com/app/geoip_download?edition_id=%s&license_key=%s&suffix=tar.gz';
21
22
    private const PATH = '%s/%s.mmdb';
23
24
    /**
25
     * @var string
26
     */
27
    private $cache_dir;
28
29
    /**
30
     * @param string|null $cache_dir
31
     */
32 41
    public function __construct(?string $cache_dir)
33
    {
34 41
        $this->cache_dir = $cache_dir ?: sys_get_temp_dir();
35 41
    }
36
37
    /**
38
     * @return TreeBuilder
39
     */
40 41
    public function getConfigTreeBuilder(): TreeBuilder
41
    {
42 41
        $tree_builder = new TreeBuilder('gpslab_geoip');
43
44 41
        if (method_exists($tree_builder, 'getRootNode')) {
45
            // Symfony 4.2 +
46
            $root_node = $tree_builder->getRootNode();
47
        } else {
48
            // Symfony 4.1 and below
49 41
            $root_node = $tree_builder->root('gpslab_geoip');
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...
50
        }
51
52
        // normalize default_database from databases
53
        $root_node
54 41
            ->beforeNormalization()
55
            ->ifTrue(static function ($v): bool {
56
                return
57 39
                    is_array($v) &&
58 39
                    !array_key_exists('default_database', $v) &&
59 39
                    array_key_exists('databases', $v) &&
60 39
                    is_array($v['databases']);
61 41
            })
62
            ->then(static function (array $v): array {
63 14
                $keys = array_keys($v['databases']);
64 14
                $v['default_database'] = reset($keys);
65
66 14
                return $v;
67 41
            });
68
69
        // normalize databases root configuration to default_database
70
        $root_node
71 41
            ->beforeNormalization()
72
            ->ifTrue(static function ($v): bool {
73 39
                return is_array($v) && !array_key_exists('databases', $v) && !array_key_exists('database', $v);
74 41
            })
75
            ->then(static function (array $v): array {
76
                // key that should not be rewritten to the database config
77 12
                $database = [];
78 12
                foreach ($v as $key => $value) {
79 10
                    if ($key !== 'default_database') {
80 10
                        $database[$key] = $v[$key];
81 10
                        unset($v[$key]);
82
                    }
83
                }
84 12
                $v['default_database'] = isset($v['default_database']) ? (string) $v['default_database'] : 'default';
85 12
                $v['databases'] = [$v['default_database'] => $database];
86
87 12
                return $v;
88 41
            });
89
90
        // default_database should be exists in databases
91
        $root_node
92 41
            ->validate()
93
                ->ifTrue(static function ($v): bool {
94
                    return
95 25
                        is_array($v) &&
96 25
                        array_key_exists('default_database', $v) &&
97 25
                        array_key_exists('databases', $v) &&
98 25
                        !array_key_exists($v['default_database'], $v['databases']);
99 41
                })
100
                ->then(static function (array $v): array {
101 8
                    $databases = implode('", "', array_keys($v['databases']));
102
103 8
                    throw new \InvalidArgumentException(sprintf('Undefined default database "%s". Available "%s" databases.', $v['default_database'], $databases));
104 41
                });
105
106
        // add license to databases config if not exists (allow use a global license for all databases)
107
        $root_node
108 41
            ->beforeNormalization()
109
            ->ifTrue(static function ($v): bool {
110
                return
111 39
                    is_array($v) &&
112 39
                    array_key_exists('license', $v) &&
113 39
                    array_key_exists('databases', $v) &&
114 39
                    is_array($v['databases']);
115 41
            })
116
            ->then(static function (array $v): array {
117 6
                foreach ($v['databases'] as $name => $database) {
118 4
                    if (!array_key_exists('license', $database)) {
119 4
                        $v['databases'][$name]['license'] = $v['license'];
120
                    }
121
                }
122
123 6
                return $v;
124 41
            });
125
126
        // add locales to databases config if not exists (allow use a global locales for all databases)
127
        $root_node
128 41
            ->beforeNormalization()
129
            ->ifTrue(static function ($v): bool {
130
                return
131 39
                    is_array($v) &&
132 39
                    array_key_exists('locales', $v) &&
133 39
                    array_key_exists('databases', $v) &&
134 39
                    is_array($v['databases']);
135 41
            })
136
            ->then(static function (array $v): array {
137 2
                foreach ($v['databases'] as $name => $database) {
138 2
                    if (!array_key_exists('locales', $database)) {
139 2
                        $v['databases'][$name]['locales'] = $v['locales'];
140
                    }
141
                }
142
143 2
                return $v;
144 41
            });
145
146
        // validate database locales
147
        $root_node
148 41
            ->validate()
149
                ->ifTrue(static function ($v): bool {
150
                    return
151 17
                        is_array($v) &&
152 17
                        array_key_exists('databases', $v) &&
153 17
                        is_array($v['databases']);
154 41
                })
155
                ->then(static function (array $v): array {
156 17
                    foreach ($v['databases'] as $name => $database) {
157 17
                        if (!array_key_exists('locales', $database) || empty($database['locales'])) {
158 17
                            throw new \InvalidArgumentException(sprintf('The list of locales should not be empty in databases "%s".', $name));
159
                        }
160
                    }
161
162 17
                    return $v;
163 41
                });
164
165 41
        $root_node->fixXmlConfig('locale');
166 41
        $locales = $root_node->children()->arrayNode('locales');
167 41
        $locales->prototype('scalar');
168
        $locales
169 41
            ->treatNullLike([])
170 41
            ->defaultValue(['en']);
171
172 41
        $root_node->children()->scalarNode('license');
173
174 41
        $default_database = $root_node->children()->scalarNode('default_database');
175 41
        $default_database->defaultValue('default');
176
177 41
        $root_node->fixXmlConfig('database');
178 41
        $root_node->append($this->getDatabaseNode());
179
180 41
        return $tree_builder;
181
    }
182
183
    /**
184
     * @return ArrayNodeDefinition
185
     */
186 41
    private function getDatabaseNode(): ArrayNodeDefinition
187
    {
188 41
        $tree_builder = new TreeBuilder('databases');
189
190 41
        if (method_exists($tree_builder, 'getRootNode')) {
191
            // Symfony 4.2 +
192
            $root_node = $tree_builder->getRootNode();
193
        } else {
194
            // Symfony 4.1 and below
195 41
            $root_node = $tree_builder->root('databases');
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...
196
        }
197
198
        /** @var ArrayNodeDefinition $database_node */
199
        $database_node = $root_node
200 41
            ->requiresAtLeastOneElement()
201 41
            ->useAttributeAsKey('name')
202 41
            ->prototype('array');
203
204
        // normalize url from license and edition
205
        $database_node
206 41
            ->beforeNormalization()
207
            ->ifTrue(static function ($v): bool {
208
                return
209 31
                    is_array($v) &&
210 31
                    !array_key_exists('url', $v) &&
211 31
                    array_key_exists('license', $v) &&
212 31
                    array_key_exists('edition', $v);
213 41
            })
214
            ->then(static function (array $v): array {
215 19
                $v['url'] = sprintf(self::URL, urlencode($v['edition']), urlencode($v['license']));
216
217 19
                return $v;
218 41
            });
219
220
        // normalize path from edition
221
        $database_node
222 41
            ->beforeNormalization()
223
            ->ifTrue(static function ($v): bool {
224 31
                return is_array($v) && !array_key_exists('path', $v) && array_key_exists('edition', $v);
225 41
            })
226
            ->then(function (array $v): array {
227 23
                $v['path'] = sprintf(self::PATH, $this->cache_dir, $v['edition']);
228
229 23
                return $v;
230 41
            });
231
232 41
        $url = $database_node->children()->scalarNode('url');
233 41
        $url->isRequired();
234
        // url must be a valid URL
235
        $url
236 41
            ->validate()
237
            ->ifTrue(static function ($v): bool {
238 27
                return is_string($v) && !filter_var($v, FILTER_VALIDATE_URL);
239 41
            })
240
            ->then(static function (string $v): array {
241 2
                throw new \InvalidArgumentException(sprintf('URL "%s" must be valid.', $v));
242 41
            });
243
244 41
        $path = $database_node->children()->scalarNode('path');
245 41
        $path->isRequired();
246
247 41
        $database_node->fixXmlConfig('locale');
248 41
        $locales = $database_node->children()->arrayNode('locales');
249 41
        $locales->prototype('scalar');
250
        $locales
251 41
            ->treatNullLike([])
252 41
            ->requiresAtLeastOneElement()
253 41
            ->defaultValue(['en']);
254
255 41
        $database_node->children()->scalarNode('license');
256
257 41
        $database_node->children()->scalarNode('edition');
258
259 41
        return $root_node;
260
    }
261
}
262