Test Failed
Push — master ( e6f4b5...c3cccf )
by
unknown
15:56
created

XmlDecoder::arrayAddChilds()   C

Complexity

Conditions 12
Paths 12

Size

Total Lines 54
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 20.3527

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 12
eloc 33
c 1
b 0
f 0
nc 12
nop 4
dl 0
loc 54
ccs 19
cts 31
cp 0.6129
crap 20.3527
rs 6.9666

How to fix   Long Method    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
declare(strict_types=1);
4
5
/**
6
 * LibreDTE: Biblioteca PHP (Núcleo).
7
 * Copyright (C) LibreDTE <https://www.libredte.cl>
8
 *
9
 * Este programa es software libre: usted puede redistribuirlo y/o modificarlo
10
 * bajo los términos de la Licencia Pública General Affero de GNU publicada por
11
 * la Fundación para el Software Libre, ya sea la versión 3 de la Licencia, o
12
 * (a su elección) cualquier versión posterior de la misma.
13
 *
14
 * Este programa se distribuye con la esperanza de que sea útil, pero SIN
15
 * GARANTÍA ALGUNA; ni siquiera la garantía implícita MERCANTIL o de APTITUD
16
 * PARA UN PROPÓSITO DETERMINADO. Consulte los detalles de la Licencia Pública
17
 * General Affero de GNU para obtener una información más detallada.
18
 *
19
 * Debería haber recibido una copia de la Licencia Pública General Affero de
20
 * GNU junto a este programa.
21
 *
22
 * En caso contrario, consulte <http://www.gnu.org/licenses/agpl.html>.
23
 */
24
25
namespace libredte\lib\Core\Xml;
26
27
use DOMElement;
28
use DOMNodeList;
29
use DOMText;
30
31
/**
32
 * Clase `XmlDecoder` crea un arreglo PHP a partir de un documento XML.
33
 */
