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