Passed
Push — main ( 77e259...371264 )
by Oscar
08:09 queued 06:34
created

SvgRuntime::convertToSymbols()   B

Complexity

Conditions 9
Paths 21

Size

Total Lines 80
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 36
CRAP Score 9

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 38
c 1
b 0
f 0
dl 0
loc 80
ccs 36
cts 36
cp 1
rs 7.7564
cc 9
nc 21
nop 2
crap 9

How to fix   Long Method   

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;
13
14
use Masterminds\HTML5;
15
use Ocubom\Twig\Extension\Svg\Exception\RuntimeException;
16
use Ocubom\Twig\Extension\Svg\FinderInterface;
17
use Ocubom\Twig\Extension\Svg\Svg;
18
use Ocubom\Twig\Extension\Svg\Symbol;
19
use Ocubom\Twig\Extension\Svg\Util\DomHelper;
20
use Psr\Log\LoggerInterface;
21
use Psr\Log\NullLogger;
22
use Twig\Environment;
23
use Twig\Extension\RuntimeExtensionInterface;
24
25
class SvgRuntime implements RuntimeExtensionInterface
26
{
27
    private FinderInterface $finder;
28
    private LoggerInterface $logger;
29
30 9
    public function __construct(FinderInterface $finder, LoggerInterface $logger = null)
31
    {
32 9
        $this->finder = $finder;
33 9
        $this->logger = $logger ?? new NullLogger();
34
    }
35
36
    /**
37
     * Convert all SVG into Symbol references and inline symbols.
38
     */
39 5
    public function convertToSymbols(Environment $twig, string $html): string
40
    {
41 5
        $parser = new HTML5();
42
43
        // Load Document
44 5
        $doc = $parser->loadHTML($html);
45 5
        if ($parser->hasErrors()) {
46 1
            throw new RuntimeException(sprintf(
47 1
                'Unable to parse HTML: %s',
48 1
                implode("\n", $parser->getErrors())
49 1
            ));
50
        }
51
52
        /** @var \DOMElement[] $symbols */
53 4
        $symbols = [];
54
55
        /** @var \DOMElement $svg */
56 4
        foreach ($doc->getElementsByTagName('svg') as $svg) {
57 3
            $symbol = new Symbol($svg, [
58 3
                'debug' => $twig->isDebug(),
59 3
            ]);
60
61
            // Replace all SVG with use
62 3
            DomHelper::replaceNode($svg, $symbol->getReference());
63
64
            // Index symbol by id
65 3
            $symbols[$symbol->getId()] = $symbol->getElement();
66
        }
67
68
        // Dump all symbols
69 4
        if (count($symbols) > 0) {
70
            // Create symbols container element before the end of body tag or DOM
71 3
            $node = DomHelper::createElement('svg', '', $doc
72 3
                ->getElementsByTagName('body')
73 3
                ->item(0) ?? $doc
74 3
            );
75 3
            $node->setAttribute('style', 'display:none');
76
77
            // Add format on debug mode
78 3
            if ($twig->isDebug()) {
79
                assert($node->previousSibling instanceof \DOMNode);
80 3
                DomHelper::appendChildNode($node->previousSibling, $node);
81
            }
82
83 3
            uksort($symbols, 'strnatcasecmp');
84 3
            foreach ($symbols as $symbol) {
85 3
                DomHelper::appendChildNode($symbol, $node);
86
87 3
                if ($twig->isDebug()) {
88
                    assert($node->previousSibling instanceof \DOMNode);
89 3
                    DomHelper::appendChildNode($node->previousSibling, $node);
90
                }
91
            }
92
93 3
            if ($twig->isDebug()) {
94
                assert($node->parentNode instanceof \DOMNode);
95
96 3
                DomHelper::appendChildNode(
97 3
                    $doc->createTextNode("\n"),
98 3
                    $node->parentNode
99 3
                );
100
            }
101
        }
102
103
        // Normalize final doc
104 4
        $doc->normalize();
105
106
        // Fix EOL lines problem on Windows
107 4
        if ('Windows' === \PHP_OS_FAMILY) {
108
            // @codeCoverageIgnoreStart
109
            return str_replace(
110
                \PHP_EOL,
111
                "\n",
112
                $parser->saveHTML($doc)
113
            );
114
            // @codeCoverageIgnoreEnd
115
        }
116
117
        // Generate output
118 4
        return $parser->saveHTML($doc);
119
    }
120
121
    /**
122
     * Render an inlined SVG image.
123
     */
124 4
    public function renderSvg(string $ident, array $options = []): string
125
    {
126 4
        $this->logger->debug('Resolving "{ident}"', [
127 4
            'ident' => $ident,
128 4
            'options' => $options,
129 4
        ]);
130
131 4
        $svg = Svg::createFromFile($this->finder->resolve($ident), $options);
132
133 2
        $this->logger->debug('Render "{ident}" as inlined SVG', [
134 2
            'ident' => $ident,
135 2
        ]);
136
137 2
        return (string) $svg;
138
    }
139
}
140