Passed
Push — main ( d61a4b...a3ea10 )
by Oscar
12:17
created

IconifyRuntime::queryIconify()   B

Complexity

Conditions 10
Paths 10

Size

Total Lines 36
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 10

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 18
dl 0
loc 36
ccs 23
cts 23
cp 1
rs 7.6666
c 1
b 0
f 0
cc 10
nc 10
nop 2
crap 10

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of ocubom/twig-svg-extension
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\Twig\Extension\Svg\Provider\Iconify;
13
14
use Ocubom\Twig\Extension\Svg\Loader\LoaderInterface;
15
use Ocubom\Twig\Extension\Svg\Util\DomUtil;
16
use Ocubom\Twig\Extension\Svg\Util\Html5Util;
17
use Symfony\Component\OptionsResolver\Options;
18
use Symfony\Component\OptionsResolver\OptionsResolver;
19
use Twig\Environment;
20
use Twig\Extension\RuntimeExtensionInterface;
21
22
use function BenTools\IterableFunctions\iterable_merge;
23
use function BenTools\IterableFunctions\iterable_to_array;
24
use function Ocubom\Twig\Extension\is_string;
25
26
class IconifyRuntime implements RuntimeExtensionInterface
27
{
28
    private LoaderInterface $loader;
29
30
    private array $options;
31
32 6
    public function __construct(IconifyLoader $loader, iterable $options = null)
33
    {
34 6
        $this->loader = $loader;
35 6
        $this->options = static::configureOptions()
36 6
            ->resolve(iterable_to_array($options ?? /* @scrutinizer ignore-type */ []));
37
    }
38
39 6
    public function replaceIcons(
40
        Environment $twig,
41
        string $html,
42
        array $options = []
43
    ): string {
44
        // Load HTML
45 6
        $doc = Html5Util::loadHtml($html);
46
47
        /** @var \DOMNode $node */
48 6
        foreach (iterable_to_array($this->queryIconify($doc, $options)) as $node) {
49 6
            if ($node instanceof \DOMElement) {
50 6
                if ($twig->isDebug()) {
51 3
                    DomUtil::createComment(DomUtil::toHtml($node), $node, true);
52
                }
53
54 6
                $ident = $node->hasAttribute('data-icon')
55 5
                    ? $node->getAttribute('data-icon')
56 2
                    : $node->getAttribute('icon');
57 6
                $icon = $this->loader->resolve(
58 6
                    $ident, // Resolve icon
59 6
                    DomUtil::getElementAttributes($node) // … and clone all its attributes as options
60 6
                );
61
62
                // Replace node
63 3
                DomUtil::replaceNode($node, $icon->getElement());
64
            }
65
        }
66
67
        // Generate normalized HTML
68 3
        return Html5Util::toHtml($doc);
69
    }
70
71
    /**
72
     * @return iterable<\DOMElement>
73
     */
74 6
    private function queryIconify(\DOMDocument $doc, iterable $options = null)
75
    {
76 6
        $options = static::configureOptions()->resolve(iterable_to_array(iterable_merge(
77 6
            $this->options,
78 6
            $options ?? /* @scrutinizer ignore-type */ []
79 6
        )));
80
81
        // SVG Framework
82
        // <span class="iconify" data-icon="mdi:home"></span>
83
        // <span class="iconify-inline" data-icon="mdi:home"></span>
84 6
        if ($options['svg_framework']) {
85 5
            $query = implode(' | ', array_map(
86 5
                function (string $class): string {
87 5
                    return sprintf(
88 5
                        'descendant-or-self::*[@class and contains(concat(\' \', normalize-space(@class), \' \'), \' %s \')]',
89 5
                        $class
90 5
                    );
91 5
                },
92 5
                $options['svg_framework']
93 5
            ));
94
95 5
            foreach (DomUtil::query($query, $doc) as $node) {
96 5
                if ($node instanceof \DOMElement && $node->hasAttribute('data-icon')) {
97 5
                    yield $node;
98
                }
99
            }
100
        }
101
102
        // Web Component
103
        // <icon icon="mdi:home" />
104
        // <iconify-icon icon="mdi:home"></iconify-icon>
105 6
        if ($options['web_component']) {
106 5
            foreach ($options['web_component'] as $tag) {
107 5
                foreach ($doc->getElementsByTagName($tag) as $node) {
108 2
                    if ($node instanceof \DOMElement && $node->hasAttribute('icon')) {
109 2
                        yield $node;
110
                    }
111
                }
112
            }
113
        }
114
    }
115
116
    /** @psalm-suppress MissingClosureParamType */
117 6
    protected static function configureOptions(OptionsResolver $resolver = null): OptionsResolver
118
    {
119 6
        $resolver = $resolver ?? new OptionsResolver();
120
121 6
        $normalizeStringArray = function (Options $options, $value): array {
122 6
            return array_filter(
123 6
                is_string($value) ? preg_split('@\s+@Uis', $value) : ($value ?? []),
124 6
                function (string $item): bool {
125 6
                    return !empty($item);
126 6
                }
127 6
            );
128 6
        };
129
130 6
        $resolver->define('svg_framework')
131 6
            ->default(['iconify', 'iconify-inline'])
132 6
            ->allowedTypes('null', 'string', 'string[]')
133 6
            ->normalize($normalizeStringArray)
134 6
            ->info('SVG Framework classes');
135
136 6
        $resolver->define('web_component')
137 6
            ->default(['icon', 'iconify-icon'])
138 6
            ->allowedTypes('null', 'string', 'string[]')
139 6
            ->normalize($normalizeStringArray)
140 6
            ->info('Web Component tags');
141
142 6
        return $resolver;
143
    }
144
}
145