Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
95.92% |
47 / 49 |
|
88.89% |
8 / 9 |
CRAP | |
0.00% |
0 / 1 |
XMLReader | |
95.92% |
47 / 49 |
|
88.89% |
8 / 9 |
25 | |
0.00% |
0 / 1 |
getDomFromZip | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
4 | |||
getDomFromString | |
71.43% |
5 / 7 |
|
0.00% |
0 / 1 |
3.21 | |||
getElements | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
4 | |||
registerNamespace | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
getElement | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getAttribute | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
5 | |||
getValue | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
countElements | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
elementExists | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * This file is part of PHPWord - A pure PHP library for reading and writing |
4 | * word processing documents. |
5 | * |
6 | * PHPWord is free software distributed under the terms of the GNU Lesser |
7 | * General Public License version 3 as published by the Free Software Foundation. |
8 | * |
9 | * For the full copyright and license information, please read the LICENSE |
10 | * file that was distributed with this source code. For the full list of |
11 | * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. |
12 | * |
13 | * @see https://github.com/PHPOffice/PHPWord |
14 | * |
15 | * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 |
16 | */ |
17 | |
18 | namespace PhpOffice\PhpWord\Shared; |
19 | |
20 | use DOMDocument; |
21 | use DOMElement; |
22 | use DOMNodeList; |
23 | use DOMXpath; |
24 | use Exception; |
25 | use InvalidArgumentException; |
26 | use ZipArchive; |
27 | |
28 | /** |
29 | * XML Reader wrapper. |
30 | * |
31 | * @since 0.2.1 |
32 | */ |
33 | class XMLReader |
34 | { |
35 | /** |
36 | * DOMDocument object. |
37 | * |
38 | * @var DOMDocument |
39 | */ |
40 | private $dom; |
41 | |
42 | /** |
43 | * DOMXpath object. |
44 | * |
45 | * @var DOMXpath |
46 | */ |
47 | private $xpath; |
48 | |
49 | /** |
50 | * Get DOMDocument from ZipArchive. |
51 | * |
52 | * @param string $zipFile |
53 | * @param string $xmlFile |
54 | * |
55 | * @return DOMDocument|false |
56 | */ |
57 | public function getDomFromZip($zipFile, $xmlFile) |
58 | { |
59 | if (file_exists($zipFile) === false) { |
60 | throw new Exception('Cannot find archive file.'); |
61 | } |
62 | |
63 | $zip = new ZipArchive(); |
64 | $openStatus = $zip->open($zipFile); |
65 | if ($openStatus !== true) { |
66 | /** |
67 | * Throw an exception since making further calls on the ZipArchive would cause a fatal error. |
68 | * This prevents fatal errors on corrupt archives and attempts to open old "doc" files. |
69 | */ |
70 | throw new Exception("The archive failed to load with the following error code: $openStatus"); |
71 | } |
72 | |
73 | $content = $zip->getFromName(ltrim($xmlFile, '/')); |
74 | $zip->close(); |
75 | |
76 | if ($content === false) { |
77 | return false; |
78 | } |
79 | |
80 | return $this->getDomFromString($content); |
81 | } |
82 | |
83 | /** |
84 | * Get DOMDocument from content string. |
85 | * |
86 | * @param string $content |
87 | * |
88 | * @return DOMDocument |
89 | */ |
90 | public function getDomFromString($content) |
91 | { |
92 | if (\PHP_VERSION_ID < 80000) { |
93 | $originalLibXMLEntityValue = libxml_disable_entity_loader(true); |
94 | } |
95 | $this->dom = new DOMDocument(); |
96 | $this->dom->loadXML($content); |
97 | if (\PHP_VERSION_ID < 80000) { |
98 | libxml_disable_entity_loader($originalLibXMLEntityValue); |
99 | } |
100 | |
101 | return $this->dom; |
102 | } |
103 | |
104 | /** |
105 | * Get elements. |
106 | * |
107 | * @param string $path |
108 | * |
109 | * @return DOMNodeList<DOMElement> |
110 | */ |
111 | public function getElements($path, ?DOMElement $contextNode = null) |
112 | { |
113 | if ($this->dom === null) { |
114 | return new DOMNodeList(); // @phpstan-ignore-line |
115 | } |
116 | if ($this->xpath === null) { |
117 | $this->xpath = new DOMXpath($this->dom); |
118 | } |
119 | |
120 | $result = @$this->xpath->query($path, $contextNode); |
121 | |
122 | return empty($result) ? new DOMNodeList() : $result; // @phpstan-ignore-line |
123 | } |
124 | |
125 | /** |
126 | * Registers the namespace with the DOMXPath object. |
127 | * |
128 | * @param string $prefix The prefix |
129 | * @param string $namespaceURI The URI of the namespace |
130 | * |
131 | * @return bool true on success or false on failure |
132 | */ |
133 | public function registerNamespace($prefix, $namespaceURI) |
134 | { |
135 | if ($this->dom === null) { |
136 | throw new InvalidArgumentException('Dom needs to be loaded before registering a namespace'); |
137 | } |
138 | if ($this->xpath === null) { |
139 | $this->xpath = new DOMXpath($this->dom); |
140 | } |
141 | |
142 | return $this->xpath->registerNamespace($prefix, $namespaceURI); |
143 | } |
144 | |
145 | /** |
146 | * Get element. |
147 | * |
148 | * @param string $path |
149 | * |
150 | * @return null|DOMElement |
151 | */ |
152 | public function getElement($path, ?DOMElement $contextNode = null) |
153 | { |
154 | $elements = $this->getElements($path, $contextNode); |
155 | if ($elements->length > 0) { |
156 | return $elements->item(0); |
157 | } |
158 | |
159 | return null; |
160 | } |
161 | |
162 | /** |
163 | * Get element attribute. |
164 | * |
165 | * @param string $attribute |
166 | * @param string $path |
167 | * |
168 | * @return null|string |
169 | */ |
170 | public function getAttribute($attribute, ?DOMElement $contextNode = null, $path = null) |
171 | { |
172 | $return = null; |
173 | if ($path !== null) { |
174 | $elements = $this->getElements($path, $contextNode); |
175 | if ($elements->length > 0) { |
176 | /** @var DOMElement $node Type hint */ |
177 | $node = $elements->item(0); |
178 | $return = $node->getAttribute($attribute); |
179 | } |
180 | } else { |
181 | if ($contextNode !== null) { |
182 | $return = $contextNode->getAttribute($attribute); |
183 | } |
184 | } |
185 | |
186 | return ($return == '') ? null : $return; |
187 | } |
188 | |
189 | /** |
190 | * Get element value. |
191 | * |
192 | * @param string $path |
193 | * |
194 | * @return null|string |
195 | */ |
196 | public function getValue($path, ?DOMElement $contextNode = null) |
197 | { |
198 | $elements = $this->getElements($path, $contextNode); |
199 | if ($elements->length > 0) { |
200 | return $elements->item(0)->nodeValue; |
201 | } |
202 | |
203 | return null; |
204 | } |
205 | |
206 | /** |
207 | * Count elements. |
208 | * |
209 | * @param string $path |
210 | * |
211 | * @return int |
212 | */ |
213 | public function countElements($path, ?DOMElement $contextNode = null) |
214 | { |
215 | $elements = $this->getElements($path, $contextNode); |
216 | |
217 | return $elements->length; |
218 | } |
219 | |
220 | /** |
221 | * Element exists. |
222 | * |
223 | * @param string $path |
224 | * |
225 | * @return bool |
226 | */ |
227 | public function elementExists($path, ?DOMElement $contextNode = null) |
228 | { |
229 | return $this->getElements($path, $contextNode)->length > 0; |
230 | } |
231 | } |