Configuration::getConfigTreeBuilder()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 8
nc 2
nop 0
dl 0
loc 17
ccs 10
cts 10
cp 1
crap 2
rs 10
c 1
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of ocubom/twig-extra-bundle
5
 *
6
 * © Oscar Cubo Medina <https://ocubom.github.io>
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 Ocubom\TwigExtraBundle\DependencyInjection;
13
14
use Iconify\IconsJSON\Finder as IconifyFinder;
15
use Ocubom\TwigExtraBundle\Extensions;
16
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
17
use Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition;
18
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
19
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
20
use Symfony\Component\Config\Definition\ConfigurationInterface;
21
22
class Configuration implements ConfigurationInterface
23
{
24 14
    public function getConfigTreeBuilder(): TreeBuilder
25
    {
26 14
        $builder = new TreeBuilder('ocubom_twig_extra');
27 14
        $root = $builder->getRootNode();
28
29
        // Register available extensions
30 14
        foreach (Extensions::getClasses() as $name => $class) {
31 14
            $call = str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $name)));
32 14
            $this->{'add'.$call.'Section'}(
33 14
                $this->createSectionNode($root, $name, class_exists($class))
34 14
            );
35
        }
36
37
        // Headers listener included on this bundle
38 14
        $this->addHttpHeaderSection($root);
39
40 14
        return $builder;
41
    }
42
43 14
    private function createSectionNode(
44
        ArrayNodeDefinition $root,
45
        string $name,
46
        bool $canBeDisabled = true
47
    ): ArrayNodeDefinition {
48 14
        $node = $root->children()->arrayNode($name);
49
        assert($node instanceof ArrayNodeDefinition);
50
51 14
        $node->info(sprintf('Twig %s Extension', ucfirst($name)));
52 14
        $node->{$canBeDisabled ? 'canBeDisabled' : 'canBeEnabled'}();
53
54 14
        $enabled = $node->find('enabled');
55
        assert($enabled instanceof BooleanNodeDefinition);
56 14
        $enabled->info('Enable or disable this extension');
57
58 14
        return $node;
59
    }
60
61 14
    private function addHttpHeaderSection(ArrayNodeDefinition $root): void
62
    {
63 14
        $root
64 14
            ->fixXmlConfig('http_header')
65 14
            ->children()
66 14
                ->arrayNode('http_headers')
67 14
                    ->info(implode("\n", [
68 14
                        'HTTP headers that must be set.',
69 14
                        'The listener will only be registered if at least one rule is enabled.',
70 14
                    ]))
71 14
                    ->prototype('array')
72 14
                        ->treatFalseLike(['enabled' => false])
73 14
                        ->treatTrueLike(['enabled' => true])
74 14
                        ->treatNullLike(['enabled' => true])
75 14
                        ->children()
76 14
                            ->booleanNode('enabled')
77 14
                                ->info('Enable or disable this rule')
78 14
                                ->defaultTrue()
79 14
                            ->end()
80 14
                            ->scalarNode('name')
81 14
                                ->info('The header name to be added.')
82 14
                                ->example('X-UA-Compatible')
83 14
                                ->isRequired()
84 14
                                ->cannotBeEmpty()
85 14
                            ->end()
86 14
                            ->scalarNode('pattern')
87 14
                                ->info('A regular expression to extract the header value.')
88 14
                                ->example('@@[\p{Zs}]*<meta\s+(?:http-equiv="X-UA-Compatible"\s+content="([^"]+)"|content="([^"]+)"\s+http-equiv="X-UA-Compatible")\s*>\p{Zs}*\n?@i')
89 14
                                ->isRequired()
90 14
                                ->cannotBeEmpty()
91 14
                            ->end()
92 14
                            ->scalarNode('value')
93 14
                                ->info('The format of the value (printf processed using the matched value).')
94 14
                                ->example('%2$s')
95 14
                                ->isRequired()
96 14
                                ->cannotBeEmpty()
97 14
                            ->end()
98 14
                            ->scalarNode('replace')
99 14
                                ->info('The text that replaces the match in the original (printf processed using the matched value).')
100 14
                                ->defaultValue('%s')
101 14
                            ->end()
102 14
                            ->arrayNode('formats')
103 14
                                ->info('The response formats when this replacement must be done.')
104 14
                                ->example('["text/html"]')
105 14
                                ->prototype('scalar')->end()
106 14
                            ->end()
107 14
                        ->end()
108 14
                    ->end()
109 14
                ->end()
110 14
            ->end();
111
    }
