Passed
Push — master ( dcddfc...dfc3a0 )
by Tomáš
11:42
created

Formatter   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 155
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 32
eloc 57
c 1
b 0
f 0
dl 0
loc 155
ccs 62
cts 62
cp 1
rs 9.84

5 Methods

Rating   Name   Duplication   Size   Complexity  
A encodeValue() 0 15 6
A decodeValue() 0 15 4
B simplifyArray() 0 22 7
C nodeToArray() 0 47 12
A namespacesToAttributes() 0 13 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Inspirum\XML\Formatter;
6
7
use DOMNode;
8
use Stringable;
9
use function array_keys;
10
use function count;
11
use function in_array;
12
use function is_bool;
13
use function is_numeric;
14
use function is_scalar;
15
use function rtrim;
16
use function sprintf;
17
use function str_starts_with;
18
use function trim;
19
use const XML_CDATA_SECTION_NODE;
20
use const XML_TEXT_NODE;
21
22
final class Formatter
23
{
24
    /**
25
     * Prefix namespaces with xmlns
26
     *
27
     * @param array<string,string> $namespaces
28
     *
29
     * @return array<string,string>
30
     */
31 2
    public static function namespacesToAttributes(array $namespaces): array
32
    {
33 2
        $attributes = [];
34
35 2
        foreach ($namespaces as $namespaceLocalName => $namespaceValue) {
36 2
            if (str_starts_with($namespaceLocalName, 'xmlns') === false) {
37 2
                $namespaceLocalName = rtrim(sprintf('xmlns:%s', $namespaceLocalName), ':');
38
            }
39
40 2
            $attributes[$namespaceLocalName] = $namespaceValue;
41
        }
42
43 2
        return $attributes;
44
    }
45
46
    /**
47
     * Normalize value.
48
     */
49 48
    public static function encodeValue(mixed $value): ?string
50
    {
51 48
        if ($value === null) {
52 48
            return null;
53
        }
54
55 44
        if (is_bool($value)) {
56 10
            $value = $value ? 'true' : 'false';
57
        }
58
59 44
        if (is_scalar($value) || $value instanceof Stringable) {
60 44
            return (string) $value;
61
        }
62
63 1
        return null;
64
    }
65
66
    /**
67
     * Normalize value.
68
     *
69
     * @param mixed $value
70
     *
71
     * @return mixed
72
     */
73 2
    public static function decodeValue(mixed $value): mixed
74
    {
75 2
        if (is_numeric($value)) {
76 2
            return $value + 0;
77
        }
78
79 2
        if (in_array($value, ['true', 'True'], true)) {
80 2
            return true;
81
        }
82
83 2
        if (in_array($value, ['false', 'False'], true)) {
84 1
            return false;
85
        }
86
87 2
        return $value;
88
    }
89
90
    /**
91
     * Convert DOM node to array
92
     *
93
     * @param \DOMNode                       $node
94
     * @param \Inspirum\XML\Formatter\Config $config
95
     *
96
     * @return mixed
97
     */
98 13
    public static function nodeToArray(DOMNode $node, Config $config): mixed
99
    {
100 13
        $value = null;
101
        /** @var array<string,mixed> $attributes */
102 13
        $attributes = [];
103
        /** @var array<string,array<mixed>> $nodes */
104 13
        $nodes = [];
105
106 13
        if ($node->hasAttributes()) {
107
            /** @var \DOMAttr $attribute */
108 8
            foreach ($node->attributes ?? [] as $attribute) {
109 8
                $attributes[$attribute->nodeName] = $config->autoCast
110 1
                    ? self::decodeValue($attribute->nodeValue)
111 7
                    : $attribute->nodeValue;
112
            }
113
        }
114
115 13
        if ($node->hasChildNodes()) {
116
            /** @var \DOMNode $child */
117 11
            foreach ($node->childNodes ?? [] as $child) {
118 11
                if (in_array($child->nodeType, [XML_TEXT_NODE, XML_CDATA_SECTION_NODE])) {
119 11
                    if (trim((string) $child->nodeValue) !== '') {
120 11
                        $value = $config->autoCast
121 1
                            ? self::decodeValue($child->nodeValue)
122 10
                            : $child->nodeValue;
123
                    }
124
125 11
                    continue;
126
                }
127
128 11
                $nodes[$child->nodeName][] = self::nodeToArray($child, $config);
129
            }
130
        }
131
132 13
        if ($config->fullResponse) {
133 1
            return [
134 1
                $config->attributesName => $attributes,
135 1
                $config->valueName      => $value,
136 1
                $config->nodesName      => $nodes,
137 1
            ];
138
        }
139
140 12
        if (count($nodes) === 0 && count($attributes) === 0) {
141 10
            return $value;
142
        }
143
144 11
        return self::simplifyArray($attributes, $value, $nodes, $config, $node);
145
    }
146
147
    /**
148
     * Remove unnecessary data
149
     *
150
     * @param array<string,mixed>        $attributes
151
     * @param array<string,array<mixed>> $nodes
152
     *
153
     * @return array<int|string,mixed>
154
     */
155 11
    private static function simplifyArray(array $attributes, mixed $value, array $nodes, Config $config, DOMNode $node): array
156
    {
157 11
        $simpleResult = $nodes;
158 11
        foreach ($nodes as $nodeName => $values) {
159
            if (
160 10
                in_array($nodeName, $config->alwaysArray, true) === false
161 10
                && in_array($node->nodeName . '.' . $nodeName, $config->alwaysArray, true) === false
162 10
                && array_keys($values) === [0]
163
            ) {
164 9
                $simpleResult[$nodeName] = $values[0];
165
            }
166
        }
167
168 11
        if (count($attributes) > 0) {
169 7
            $simpleResult[$config->attributesName] = $attributes;
170
        }
171
172 11
        if ($value !== null) {
173 4
            $simpleResult[$config->valueName] = $value;
174
        }
175
176 11
        return $simpleResult;
177
    }
178
}
179