34
class XmlDecoder
35
{
36
    /**
37
     * Convierte un documento XML a un arreglo PHP.
38
     *
39
     * @param XmlDocument|DOMElement $documentElement Documento XML que se
40
     * desea convertir a un arreglo de PHP o el elemento donde vamos a hacer la
41
     * conversión si no es el documento XML completo.
42
     * @param array|null $data Arreglo donde se almacenarán los resultados.
43
     * @param bool $twinsAsArray Indica si se deben tratar los nodos gemelos
44
     * como un arreglo.
45
     * @return array Arreglo con la representación del XML.
46
     */
47 17
    public static function decode(
48
        XmlDocument|DOMElement $documentElement,
49
        ?array &$data = null,
50
        bool $twinsAsArray = false
51
    ): array {
52
        // Si no viene un tagElement se busca uno, si no se obtiene se termina
53
        // la generación.
54 17
        $tagElement = $documentElement instanceof DOMElement
55 17
            ? $documentElement
56 17
            : $documentElement->documentElement
57 17
        ;
58 17
        if ($tagElement === null) {
59
            return [];
60
        }
61
62
        // Índice en el arreglo que representa al tag. Además es un nombre de
63
        // variable más corto :)
64 17
        $key = $tagElement->tagName;
65
66
        // Si no hay un arreglo de destino para los datos se crea un arreglo
67
        // con el índice del nodo principal con valor vacío.
68 17
        if ($data === null) {
69
            //$data = [$key => self::getEmptyValue()];
70 17
            $data = [$key => null];
71
        }
72
73
        // Si el tagElement tiene atributos se agregan al arreglo dentro del
74
        // índice especial '@attributes'.
75 17
        if ($tagElement->hasAttributes()) {
76 16
            $data[$key]['@attributes'] = [];
77 16
            foreach ($tagElement->attributes as $attribute) {
78 16
                $data[$key]['@attributes'][$attribute->name] = $attribute->value;
79
            }
80
        }
81
82
        // Si el tagElement tiene nodos hijos se agregan al valor del tag.
83 17
        if ($tagElement->hasChildNodes()) {
84 17
            self::arrayAddChilds(
85 17
                $data,
86 17
                $tagElement,
87 17
                $tagElement->childNodes,
88 17
                $twinsAsArray
89 17
            );
90
        }
91
92
        // Entregar los datos del documento XML como un arreglo.
93 17
        return $data;
94
    }
95
96
    /**
97
     * Agrega nodos hijos de un documento XML a un arreglo PHP.
98
     *
99
     * @param array &$data Arreglo donde se agregarán los nodos hijos.
100
     * @param DOMElement $tagElement Nodo padre del que se extraerán los nodos
101
     * hijos.
102
     * @param DOMNodeList $childs Lista de nodos hijos del nodo padre.
103
     * @param bool $twinsAsArray Indica si se deben tratar los nodos gemelos
104
     * como un arreglo.
105
     * @return void
106
     */
107 17
    private static function arrayAddChilds(
108
        array &$data,
109
        DOMElement $tagElement,
110
        DOMNodeList $childs,
111
        bool $twinsAsArray,
112
    ): void {
113 17
        $key = $tagElement->tagName;
114
        // Se recorre cada uno de los nodos hijos.
115 17
        foreach ($childs as $child) {
116 17
            if ($child instanceof DOMText) {
117 17
                $textContent = trim($child->textContent);
118 17
                if ($textContent !== '') {
119 17
                    if ($tagElement->hasAttributes()) {
120 11
                        $data[$key]['@value'] = $textContent;
121 17
                    } elseif ($childs->length === 1 && empty($data[$key])) {
122 17
                        $data[$key] = $textContent;
123
                    } else {
124 17
                        $array[$key]['@value'] = $textContent;
125
                    }
126
                }
127 17
            } elseif ($child instanceof DOMElement) {
128 17
                $n_twinsNodes = self::nodeCountTwins(
129 17
                    $tagElement,
130 17
                    $child->tagName
131 17
                );
132 17
                if ($n_twinsNodes === 1) {
133 17
                    if ($twinsAsArray) {
134
                        self::decode($child, $data);
135
                    } else {
136 17
                        self::decode($child, $data[$key]);
137
                    }
138
                } else {
139
                    // Se crea una lista para el nodo hijo, pues tiene varios
140
                    // nodos iguales el XML.
141
                    if (!isset($data[$key][$child->tagName])) {
142
                        $data[$key][$child->tagName] = [];
143
                    }
144
145
                    // Se revisa si el nodo hijo es escalar. Si lo es, se añade
146
                    // a la lista directamente.
147
                    $textContent = trim($child->textContent);
148
                    if ($textContent !== '') {
149
                        $data[$key][$child->tagName][] = $textContent;
150
                    }
151
                    // Si el nodo hijo es un escalar, sino que es una lista de
152
                    // nodos, se construye como si fuese un arreglo normal con
153
                    // la llamada a decode().
154
                    else {
155
                        $siguiente = count($data[$key][$child->tagName]);
156
                        $data[$key][$child->tagName][$siguiente] = [];
157
                        self::decode(
158
                            $child,
159
                            $data[$key][$child->tagName][$siguiente],
160
                            true
161
                        );
162
                    }
163
                }
164
            }
165
        }
166
    }
167
168
    /**
169
     * Cuenta los nodos con el mismo nombre hijos de un DOMElement.
170
     *
171
     * @param DOMElement $dom Elemento DOM donde se buscarán los nodos.
172
     * @param string $tagName Nombre del tag a contar.
173
     * @return int Cantidad de nodos hijos con el mismo nombre.
174
     */
175 17
    private static function nodeCountTwins(DOMElement $dom, string $tagName): int
176
    {
177 17
        $twins = 0;
178 17
        foreach ($dom->childNodes as $child) {
179 17
            if ($child instanceof DOMElement && $child->tagName === $tagName) {
180 17
                $twins++;
181
            }
182
        }
183 17
        return $twins;
184
    }
185
}
186