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

Ident   A

Complexity

Total Complexity 4

Size/Duplication

Total Lines 80
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 4
eloc 45
c 1
b 0
f 0
dl 0
loc 80
ccs 44
cts 44
cp 1
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
A configureOptions() 0 32 2
A generate() 0 36 2
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;
13
14
use function BenTools\IterableFunctions\iterable_to_array;
15
use function Ocubom\Math\base_convert;
16
17
use Ocubom\Twig\Extension\Svg\Util\DomHelper;
18
use Symfony\Component\OptionsResolver\OptionsResolver;
19
20
class Ident
21
{
22
    private static ?OptionsResolver $resolver = null;
23
24
    private const REPLACEMENTS = [
25
        '> <' => '><',
26
        '" >' => '">',
27
        '\' >' => '\'>',
28
    ];
29
30 4
    public static function generate(
31
        \DOMElement $element,
32
        iterable $options = null
33
    ): string {
34
        // Initialize static variables
35
        /* @psalm-suppress RedundantPropertyInitializationCheck */
36 4
        self::$resolver = self::$resolver ?? self::configureOptions();
37
38
        // Resolve options
39 4
        $options = self::$resolver->resolve(iterable_to_array($options ?? []));
40
41
        // Work on an optimized clone
42 4
        $node = DomHelper::cloneElement($element);
43
        // Remove identifier
44 4
        $node->removeAttribute('id');
45
46
        // Convert to text
47 4
        $text = DomHelper::toXml($node);
48 4
        $text = preg_replace('/\s\s+/', ' ', $text);
49 4
        $text = trim($text);
50 4
        foreach (self::REPLACEMENTS as $search => $replace) {
51 4
            $text = str_replace($search, $replace, $text);
52
        }
53
54
        // Generate a hash
55 4
        $hash = hash($options['algo'], $text);
56
57
        // Convert to base and pad to maximum length in new base
58 4
        $hash = str_pad(
59 4
            base_convert($hash, 16, $options['base']),
60 4
            (int) ceil(strlen($hash) * log(16, $options['base'])),
61 4
            '0',
62 4
            \STR_PAD_LEFT
63 4
        );
64
65 4
        return sprintf($options['format'], substr($hash, -$options['length']));
66
    }
67
68 4
    protected static function configureOptions(OptionsResolver $resolver = null): OptionsResolver
69
    {
70 1
        $resolver = $resolver ?? new OptionsResolver();
71
72 1
        $resolver->define('algo')
73 1
            ->default('sha512')
74 1
            ->allowedTypes('string')
75 1
            ->allowedValues(...hash_algos())
76 1
            ->info('Hash algorithm used to hash values');
77
78 1
        $resolver->define('format')
79 1
            ->default('%s')
80 1
            ->allowedTypes('string')
81 1
            ->allowedValues(function (string $value) {
82 4
                return str_contains($value, '%');
83 1
            })
84 1
            ->info('Native printf format string to generate final output. Must include %s');
85
86 1
        $resolver->define('base')
87 1
            ->default(62)
88 1
            ->allowedTypes('int')
89 1
            ->allowedValues(function (int $value) {
90 4
                return $value >= 2 && $value <= 62;
91 1
            })
92 1
            ->info('The base used to encode value to reduce its length or increase entropy');
93
94 1
        $resolver->define('length')
95 1
            ->default(7)
96 1
            ->allowedTypes('int')
97 1
            ->info('Length of the generated identifier (after base conversion)');
98
99 1
        return $resolver;
100
    }
101
}
102