DecoderWorker::decode()   B
last analyzed

Complexity

Conditions 7
Paths 18

Size

Total Lines 47
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 19
c 0
b 0
f 0
nc 18
nop 3
dl 0
loc 47
ccs 22
cts 22
cp 1
crap 7
rs 8.8333
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Derafu: Biblioteca PHP (Núcleo).
7
 * Copyright (C) Derafu <https://www.derafu.org>
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 GNU
20
 * junto a este programa.
21
 *
22
 * En caso contrario, consulte <http://www.gnu.org/licenses/agpl.html>.
23
 */
24
25
namespace Derafu\Lib\Core\Package\Prime\Component\Xml\Worker;
26
27
use Derafu\Lib\Core\Foundation\Abstract\AbstractWorker;
28
use Derafu\Lib\Core\Package\Prime\Component\Xml\Contract\DecoderWorkerInterface;
29
use Derafu\Lib\Core\Package\Prime\Component\Xml\Contract\XmlInterface;
30
use DOMElement;
31
use DOMNodeList;
32
use DOMText;
33
34
/**
35
 * Clase que crea un arreglo PHP a partir de un documento XML.
36
 */
37
class DecoderWorker extends AbstractWorker implements DecoderWorkerInterface
38
{
39
    /**
40
     * {@inheritDoc}
41
     */
42 21
    public function decode(
43
        XmlInterface|DOMElement $documentElement,
44
        ?array &$data = null,
45
        bool $twinsAsArray = false
46
    ): array {
47
        // Si no viene un tagElement se busca uno, si no se obtiene se termina
48
        // la generación.
49 21
        $tagElement = $documentElement instanceof DOMElement
50 18
            ? $documentElement
51 21
            : $documentElement->getDocumentElement()
52 21
        ;
53 21
        if ($tagElement === null) {
54 1
            return [];
55
        }
56
57
        // Índice en el arreglo que representa al tag. Además es un nombre de
58
        // variable más corto :)
59 20
        $key = $tagElement->tagName;
60
61
        // Si no hay un arreglo de destino para los datos se crea un arreglo
62
        // con el índice del nodo principal con valor vacío.
63 20
        if ($data === null) {
64
            //$data = [$key => self::getEmptyValue()];
65 20
            $data = [$key => null];
66
        }
67
68
        // Si el tagElement tiene atributos se agregan al arreglo dentro del
69
        // índice especial '@attributes'.
70 20
        if ($tagElement->hasAttributes()) {
71 7
            $data[$key]['@attributes'] = [];
72 7
            foreach ($tagElement->attributes as $attribute) {
73 7
                $data[$key]['@attributes'][$attribute->name] = $attribute->value;
74
            }
75
        }
76
77
        // Si el tagElement tiene nodos hijos se agregan al valor del tag.
78 20
        if ($tagElement->hasChildNodes()) {
79 20
            self::arrayAddChilds(
0 ignored issues
show
Bug Best Practice introduced by
The method Derafu\Lib\Core\Package\...orker::arrayAddChilds() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

79
            self::/** @scrutinizer ignore-call */ 
80
                  arrayAddChilds(
Loading history...
80 20
                $data,
81 20
                $tagElement,
82 20
                $tagElement->childNodes,
83 20
                $twinsAsArray
84 20
            );
85
        }
86
87
        // Entregar los datos del documento XML como un arreglo.
88 20
        return $data;
89
    }
90
91
    /**
92
     * Agrega nodos hijos de un documento XML a un arreglo PHP.
93
     *
94
     * @param array &$data Arreglo donde se agregarán los nodos hijos.
95
     * @param DOMElement $tagElement Nodo padre del que se extraerán los nodos
96
     * hijos.
97
     * @param DOMNodeList $childs Lista de nodos hijos del nodo padre.
98
     * @param bool $twinsAsArray Indica si se deben tratar los nodos gemelos
99
     * como un arreglo.
100
     * @return void
101
     */
102 20
    private function arrayAddChilds(
103
        array &$data,
104
        DOMElement $tagElement,
105
        DOMNodeList $childs,
106
        bool $twinsAsArray,
107
    ): void {
108 20
        $key = $tagElement->tagName;
109
        // Se recorre cada uno de los nodos hijos.
110 20
        foreach ($childs as $child) {
111 20
            if ($child instanceof DOMText) {
112 16
                $textContent = trim($child->textContent);
113 16
                if ($textContent !== '') {
114 16
                    if ($tagElement->hasAttributes()) {
115 2
                        $data[$key]['@value'] = $textContent;
116 14
                    } elseif ($childs->length === 1 && empty($data[$key])) {
117 14
                        $data[$key] = $textContent;
118
                    } else {
119
                        $array[$key]['@value'] = $textContent;
120
                    }
121
                }
122 20
            } elseif ($child instanceof DOMElement) {
123 20
                $n_twinsNodes = self::nodeCountTwins(
0 ignored issues
show
Bug Best Practice introduced by
The method Derafu\Lib\Core\Package\...orker::nodeCountTwins() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

123
                /** @scrutinizer ignore-call */ 
124
                $n_twinsNodes = self::nodeCountTwins(
Loading history...
124 20
                    $tagElement,
125 20
                    $child->tagName
126 20
                );
127 20
                if ($n_twinsNodes === 1) {
128 18
                    if ($twinsAsArray) {
129
                        self::decode($child, $data);
0 ignored issues
show
Bug Best Practice introduced by
The method Derafu\Lib\Core\Package\...DecoderWorker::decode() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

129
                        self::/** @scrutinizer ignore-call */ 
130
                              decode($child, $data);
Loading history...
130
                    } else {
131 18
                        self::decode($child, $data[$key]);
132
                    }
133
                } else {
134
                    // Se crea una lista para el nodo hijo, pues tiene varios
135
                    // nodos iguales el XML.
136 2
                    if (!isset($data[$key][$child->tagName])) {
137 2
                        $data[$key][$child->tagName] = [];
138
                    }
139
140
                    // Se revisa si el nodo hijo es escalar. Si lo es, se añade
141
                    // a la lista directamente.
142 2
                    $textContent = trim($child->textContent);
143 2
                    if ($textContent !== '') {
144 2
                        $data[$key][$child->tagName][] = $textContent;
145
                    }
146
                    // Si el nodo hijo es un escalar, sino que es una lista de
147
                    // nodos, se construye como si fuese un arreglo normal con
148
                    // la llamada a decode().
149
                    else {
150
                        $siguiente = count($data[$key][$child->tagName]);
151
                        $data[$key][$child->tagName][$siguiente] = [];
152
                        self::decode(
153
                            $child,
154
                            $data[$key][$child->tagName][$siguiente],
155
                            true
156
                        );
157
                    }
158
                }
159
            }
160
        }
161
    }
162
163
    /**
164
     * Cuenta los nodos con el mismo nombre hijos de un DOMElement.
165
     *
166
     * @param DOMElement $dom Elemento DOM donde se buscarán los nodos.
167
     * @param string $tagName Nombre del tag a contar.
168
     * @return int Cantidad de nodos hijos con el mismo nombre.
169
     */
170 20
    private function nodeCountTwins(DOMElement $dom, string $tagName): int
171
    {
172 20
        $twins = 0;
173 20
        foreach ($dom->childNodes as $child) {
174 20
            if ($child instanceof DOMElement && $child->tagName === $tagName) {
175 20
                $twins++;
176
            }
177
        }
178
179 20
        return $twins;
180
    }
181
}
182