112
113 14
    private function addHtmlSection(ArrayNodeDefinition $root): void
114
    {
115 14
        $children = $root
116 14
            ->children()
117 14
                ->arrayNode('compression')
118 14
                    ->info('Compress HTML output')
119 14
                    ->addDefaultsIfNotSet()
120 14
                    ->children();
121
122 14
        $children
123 14
            ->booleanNode('force')
124 14
                ->info('Force compression')
125 14
                ->defaultFalse()
126 14
            ->end();
127
128 14
        $children
129 14
            ->enumNode('level')
130 14
                ->info('The level of compression to use')
131 14
                ->defaultValue('smallest')
132 14
                ->values([
133 14
                    'none',
134 14
                    'fastest',
135 14
                    'normal',
136 14
                    'smallest',
137 14
                ])
138 14
            ->end();
139
    }
140
141 14
    private function addSvgSection(ArrayNodeDefinition $root): void
142
    {
143 14
        $providers = [
144 14
            'file_system' => [
145 14
                'name' => 'Local File System',
146 14
                'paths' => [
147 14
                    '%kernel.project_dir%/assets',
148 14
                    '%kernel.project_dir%/node_modules',
149 14
                ],
150 14
            ],
151 14
            'font_awesome' => [
152 14
                'name' => 'FontAwesome',
153 14
                'paths' => [
154 14
                    '%kernel.project_dir%/node_modules/@fortawesome/fontawesome-pro/svgs',
155 14
                    '%kernel.project_dir%/node_modules/@fortawesome/fontawesome-free/svgs',
156 14
                    '%kernel.project_dir%/vendor/fortawesome/font-awesome/svgs/',
157 14
                ],
158 14
            ],
159 14
            'iconify' => [
160 14
                'name' => 'Iconify',
161 14
                'paths' => [
162 14
                    '%kernel.project_dir%/node_modules/@iconify-json/',
163 14
                    '%kernel.project_dir%/node_modules/@iconify/json/',
164 14
                    '%kernel.project_dir%/vendor/iconify/json/',
165 14
                    class_exists(IconifyFinder::class) ? IconifyFinder::rootDir() : '',
166 14
                ],
167 14
                'runtime' => function (NodeBuilder $parent): void {
168 14
                    $parent
169 14
                        ->arrayNode('svg_framework')
170 14
                            ->info('Enable SVG Framework Server Side Rendering on classes (empty to disable).')
171 14
                            ->defaultValue(['iconify', 'iconify-inline'])
172 14
                            ->prototype('scalar')->end()
173 14
                        ->end();
174
175 14
                    $parent
176 14
                        ->arrayNode('web_component')
177 14
                            ->info('Enable Web Component Server Side Rendering on tags (empty to disable).')
178 14
                            ->defaultValue(['icon', 'iconify-icon'])
179 14
                            ->prototype('scalar')->end()
180 14
                        ->end();
181 14
                },
182 14
                'loader' => function (NodeBuilder $parent): void {
183 14
                    $parent
184 14
                        ->scalarNode('cache_dir')
185 14
                            ->info('Enable cache on this path (empty to disable).')
186 14
                            ->defaultValue('%kernel.cache_dir%/iconify')
187 14
                        ->end();
188 14
                },
189 14
            ],
190 14
        ];
191
192 14
        $root
193 14
            ->beforeNormalization()
194 14
                ->always(static function ($v) {
195
                    // Convert to finders (1.x) configuration
196 12
                    $v['finders'] = $v['finders'] ?? [];
197
                    // Default finder
198 12
                    $v['finders']['default'] = $v['finders']['default'] ?? $v['search_path'] ?? [];
199 12
                    unset($v['search_path']);
200
                    // FontAwesome
201 12
                    $v['finders']['fontawesome'] = $v['finders']['fontawesome'] ?? $v['fontawesome']['search_path'] ?? [];
202 12
                    unset($v['fontawesome']);
203
204
                    // Convert to providers (2.x) configuration
205 12
                    foreach ($v['finders'] as $key => $val) {
206 12
                        if (empty($val)) {
207 10
                            continue;
208
                        }
209
210
                        switch ($key) {
211 2
                            case 'default':
212 2
                                $v['providers']['file_system']['paths'] = $v['providers']['file_system']['paths'] ?? $val;
213 2
                                break;
214
215 2
                            case 'fontawesome':
216 2
                                $v['providers']['font_awesome']['paths'] = $v['providers']['font_awesome']['paths'] ?? $val;
217 2
                                break;
218
219
                            default:
220
                                $v['providers'][$key]['paths'] = $v['providers'][$key]['paths'] ?? $val;
221
                                break;
222
                        }
223
                    }
224 12
                    unset($v['finders']);
225
226 12
                    return $v;
227 14
                })
228 14
            ->end()
229 14
            ->validate()
230 14
                ->always(function ($v) {
231
                    // Clean deprecated configuration
232 12
                    unset($v['search_path']);
233 12
                    unset($v['fontawesome']);
234 12
                    unset($v['finders']);
235
236
                    // Enable/Disable providers
237 12
                    $enabled = 0;
238 12
                    foreach (array_keys($v['providers'] ?? []) as $key) {
239 12
                        $v['providers'][$key]['paths'] = array_filter($v['providers'][$key]['paths'] ?? []);
240 12
                        $v['providers'][$key]['enabled'] = $v['providers'][$key]['enabled'] && count($v['providers'][$key]['paths']) > 0;
241
242 12
                        if ($v['providers'][$key]['enabled']) {
243 12
                            ++$enabled;
244
                        }
245
                    }
246
247
                    // Disable if no provider are enabled
248 12
                    $v['enabled'] = $v['enabled'] && $enabled > 0;
249
250 12
                    return $v;
251 14
                })
252 14
            ->end();
253
254
        // Add providers (2.x) configuration
255 14
        $parent = $root
256 14
            ->fixXmlConfig('provider')
257 14
            ->children()
258 14
                ->arrayNode('providers')
259 14
                    ->info('SVG providers.')
260 14
                    ->addDefaultsIfNotSet();
261
262 14
        foreach ($providers as $key => $val) {
263 14
            $provider = $parent
264 14
                ->children()
265 14
                    ->arrayNode($key);
266
267 14
            $children = $provider
268 14
                    ->info(sprintf('%s provider.', $val['name']))
269 14
                    ->canBeDisabled()
270 14
                    ->children();
271
272 14
            $enabled = $provider->find('enabled');
273
            assert($enabled instanceof BooleanNodeDefinition);
274 14
            $enabled->info('Enable or disable this provider.');
275
276
            // Provider paths
277 14
            $provider->fixXmlConfig('path');
278 14
            $children
279 14
                ->arrayNode('paths')
280 14
                    ->info(sprintf('The paths where the %s files will be searched for.', $val['name']))
281 14
                    // ->example(sprintf('["%s"]', implode('", "', $val['paths'])))
282 14
                    ->defaultValue($val['paths'])
283 14
                    ->prototype('scalar')->end()
284 14
                ->end();
285
286
            // Extra configuration
287 14
            if (is_callable($val['loader'] ?? null)) {
288 14
                $val['loader']($children->arrayNode('loader')
289 14
                    ->info(sprintf('Loader configuration options for %s', $val['name']))
290 14
                    ->addDefaultsIfNotSet()
291 14
                    ->children()
292 14
                );
293
            }
294
295 14
            if (is_callable($val['runtime'] ?? null)) {
296 14
                $val['runtime']($children->arrayNode('runtime')
297 14
                    ->info(sprintf('Runtime configuration options for %s', $val['name']))
298 14
                    ->addDefaultsIfNotSet()
299 14
                    ->children()
300 14
                );
301
            }
302
        }
303
304
        // Add finders (1.x) configuration
305 14
        $root
306 14
            ->fixXmlConfig('finder')
307 14
            ->children()
308 14
                ->arrayNode('finders')
309 14
                    ->setDeprecated(
310 14
                        'ocubom/twig-extra-bundle',
311 14
                        '1.3',
312 14
                        'The "%node%" option is deprecated. Use "providers" instead.'
313 14
                    )
314 14
                    ->info('The paths where SVG files will be searched for.')
315 14
                    ->children()
316 14
                        ->arrayNode('default')
317 14
                            ->info('The default paths where the SVG files will be searched for.')
318 14
                            ->example(sprintf('["%s"]', implode('", "', $providers['file_system']['paths'])))
319 14
                            // ->defaultValue($providers['file_system']['paths'])
320 14
                            ->prototype('scalar')->end()
321 14
                        ->end()
322 14
                        ->arrayNode('fontawesome')
323 14
                            ->info('The paths where the FontAwesome files will be searched for.')
324 14
                            ->example(sprintf('["%s"]', implode('", "', $providers['font_awesome']['paths'])))
325 14
                            // ->defaultValue($providers['font_awesome']['paths'])
326 14
                            ->prototype('scalar')->end()
327 14
                        ->end()
328 14
                    ->end()
329 14
                ->end()
330 14
                ->arrayNode('search_path')
331 14
                    ->setDeprecated(
332 14
                        'ocubom/twig-extra-bundle',
333 14
                        '1.2',
334 14
                        'The "%node%" option is deprecated. Use "providers.filesystem" instead.'
335 14
                    )
336 14
                    ->info('The paths where the SVG files will be searched for.')
337 14
                    ->example(sprintf('["%s"]', implode('", "', $providers['file_system']['paths'])))
338 14
                    // ->defaultValue($providers['file_system']['paths'])
339 14
                    ->prototype('scalar')->end()
340 14
                ->end()
341 14
                ->arrayNode('fontawesome')
342 14
                    ->setDeprecated(
343 14
                        'ocubom/twig-extra-bundle',
344 14
                        '1.2',
345 14
                        'The "%node%" option is deprecated. Use "providers.fontawesome" instead.'
346 14
                    )
347 14
                    ->info('Configuration for FontAwesome.')
348 14
                    ->children()
349 14
                        ->arrayNode('search_path')
350 14
                            ->info('The paths where the FontAwesome files will be searched for.')
351 14
                            ->example(sprintf('["%s"]', implode('", "', $providers['font_awesome']['paths'])))
352 14
                            // ->defaultValue($providers['font_awesome']['paths'])
353 14
                            ->prototype('scalar')->end()
354 14
                        ->end()
355 14
                    ->end()
356 14
                ->end()
357 14
            ->end();
358
    }
359
360 14
    private function addWebpackEncoreSection(ArrayNodeDefinition $root): void
361
    {
362 14
        $defaultPaths = [
363 14
            '%kernel.project_dir%/public/build',
364 14
        ];
365
366 14
        $root
367 14
            ->children()
368 14
                ->arrayNode('output_paths')
369 14
                    ->info('Paths where Symfony Encore will generate its output.')
370 14
                    // ->example(sprintf('["%s"]', implode('", "', $defaultPaths)))
371 14
                    ->defaultValue($defaultPaths)
372 14
                    ->prototype('scalar')->end()
373 14
            ->end();
374
    }
375
}
376