PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.4
Booking for Appointments and Events Calendar – Amelia v2.4.4
2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / vendor / sabre / xml / lib / Reader.php
ameliabooking / vendor / sabre / xml / lib Last commit date
Deserializer 6 months ago Element 6 months ago Serializer 6 months ago ContextStackTrait.php 6 months ago Element.php 6 months ago LibXMLException.php 6 months ago ParseException.php 6 months ago Reader.php 6 months ago Service.php 6 months ago Version.php 6 months ago Writer.php 6 months ago XmlDeserializable.php 6 months ago XmlSerializable.php 6 months ago
Reader.php
318 lines
1 <?php
2
3 declare(strict_types=1);
4
5 namespace AmeliaVendor\Sabre\Xml;
6
7 use XMLReader;
8
9 /**
10 * The Reader class expands upon PHP's built-in XMLReader.
11 *
12 * The intended usage, is to assign certain XML elements to PHP classes. These
13 * need to be registered using the $elementMap public property.
14 *
15 * After this is done, a single call to parse() will parse the entire document,
16 * and delegate sub-sections of the document to element classes.
17 *
18 * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
19 * @author Evert Pot (http://evertpot.com/)
20 * @license http://sabre.io/license/ Modified BSD License
21 */
22 class Reader extends \XMLReader
23 {
24 use ContextStackTrait;
25
26 /**
27 * Returns the current nodename in clark-notation.
28 *
29 * For example: "{http://www.w3.org/2005/Atom}feed".
30 * Or if no namespace is defined: "{}feed".
31 *
32 * This method returns null if we're not currently on an element.
33 */
34 public function getClark(): ?string
35 {
36 if (!$this->localName) {
37 return null;
38 }
39
40 return '{'.$this->namespaceURI.'}'.$this->localName;
41 }
42
43 /**
44 * Reads the entire document.
45 *
46 * This function returns an array with the following three elements:
47 * * name - The root element name.
48 * * value - The value for the root element.
49 * * attributes - An array of attributes.
50 *
51 * This function will also disable the standard libxml error handler (which
52 * usually just results in PHP errors), and throw exceptions instead.
53 *
54 * @return array<string, mixed>
55 */
56 public function parse(): array
57 {
58 $previousEntityState = null;
59 $shouldCallLibxmlDisableEntityLoader = (\LIBXML_VERSION < 20900);
60 if ($shouldCallLibxmlDisableEntityLoader) {
61 $previousEntityState = libxml_disable_entity_loader(true);
62 }
63 $previousSetting = libxml_use_internal_errors(true);
64
65 try {
66 while (self::ELEMENT !== $this->nodeType) {
67 if (!$this->read()) {
68 $errors = libxml_get_errors();
69 libxml_clear_errors();
70 if ($errors) {
71 throw new LibXMLException($errors);
72 }
73 }
74 }
75 $result = $this->parseCurrentElement();
76
77 // last line of defense in case errors did occur above
78 $errors = libxml_get_errors();
79 libxml_clear_errors();
80 if ($errors) {
81 throw new LibXMLException($errors);
82 }
83 } finally {
84 libxml_use_internal_errors($previousSetting);
85 if ($shouldCallLibxmlDisableEntityLoader) {
86 libxml_disable_entity_loader($previousEntityState);
87 }
88 }
89
90 return $result;
91 }
92
93 /**
94 * parseGetElements parses everything in the current sub-tree,
95 * and returns an array of elements.
96 *
97 * Each element has a 'name', 'value' and 'attributes' key.
98 *
99 * If the element didn't contain sub-elements, an empty array is always
100 * returned. If there was any text inside the element, it will be
101 * discarded.
102 *
103 * If the $elementMap argument is specified, the existing elementMap will
104 * be overridden while parsing the tree, and restored after this process.
105 *
106 * @param array<string, mixed>|null $elementMap
107 *
108 * @return array<int,array<string, mixed>>
109 */
110 public function parseGetElements(?array $elementMap = null): array
111 {
112 $result = $this->parseInnerTree($elementMap);
113 if (!is_array($result)) {
114 return [];
115 }
116
117 return $result;
118 }
119
120 /**
121 * Parses all elements below the current element.
122 *
123 * This method will return a string if this was a text-node, or an array if
124 * there were sub-elements.
125 *
126 * If there's both text and sub-elements, the text will be discarded.
127 *
128 * If the $elementMap argument is specified, the existing elementMap will
129 * be overridden while parsing the tree, and restored after this process.
130 *
131 * @param array<string, mixed>|null $elementMap
132 *
133 * @return array<int,array<string, mixed>>|string|null
134 */
135 public function parseInnerTree(?array $elementMap = null)
136 {
137 $text = null;
138 $elements = [];
139
140 if (self::ELEMENT === $this->nodeType && $this->isEmptyElement) {
141 // Easy!
142 $this->next();
143
144 return null;
145 }
146
147 if (!is_null($elementMap)) {
148 $this->pushContext();
149 $this->elementMap = $elementMap;
150 }
151
152 try {
153 if (!$this->read()) {
154 $errors = libxml_get_errors();
155 libxml_clear_errors();
156 if ($errors) {
157 throw new LibXMLException($errors);
158 }
159 throw new ParseException('This should never happen (famous last words)');
160 }
161
162 $keepOnParsing = true;
163
164 while ($keepOnParsing) {
165 if (!$this->isValid()) {
166 $errors = libxml_get_errors();
167
168 if ($errors) {
169 libxml_clear_errors();
170 throw new LibXMLException($errors);
171 }
172 }
173
174 switch ($this->nodeType) {
175 case self::ELEMENT:
176 $elements[] = $this->parseCurrentElement();
177 break;
178 case self::TEXT:
179 case self::CDATA:
180 $text .= $this->value;
181 $this->read();
182 break;
183 case self::END_ELEMENT:
184 // Ensuring we are moving the cursor after the end element.
185 $this->read();
186 $keepOnParsing = false;
187 break;
188 case self::NONE:
189 throw new ParseException('We hit the end of the document prematurely. This likely means that some parser "eats" too many elements. Do not attempt to continue parsing.');
190 default:
191 // Advance to the next element
192 $this->read();
193 break;
194 }
195 }
196 } finally {
197 if (!is_null($elementMap)) {
198 $this->popContext();
199 }
200 }
201
202 return $elements ?: $text;
203 }
204
205 /**
206 * Reads all text below the current element, and returns this as a string.
207 */
208 public function readText(): string
209 {
210 $result = '';
211 $previousDepth = $this->depth;
212
213 while ($this->read() && $this->depth != $previousDepth) {
214 if (in_array($this->nodeType, [\XMLReader::TEXT, \XMLReader::CDATA, \XMLReader::WHITESPACE])) {
215 $result .= $this->value;
216 }
217 }
218
219 return $result;
220 }
221
222 /**
223 * Parses the current XML element.
224 *
225 * This method returns arn array with 3 properties:
226 * * name - A clark-notation XML element name.
227 * * value - The parsed value.
228 * * attributes - A key-value list of attributes.
229 *
230 * @return array <string, mixed>
231 */
232 public function parseCurrentElement(): array
233 {
234 $name = $this->getClark();
235
236 $attributes = [];
237
238 if ($this->hasAttributes) {
239 $attributes = $this->parseAttributes();
240 }
241
242 $value = call_user_func(
243 $this->getDeserializerForElementName((string) $name),
244 $this
245 );
246
247 return [
248 'name' => $name,
249 'value' => $value,
250 'attributes' => $attributes,
251 ];
252 }
253
254 /**
255 * Grabs all the attributes from the current element, and returns them as a
256 * key-value array.
257 *
258 * If the attributes are part of the same namespace, they will simply be
259 * short keys. If they are defined on a different namespace, the attribute
260 * name will be returned in clark-notation.
261 *
262 * @return array<string, mixed>
263 */
264 public function parseAttributes(): array
265 {
266 $attributes = [];
267
268 while ($this->moveToNextAttribute()) {
269 if ($this->namespaceURI) {
270 // Ignoring 'xmlns', it doesn't make any sense.
271 if ('http://www.w3.org/2000/xmlns/' === $this->namespaceURI) {
272 continue;
273 }
274
275 $name = $this->getClark();
276 $attributes[$name] = $this->value;
277 } else {
278 $attributes[$this->localName] = $this->value;
279 }
280 }
281 $this->moveToElement();
282
283 return $attributes;
284 }
285
286 /**
287 * Returns the function that should be used to parse the element identified
288 * by its clark-notation name.
289 */
290 public function getDeserializerForElementName(string $name): callable
291 {
292 if (!array_key_exists($name, $this->elementMap)) {
293 if ('{}' == substr($name, 0, 2) && array_key_exists(substr($name, 2), $this->elementMap)) {
294 $name = substr($name, 2);
295 } else {
296 return ['AmeliaVendor\\Sabre\\Xml\\Element\\Base', 'xmlDeserialize'];
297 }
298 }
299
300 $deserializer = $this->elementMap[$name];
301 if (is_callable($deserializer)) {
302 return $deserializer;
303 }
304
305 if (is_subclass_of($deserializer, 'AmeliaVendor\\Sabre\\Xml\\XmlDeserializable')) {
306 return [$deserializer, 'xmlDeserialize'];
307 }
308
309 $type = gettype($deserializer);
310 if (is_string($deserializer)) {
311 $type .= ' ('.$deserializer.')';
312 } elseif (is_object($deserializer)) {
313 $type .= ' ('.get_class($deserializer).')';
314 }
315 throw new \LogicException('Could not use this type as a deserializer: '.$type.' for element: '.$name);
316 }
317 }
318