Passed
Push — master ( 3039c3...ae22ab )
by Esteban De La Fuente
03:20
created

XPathQuery::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 10
ccs 5
cts 6
cp 0.8333
crap 2.0185
rs 10
c 1
b 0
f 0
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\Support\Xml;
26
27
use DOMDocument;
28
use DOMNodeList;
29
use DOMXPath;
30
use InvalidArgumentException;
31
32
/**
33
 * Clase para facilitar el manejo de XML usando XPath.
34
 */
35
class XPathQuery
36
{
37
    /**
38
     * Instancia del documento XML.
39
     *
40
     * @var DOMDocument
41
     */
42
    private readonly DOMDocument $dom;
43
44
    /**
45
     * Instancia que representa el buscador con XPath.
46
     *
47
     * @var DOMXPath
48
     */
49
    private readonly DOMXPath $xpath;
50
51
    /**
52
     * Constructor que recibe el documento XML y prepara XPath.
53
     *
54
     * @param string|DOMDocument $xml Documento XML.
55
     */
56 9
    public function __construct(string|DOMDocument $xml)
57
    {
58 9
        if ($xml instanceof DOMDocument) {
59
            $this->dom = $xml;
0 ignored issues
show
Bug introduced by
The property dom is declared read-only in Derafu\Lib\Core\Support\Xml\XPathQuery.
Loading history...
60
        } else {
61 9
            $this->dom = new DOMDocument();
62 9
            $this->loadXml($xml);
63
        }
64
65 8
        $this->xpath = new DOMXPath($this->dom);
0 ignored issues
show
Bug introduced by
The property xpath is declared read-only in Derafu\Lib\Core\Support\Xml\XPathQuery.
Loading history...
66
    }
67
68
    /**
69
     * Carga un string XML en el atributo $dom.
70
     *
71
     * @param string $xml
72
     * @return static
73
     */
74 9
    private function loadXml(string $xml): static
75
    {
76 9
        $use_errors = libxml_use_internal_errors(true);
77
78 9
        $this->dom->loadXml($xml);
79
80 9
        if ($error = libxml_get_last_error()) {
81 1
            throw new InvalidArgumentException(sprintf(
82 1
                'El XML proporcionado no es válido: %s.',
83 1
                $error->message
84 1
            ));
85
        }
86
87 8
        libxml_clear_errors();
88 8
        libxml_use_internal_errors($use_errors);
89
90 8
        return $this;
91
    }
92
93
    /**
94
     * Devuelve el DOMDocument usado internamente.
95
     *
96
     * @return DOMDocument
97
     */
98 1
    public function getDomDocument(): DOMDocument
99
    {
100 1
        return $this->dom;
101
    }
102
103
    /**
104
     * Ejecuta una consulta XPath y devuelve el resultado.
105
     *
106
     * El resultado dependerá de o que se encuentre:
107
     *
108
     *   - `null`: si no hubo coincidencias.
109
     *   - string: si hubo una coincidencia.
110
     *   - string[]: si hubo más de una coincidencia.
111
     *
112
     * @param string $query Consulta XPath.
113
     * @return string|string[]|null
114
     */
115 3
    public function get(string $query): string|array|null
116
    {
117
        // Ejecutar consulta.
118 3
        $results = $this->getValues($query);
119
120
        // Si no hay resultados null.
121 3
        if (!isset($results[0])) {
122 1
            return null;
123
        }
124
125
        // Si solo hay un resultado un string.
126 2
        if (!isset($results[1])) {
127 1
            return $results[0];
128
        }
129
130
        // Más de un resultado, arreglo.
131 1
        return $results;
132
    }
133
134
    /**
135
     * Ejecuta una consulta XPath y devuelve un arreglo de valores.
136
     *
137
     * @param string $query Consulta XPath.
138
     * @return string[] Arreglo de valores encontrados.
139
     */
140 4
    public function getValues(string $query): array
141
    {
142 4
        $nodes = $this->getNodes($query);
143
144 4
        $results = [];
145 4
        foreach ($nodes as $node) {
146 3
            $results[] = $node->nodeValue;
147
        }
148
149 4
        return $results;
150
    }
151
152
    /**
153
     * Ejecuta una consulta XPath y devuelve el primer resultado como string.
154
     *
155
     * @param string $query Consulta XPath.
156
     * @return string|null El valor del nodo, o `null` si no existe.
157
     */
158 1
    public function getValue(string $query): ?string
159
    {
160 1
        $nodes = $this->getNodes($query);
161
162 1
        return $nodes->length > 0
163 1
            ? $nodes->item(0)->nodeValue
164 1
            : null
165 1
        ;
166
    }
167
168
    /**
169
     * Ejecuta una consulta XPath y devuelve los nodos resultantes.
170
     *
171
     * @param string $query Consulta XPath.
172
     * @return DOMNodeList Nodos resultantes de la consulta XPath.
173
     */
174 7
    public function getNodes(string $query): DOMNodeList
175
    {
176 7
        $use_errors = libxml_use_internal_errors(true);
177
178 7
        $nodes = $this->xpath->query($query);
179
180 7
        if ($nodes === false || $error = libxml_get_last_error()) {
0 ignored issues
show
Unused Code introduced by
The assignment to $error is dead and can be removed.
Loading history...
181 1
            throw new InvalidArgumentException(sprintf(
182 1
                'Ocurrió un error al ejecutar la expresión XPath: %s.',
183 1
                $query
184 1
            ));
185
        }
186
187 6
        libxml_clear_errors();
188 6
        libxml_use_internal_errors($use_errors);
189
190 6
        return $nodes;
191
    }
192
}
193