Passed
Push — master ( a08a48...8426a1 )
by Esteban De La Fuente
05:58
created

XmlDecoder::arrayAddChilds()   C

Complexity

Conditions 12
Paths 12

Size

Total Lines 54
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 13.6578

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 24
cts 31
cp 0.7742
crap 13.6578
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 89
    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 89
        $tagElement = $documentElement instanceof DOMElement
55 86
            ? $documentElement
56 89
            : $documentElement->documentElement
57 89
        ;
58 89
        if ($tagElement === null) {
59 1
            return [];
60
        }
61
62
        // Índice en el arreglo que representa al tag. Además es un nombre de
63
        // variable más corto :)
64 88
        $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 88
        if ($data === null) {
69
            //$data = [$key => self::getEmptyValue()];
70 88
            $data = [$key => null];
71
        }
72
73
        // Si el tagElement tiene atributos se agregan al arreglo dentro del
74
        // índice especial '@attributes'.
75 88
        if ($tagElement->hasAttributes()) {
76 72
            $data[$key]['@attributes'] = [];
77 72
            foreach ($tagElement->attributes as $attribute) {
78 72
                $data[$key]['@attributes'][$attribute->name] = $attribute->value;
79
            }
80
        }
81
82
        // Si el tagElement tiene nodos hijos se agregan al valor del tag.
83 88
        if ($tagElement->hasChildNodes()) {
84 88
            self::arrayAddChilds(
85 88
                $data,
86 88
                $tagElement,
87 88
                $tagElement->childNodes,
88 88
                $twinsAsArray
89 88
            );
90
        }
91
92
        // Entregar los datos del documento XML como un arreglo.
93 88
        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 88
    private static function arrayAddChilds(
108
        array &$data,
109
        DOMElement $tagElement,
110
        DOMNodeList $childs,
111
        bool $twinsAsArray,
112
    ): void {
113 88
        $key = $tagElement->tagName;
114
        // Se recorre cada uno de los nodos hijos.
115 88
        foreach ($childs as $child) {
116 88
            if ($child instanceof DOMText) {
117 84
                $textContent = trim($child->textContent);
118 84
                if ($textContent !== '') {
119 84
                    if ($tagElement->hasAttributes()) {
120 66
                        $data[$key]['@value'] = $textContent;
121 82
                    } elseif ($childs->length === 1 && empty($data[$key])) {
122 82
                        $data[$key] = $textContent;
123
                    } else {
124 84
                        $array[$key]['@value'] = $textContent;
125
                    }
126
                }
127 88
            } elseif ($child instanceof DOMElement) {
128 88
                $n_twinsNodes = self::nodeCountTwins(
129 88
                    $tagElement,
130 88
                    $child->tagName
131 88
                );
132 88
                if ($n_twinsNodes === 1) {
133 86
                    if ($twinsAsArray) {
134
                        self::decode($child, $data);
135
                    } else {
136 86
                        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 4
                    if (!isset($data[$key][$child->tagName])) {
142 4
                        $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 4
                    $textContent = trim($child->textContent);
148 4
                    if ($textContent !== '') {
149 4
                        $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 88
    private static function nodeCountTwins(DOMElement $dom, string $tagName): int
176
    {
177 88
        $twins = 0;
178 88
        foreach ($dom->childNodes as $child) {
179 88
            if ($child instanceof DOMElement && $child->tagName === $tagName) {
180 88
                $twins++;
181
            }
182
        }
183 88
        return $twins;
184
    }
185
}
186