|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* This file is part of PHPOffice Common |
|
4
|
|
|
* |
|
5
|
|
|
* PHPOffice Common is free software distributed under the terms of the GNU Lesser |
|
6
|
|
|
* General Public License version 3 as published by the Free Software Foundation. |
|
7
|
|
|
* |
|
8
|
|
|
* For the full copyright and license information, please read the LICENSE |
|
9
|
|
|
* file that was distributed with this source code. For the full list of |
|
10
|
|
|
* contributors, visit https://github.com/PHPOffice/Common/contributors. |
|
11
|
|
|
* |
|
12
|
|
|
* @link https://github.com/PHPOffice/Common |
|
13
|
|
|
* @copyright 2009-2016 PHPOffice Common contributors |
|
14
|
|
|
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 |
|
15
|
|
|
*/ |
|
16
|
|
|
|
|
17
|
|
|
namespace PhpOffice\Common; |
|
18
|
|
|
|
|
19
|
|
|
/** |
|
20
|
|
|
* XML Reader wrapper |
|
21
|
|
|
* |
|
22
|
|
|
* @since 0.2.1 |
|
23
|
|
|
*/ |
|
24
|
|
|
class XMLReader |
|
25
|
|
|
{ |
|
26
|
|
|
/** |
|
27
|
|
|
* DOMDocument object |
|
28
|
|
|
* |
|
29
|
|
|
* @var \DOMDocument |
|
30
|
|
|
*/ |
|
31
|
|
|
private $dom = null; |
|
32
|
|
|
|
|
33
|
|
|
/** |
|
34
|
|
|
* DOMXpath object |
|
35
|
|
|
* |
|
36
|
|
|
* @var \DOMXpath |
|
37
|
|
|
*/ |
|
38
|
|
|
private $xpath = null; |
|
39
|
|
|
|
|
40
|
|
|
/** |
|
41
|
|
|
* Get DOMDocument from ZipArchive |
|
42
|
|
|
* |
|
43
|
|
|
* @param string $zipFile |
|
44
|
|
|
* @param string $xmlFile |
|
45
|
|
|
* @return \DOMDocument|false |
|
46
|
|
|
* @throws \Exception |
|
47
|
|
|
*/ |
|
48
|
2 |
|
public function getDomFromZip($zipFile, $xmlFile) |
|
49
|
|
|
{ |
|
50
|
2 |
|
if (file_exists($zipFile) === false) { |
|
51
|
1 |
|
throw new \Exception('Cannot find archive file.'); |
|
52
|
|
|
} |
|
53
|
|
|
|
|
54
|
1 |
|
$zip = new \ZipArchive(); |
|
55
|
1 |
|
$zip->open($zipFile); |
|
56
|
1 |
|
$content = $zip->getFromName($xmlFile); |
|
57
|
1 |
|
$zip->close(); |
|
58
|
|
|
|
|
59
|
1 |
|
if ($content === false) { |
|
60
|
1 |
|
return false; |
|
61
|
|
|
} |
|
62
|
|
|
|
|
63
|
1 |
|
return $this->getDomFromString($content); |
|
64
|
|
|
} |
|
65
|
|
|
|
|
66
|
|
|
/** |
|
67
|
|
|
* Call libxml_disable_entity_loader, but only for PHP < 8 |
|
68
|
|
|
* |
|
69
|
|
|
* @param bool $value |
|
70
|
|
|
* @return bool |
|
71
|
|
|
*/ |
|
72
|
6 |
|
public static function libxml_disable($value) |
|
73
|
|
|
{ |
|
74
|
6 |
|
return version_compare(PHP_VERSION, '8') < 0 && libxml_disable_entity_loader($value); |
|
75
|
|
|
} |
|
76
|
|
|
|
|
77
|
|
|
/** |
|
78
|
|
|
* Get DOMDocument from content string |
|
79
|
|
|
* |
|
80
|
|
|
* @param string $content |
|
81
|
|
|
* @return \DOMDocument |
|
82
|
|
|
*/ |
|
83
|
6 |
|
public function getDomFromString($content) |
|
84
|
|
|
{ |
|
85
|
6 |
|
$originalLibXMLEntityValue = self::libxml_disable(true); |
|
86
|
6 |
|
$this->dom = new \DOMDocument(); |
|
87
|
6 |
|
$this->dom->loadXML($content); |
|
88
|
6 |
|
self::libxml_disable($originalLibXMLEntityValue); |
|
89
|
|
|
|
|
90
|
6 |
|
return $this->dom; |
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
|
|
/** |
|
94
|
|
|
* Get elements |
|
95
|
|
|
* |
|
96
|
|
|
* @param string $path |
|
97
|
|
|
* @param \DOMElement $contextNode |
|
98
|
|
|
* @return \DOMNodeList |
|
99
|
|
|
*/ |
|
100
|
6 |
|
public function getElements($path, \DOMElement $contextNode = null) |
|
101
|
|
|
{ |
|
102
|
6 |
|
if ($this->dom === null) { |
|
103
|
1 |
|
return array(); |
|
|
|
|
|
|
104
|
|
|
} |
|
105
|
6 |
|
if ($this->xpath === null) { |
|
106
|
5 |
|
$this->xpath = new \DOMXpath($this->dom); |
|
107
|
|
|
} |
|
108
|
|
|
|
|
109
|
6 |
|
if (is_null($contextNode)) { |
|
110
|
6 |
|
return $this->xpath->query($path); |
|
111
|
|
|
} |
|
112
|
|
|
|
|
113
|
1 |
|
return $this->xpath->query($path, $contextNode); |
|
114
|
|
|
} |
|
115
|
|
|
|
|
116
|
|
|
/** |
|
117
|
|
|
* Registers the namespace with the DOMXPath object |
|
118
|
|
|
* |
|
119
|
|
|
* @param string $prefix The prefix |
|
120
|
|
|
* @param string $namespaceURI The URI of the namespace |
|
121
|
|
|
* @return bool true on success or false on failure |
|
122
|
|
|
* @throws \InvalidArgumentException If called before having loaded the DOM document |
|
123
|
|
|
*/ |
|
124
|
2 |
|
public function registerNamespace($prefix, $namespaceURI) |
|
125
|
|
|
{ |
|
126
|
2 |
|
if ($this->dom === null) { |
|
127
|
1 |
|
throw new \InvalidArgumentException('Dom needs to be loaded before registering a namespace'); |
|
128
|
|
|
} |
|
129
|
1 |
|
if ($this->xpath === null) { |
|
130
|
1 |
|
$this->xpath = new \DOMXpath($this->dom); |
|
131
|
|
|
} |
|
132
|
1 |
|
return $this->xpath->registerNamespace($prefix, $namespaceURI); |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
/** |
|
136
|
|
|
* Get element |
|
137
|
|
|
* |
|
138
|
|
|
* @param string $path |
|
139
|
|
|
* @param \DOMElement $contextNode |
|
140
|
|
|
* @return \DOMElement|null |
|
141
|
|
|
*/ |
|
142
|
3 |
|
public function getElement($path, \DOMElement $contextNode = null) |
|
143
|
|
|
{ |
|
144
|
3 |
|
$elements = $this->getElements($path, $contextNode); |
|
145
|
3 |
|
if ($elements->length > 0) { |
|
146
|
2 |
|
return $elements->item(0); |
|
147
|
|
|
} |
|
148
|
|
|
|
|
149
|
1 |
|
return null; |
|
150
|
|
|
} |
|
151
|
|
|
|
|
152
|
|
|
/** |
|
153
|
|
|
* Get element attribute |
|
154
|
|
|
* |
|
155
|
|
|
* @param string $attribute |
|
156
|
|
|
* @param \DOMElement $contextNode |
|
157
|
|
|
* @param string $path |
|
158
|
|
|
* @return string|null |
|
159
|
|
|
*/ |
|
160
|
1 |
|
public function getAttribute($attribute, \DOMElement $contextNode = null, $path = null) |
|
161
|
|
|
{ |
|
162
|
1 |
|
$return = null; |
|
163
|
1 |
|
if ($path !== null) { |
|
164
|
1 |
|
$elements = $this->getElements($path, $contextNode); |
|
165
|
1 |
|
if ($elements->length > 0) { |
|
166
|
|
|
/** @var \DOMElement $node Type hint */ |
|
167
|
1 |
|
$node = $elements->item(0); |
|
168
|
1 |
|
$return = $node->getAttribute($attribute); |
|
169
|
|
|
} |
|
170
|
|
|
} else { |
|
171
|
1 |
|
if ($contextNode !== null) { |
|
172
|
1 |
|
$return = $contextNode->getAttribute($attribute); |
|
173
|
|
|
} |
|
174
|
|
|
} |
|
175
|
|
|
|
|
176
|
1 |
|
return ($return == '') ? null : $return; |
|
177
|
|
|
} |
|
178
|
|
|
|
|
179
|
|
|
/** |
|
180
|
|
|
* Get element value |
|
181
|
|
|
* |
|
182
|
|
|
* @param string $path |
|
183
|
|
|
* @param \DOMElement $contextNode |
|
184
|
|
|
* @return string|null |
|
185
|
|
|
*/ |
|
186
|
2 |
|
public function getValue($path, \DOMElement $contextNode = null) |
|
187
|
|
|
{ |
|
188
|
2 |
|
$elements = $this->getElements($path, $contextNode); |
|
189
|
2 |
|
if ($elements->length > 0) { |
|
190
|
1 |
|
return $elements->item(0)->nodeValue; |
|
191
|
|
|
} |
|
192
|
|
|
|
|
193
|
1 |
|
return null; |
|
194
|
|
|
} |
|
195
|
|
|
|
|
196
|
|
|
/** |
|
197
|
|
|
* Count elements |
|
198
|
|
|
* |
|
199
|
|
|
* @param string $path |
|
200
|
|
|
* @param \DOMElement $contextNode |
|
201
|
|
|
* @return integer |
|
202
|
|
|
*/ |
|
203
|
1 |
|
public function countElements($path, \DOMElement $contextNode = null) |
|
204
|
|
|
{ |
|
205
|
1 |
|
$elements = $this->getElements($path, $contextNode); |
|
206
|
|
|
|
|
207
|
1 |
|
return $elements->length; |
|
208
|
|
|
} |
|
209
|
|
|
|
|
210
|
|
|
/** |
|
211
|
|
|
* Element exists |
|
212
|
|
|
* |
|
213
|
|
|
* @param string $path |
|
214
|
|
|
* @param \DOMElement $contextNode |
|
215
|
|
|
* @return boolean |
|
216
|
|
|
*/ |
|
217
|
4 |
|
public function elementExists($path, \DOMElement $contextNode = null) |
|
218
|
|
|
{ |
|
219
|
4 |
|
return $this->getElements($path, $contextNode)->length > 0; |
|
220
|
|
|
} |
|
221
|
|
|
} |
|
222
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_functionexpects aPostobject, and outputs the author of the post. The base classPostreturns a simple string and outputting a simple string will work just fine. However, the child classBlogPostwhich is a sub-type ofPostinstead decided to return anobject, and is therefore violating the SOLID principles. If aBlogPostwere passed tomy_function, PHP would not complain, but ultimately fail when executing thestrtouppercall in its body.