Total Complexity | 117 |
Total Lines | 905 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like XMLElement often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use XMLElement, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
12 | class XMLElement implements IteratorAggregate |
||
13 | { |
||
14 | /** |
||
15 | * This is an array of HTML elements that are self closing. |
||
16 | * @var array |
||
17 | */ |
||
18 | protected static $no_end_tags = array( |
||
19 | 'area', 'base', 'br', 'col', 'hr', 'img', 'input', 'link', 'meta', 'param' |
||
20 | ); |
||
21 | |||
22 | /** |
||
23 | * The name of the HTML Element, eg. 'p' |
||
24 | * @var string |
||
25 | */ |
||
26 | private $_name; |
||
27 | |||
28 | /** |
||
29 | * The value of this `XMLElement` as a string |
||
30 | * @var string |
||
31 | */ |
||
32 | private $_value = array(); |
||
33 | |||
34 | /** |
||
35 | * Any additional attributes can be included in an associative array |
||
36 | * with the key being the name and the value being the value of the |
||
37 | * attribute. |
||
38 | * @var array |
||
39 | */ |
||
40 | private $_attributes = array(); |
||
41 | |||
42 | /** |
||
43 | * Children of this `XMLElement`, which will also be `XMLElement`'s |
||
44 | * @var array |
||
45 | */ |
||
46 | private $_children = array(); |
||
47 | |||
48 | /** |
||
49 | * Any processing instructions that the XSLT should know about when a |
||
50 | * `XMLElement` is generated |
||
51 | * @var array |
||
52 | */ |
||
53 | private $_processingInstructions = array(); |
||
54 | |||
55 | /** |
||
56 | * The DTD the should be output when a `XMLElement` is generated, defaults to null. |
||
57 | * @var string |
||
58 | */ |
||
59 | private $_dtd = null; |
||
60 | |||
61 | /** |
||
62 | * The encoding of the `XMLElement`, defaults to 'utf-8' |
||
63 | * @var string |
||
64 | */ |
||
65 | private $_encoding = 'utf-8'; |
||
66 | |||
67 | /** |
||
68 | * The version of the XML that is used for generation, defaults to '1.0' |
||
69 | * @var string |
||
70 | */ |
||
71 | private $_version = '1.0'; |
||
72 | |||
73 | /** |
||
74 | * The type of element, defaults to 'xml'. Used when determining the style |
||
75 | * of end tag for this element when generated |
||
76 | * @var string |
||
77 | */ |
||
78 | private $_elementStyle = 'xml'; |
||
79 | |||
80 | /** |
||
81 | * When set to true this will include the XML declaration will be |
||
82 | * output when the `XMLElement` is generated. Defaults to `false`. |
||
83 | * @var boolean |
||
84 | */ |
||
85 | private $_includeHeader = false; |
||
86 | |||
87 | /** |
||
88 | * Specifies whether this HTML element has an closing element, or if |
||
89 | * it self closing. Defaults to `true`. |
||
90 | * eg. `<p></p>` or `<input />` |
||
91 | * @var boolean |
||
92 | */ |
||
93 | private $_selfclosing = true; |
||
94 | |||
95 | /** |
||
96 | * Specifies whether attributes need to have a value or if they can |
||
97 | * be shorthand. Defaults to `true`. An example of this would be: |
||
98 | * `<option selected>Value</option>` |
||
99 | * @var boolean |
||
100 | */ |
||
101 | private $_allowEmptyAttributes = true; |
||
102 | |||
103 | /** |
||
104 | * The constructor for the `XMLElement` |
||
105 | * |
||
106 | * @param string $name |
||
107 | * The name of the `XMLElement`, 'p'. |
||
108 | * @param string|XMLElement $value (optional) |
||
109 | * The value of this `XMLElement`, it can be a string |
||
110 | * or another `XMLElement` object. |
||
111 | * @param array $attributes (optional) |
||
112 | * Any additional attributes can be included in an associative array with |
||
113 | * the key being the name and the value being the value of the attribute. |
||
114 | * Attributes set from this array will override existing attributes |
||
115 | * set by previous params. |
||
116 | * @param boolean $createHandle |
||
117 | * Whether this function should convert the `$name` to a handle. Defaults to |
||
118 | * `false`. |
||
119 | */ |
||
120 | public function __construct($name, $value = null, array $attributes = array(), $createHandle = false) |
||
121 | { |
||
122 | $this->setName($name, $createHandle); |
||
123 | $this->setValue($value); |
||
124 | |||
125 | if (is_array($attributes) && !empty($attributes)) { |
||
126 | $this->setAttributeArray($attributes); |
||
127 | } |
||
128 | } |
||
129 | |||
130 | /** |
||
131 | * Accessor for `$_name` |
||
132 | * |
||
133 | * @return string |
||
134 | */ |
||
135 | public function getName() |
||
136 | { |
||
137 | return $this->_name; |
||
138 | } |
||
139 | |||
140 | /** |
||
141 | * Accessor for `$_value` |
||
142 | * |
||
143 | * @return string|XMLElement |
||
144 | */ |
||
145 | public function getValue() |
||
146 | { |
||
147 | $value = ''; |
||
148 | |||
149 | if (is_array($this->_value)) { |
||
150 | foreach ($this->_value as $v) { |
||
151 | if ($v instanceof XMLElement) { |
||
152 | $value .= $v->generate(); |
||
153 | } else { |
||
154 | $value .= $v; |
||
155 | } |
||
156 | } |
||
157 | } elseif (!is_null($this->_value)) { |
||
158 | $value = $this->_value; |
||
159 | } |
||
160 | |||
161 | return $value; |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * Retrieves the value of an attribute by name |
||
166 | * |
||
167 | * @param string $name |
||
168 | * @return string |
||
169 | */ |
||
170 | public function getAttribute($name) |
||
171 | { |
||
172 | if (!isset($this->_attributes[$name])) { |
||
173 | return null; |
||
174 | } |
||
175 | |||
176 | return $this->_attributes[$name]; |
||
177 | } |
||
178 | |||
179 | /** |
||
180 | * Accessor for `$this->_attributes` |
||
181 | * |
||
182 | * @return array |
||
183 | */ |
||
184 | public function getAttributes() |
||
185 | { |
||
186 | return $this->_attributes; |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * Retrieves a child-element by position |
||
191 | * |
||
192 | * @since Symphony 2.3 |
||
193 | * @param integer $position |
||
194 | * @return XMLElement |
||
195 | */ |
||
196 | public function getChild($position) |
||
197 | { |
||
198 | if (!isset($this->_children[$this->getRealIndex($position)])) { |
||
199 | return null; |
||
200 | } |
||
201 | |||
202 | return $this->_children[$this->getRealIndex($position)]; |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * Accessor for `$this->_children` |
||
207 | * |
||
208 | * @return array |
||
209 | */ |
||
210 | public function getChildren() |
||
211 | { |
||
212 | return $this->_children; |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * Accessor for `$this->_children`, returning only `XMLElement` children, |
||
217 | * not text nodes. |
||
218 | * |
||
219 | * @return XMLElementChildrenFilter |
||
220 | */ |
||
221 | public function getIterator() |
||
222 | { |
||
223 | return new XMLElementChildrenFilter(new ArrayIterator($this->_children)); |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Retrieves child-element by name and position. If no child is found, |
||
228 | * `NULL` will be returned. |
||
229 | * |
||
230 | * @since Symphony 2.3 |
||
231 | * @param string $name |
||
232 | * @param integer $position |
||
233 | * @return XMLElement |
||
234 | */ |
||
235 | public function getChildByName($name, $position) |
||
236 | { |
||
237 | $result = array_values($this->getChildrenByName($name)); |
||
238 | |||
239 | if (!isset($result[$position])) { |
||
240 | return null; |
||
241 | } |
||
242 | |||
243 | return $result[$position]; |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * Accessor to return an associative array of all `$this->_children` |
||
248 | * whose's name matches the given `$name`. If no children are found, |
||
249 | * an empty array will be returned. |
||
250 | * |
||
251 | * @since Symphony 2.2.2 |
||
252 | * @param string $name |
||
253 | * @return array |
||
254 | * An associative array where the key is the `$index` of the child |
||
255 | * in `$this->_children` |
||
256 | */ |
||
257 | public function getChildrenByName($name) |
||
258 | { |
||
259 | $result = array(); |
||
260 | |||
261 | foreach ($this as $i => $child) { |
||
262 | if ($child->getName() != $name) { |
||
263 | continue; |
||
264 | } |
||
265 | |||
266 | $result[$i] = $child; |
||
267 | } |
||
268 | |||
269 | return $result; |
||
270 | } |
||
271 | |||
272 | /** |
||
273 | * Adds processing instructions to this `XMLElement` |
||
274 | * |
||
275 | * @param string $pi |
||
276 | */ |
||
277 | public function addProcessingInstruction($pi) |
||
278 | { |
||
279 | $this->_processingInstructions[] = $pi; |
||
280 | } |
||
281 | |||
282 | /** |
||
283 | * Sets the DTD for this `XMLElement` |
||
284 | * |
||
285 | * @param string $dtd |
||
286 | */ |
||
287 | public function setDTD($dtd) |
||
288 | { |
||
289 | $this->_dtd = $dtd; |
||
290 | } |
||
291 | |||
292 | /** |
||
293 | * Sets the encoding for this `XMLElement` for when |
||
294 | * it's generated. |
||
295 | * |
||
296 | * @param string $value |
||
297 | */ |
||
298 | public function setEncoding($value) |
||
299 | { |
||
300 | $this->_encoding = $value; |
||
301 | } |
||
302 | |||
303 | /** |
||
304 | * Sets the version for the XML declaration of this |
||
305 | * `XMLElement` |
||
306 | * |
||
307 | * @param string $value |
||
308 | */ |
||
309 | public function setVersion($value) |
||
310 | { |
||
311 | $this->_version = $value; |
||
312 | } |
||
313 | |||
314 | /** |
||
315 | * Sets the style of the `XMLElement`. Used when the |
||
316 | * `XMLElement` is being generated to determine whether |
||
317 | * needs to be closed, is self closing or is standalone. |
||
318 | * |
||
319 | * @param string $style (optional) |
||
320 | * Defaults to 'xml', any other value will trigger the |
||
321 | * XMLElement to be closed by itself or left standalone |
||
322 | * if it is in the `XMLElement::no_end_tags`. |
||
323 | */ |
||
324 | public function setElementStyle($style = 'xml') |
||
327 | } |
||
328 | |||
329 | /** |
||
330 | * Sets whether this `XMLElement` needs to output an |
||
331 | * XML declaration or not. This normally is only set to |
||
332 | * true for the parent `XMLElement`, eg. 'html'. |
||
333 | * |
||
334 | * @param bool|string $value (optional) |
||
335 | * Defaults to false |
||
336 | */ |
||
337 | public function setIncludeHeader($value = false) |
||
340 | } |
||
341 | |||
342 | /** |
||
343 | * Sets whether this `XMLElement` is self closing or not. |
||
344 | * |
||
345 | * @param bool|string $value (optional) |
||
346 | * Defaults to true |
||
347 | */ |
||
348 | public function setSelfClosingTag($value = true) |
||
349 | { |
||
350 | $this->_selfclosing = $value; |
||
351 | } |
||
352 | |||
353 | /** |
||
354 | * Specifies whether attributes need to have a value |
||
355 | * or if they can be shorthand on this `XMLElement`. |
||
356 | * |
||
357 | * @param bool|string $value (optional) |
||
358 | * Defaults to true |
||
359 | */ |
||
360 | public function setAllowEmptyAttributes($value = true) |
||
361 | { |
||
362 | $this->_allowEmptyAttributes = $value; |
||
363 | } |
||
364 | |||
365 | /** |
||
366 | * Sets the name of this `XMLElement`, ie. 'p' => <p /> |
||
367 | * |
||
368 | * @since Symphony 2.3.2 |
||
369 | * @param string $name |
||
370 | * The name of the `XMLElement`, 'p'. |
||
371 | * @param boolean $createHandle |
||
372 | * Whether this function should convert the `$name` to a handle. Defaults to |
||
373 | * `false`. |
||
374 | */ |
||
375 | public function setName($name, $createHandle = false) |
||
376 | { |
||
377 | $this->_name = ($createHandle) ? Lang::createHandle($name) : $name; |
||
378 | } |
||
379 | |||
380 | /** |
||
381 | * Sets the value of the `XMLElement`. Checks to see |
||
382 | * whether the value should be prepended or appended |
||
383 | * to the children. |
||
384 | * |
||
385 | * @param string|XMLElement|array $value |
||
386 | * Defaults to true. |
||
387 | */ |
||
388 | public function setValue($value) |
||
389 | { |
||
390 | if (is_array($value)) { |
||
391 | $value = implode(', ', $value); |
||
392 | } |
||
393 | |||
394 | if (!is_null($value)) { |
||
395 | $this->_value = $value; |
||
396 | $this->appendChild($value); |
||
397 | } |
||
398 | } |
||
399 | |||
400 | /** |
||
401 | * This function will remove all text attributes from the `XMLElement` node |
||
402 | * and replace them with the given value. |
||
403 | * |
||
404 | * @since Symphony 2.4 |
||
405 | * @param string|XMLElement|array $value |
||
406 | */ |
||
407 | public function replaceValue($value) |
||
408 | { |
||
409 | foreach ($this->_children as $i => $child) { |
||
410 | if ($child instanceof XMLElement) { |
||
411 | continue; |
||
412 | } |
||
413 | |||
414 | unset($this->_children[$i]); |
||
415 | } |
||
416 | |||
417 | $this->setValue($value); |
||
418 | } |
||
419 | |||
420 | /** |
||
421 | * Sets an attribute |
||
422 | * |
||
423 | * @param string $name |
||
424 | * The name of the attribute |
||
425 | * @param string $value |
||
426 | * The value of the attribute |
||
427 | */ |
||
428 | public function setAttribute($name, $value) |
||
429 | { |
||
430 | $this->_attributes[$name] = $value; |
||
431 | } |
||
432 | |||
433 | /** |
||
434 | * A convenience method to quickly add multiple attributes to |
||
435 | * an `XMLElement` |
||
436 | * |
||
437 | * @param array $attributes |
||
438 | * Associative array with the key being the name and |
||
439 | * the value being the value of the attribute. |
||
440 | */ |
||
441 | public function setAttributeArray(array $attributes = null) |
||
442 | { |
||
443 | if (!is_array($attributes) || empty($attributes)) { |
||
444 | return; |
||
445 | } |
||
446 | |||
447 | foreach ($attributes as $name => $value) { |
||
448 | $this->setAttribute($name, $value); |
||
449 | } |
||
450 | } |
||
451 | |||
452 | /** |
||
453 | * A convenience method that encapsulate validation of a child node. |
||
454 | * This should prevent generate errors by catching them earlier. |
||
455 | * |
||
456 | * @since Symphony 2.5.0 |
||
457 | * @param XMLElement $child |
||
458 | * The child to validate |
||
459 | * |
||
460 | */ |
||
461 | private function validateChild($child) |
||
462 | { |
||
463 | if ($this === $child) { |
||
464 | throw new Exception(__('Can not add the element itself as one of its child')); |
||
465 | } |
||
466 | } |
||
467 | |||
468 | /** |
||
469 | * This function expects an array of `XMLElement` that will completely |
||
470 | * replace the contents of `$this->_children`. Take care when using |
||
471 | * this function. |
||
472 | * |
||
473 | * @since Symphony 2.2.2 |
||
474 | * @param array $children |
||
475 | * An array of XMLElement's to act as the children for the current |
||
476 | * XMLElement instance |
||
477 | * @return boolean |
||
478 | */ |
||
479 | public function setChildren(array $children = null) |
||
480 | { |
||
481 | foreach ($children as $child) { |
||
482 | $this->validateChild($child); |
||
483 | } |
||
484 | $this->_children = $children; |
||
485 | |||
486 | return true; |
||
487 | } |
||
488 | |||
489 | /** |
||
490 | * Adds an `XMLElement` to the children array |
||
491 | * |
||
492 | * @param XMLElement|string $child |
||
493 | * @return boolean |
||
494 | */ |
||
495 | public function appendChild($child) |
||
496 | { |
||
497 | $this->validateChild($child); |
||
498 | $this->_children[] = $child; |
||
499 | |||
500 | return true; |
||
501 | } |
||
502 | |||
503 | /** |
||
504 | * A convenience method to add children to an `XMLElement` |
||
505 | * quickly. |
||
506 | * |
||
507 | * @param array $children |
||
508 | */ |
||
509 | public function appendChildArray(array $children = null) |
||
510 | { |
||
511 | if (is_array($children) && !empty($children)) { |
||
512 | foreach ($children as $child) { |
||
513 | $this->appendChild($child); |
||
514 | } |
||
515 | } |
||
516 | } |
||
517 | |||
518 | /** |
||
519 | * Adds an `XMLElement` to the start of the children |
||
520 | * array, this will mean it is output before any other |
||
521 | * children when the `XMLElement` is generated |
||
522 | * |
||
523 | * @param XMLElement $child |
||
524 | */ |
||
525 | public function prependChild(XMLElement $child) |
||
526 | { |
||
527 | array_unshift($this->_children, $child); |
||
528 | } |
||
529 | |||
530 | /** |
||
531 | * A convenience method to quickly add a CSS class to this `XMLElement`'s |
||
532 | * existing class attribute. If the attribute does not exist, it will |
||
533 | * be created. |
||
534 | * |
||
535 | * @since Symphony 2.2.2 |
||
536 | * @param string $class |
||
537 | * The CSS classname to add to this `XMLElement` |
||
538 | */ |
||
539 | public function addClass($class) |
||
540 | { |
||
541 | $current = preg_split('%\s+%', $this->getAttribute('class'), 0, PREG_SPLIT_NO_EMPTY); |
||
542 | $added = preg_split('%\s+%', $class, 0, PREG_SPLIT_NO_EMPTY); |
||
543 | $current = array_merge($current, $added); |
||
544 | $classes = implode(' ', $current); |
||
545 | |||
546 | $this->setAttribute('class', $classes); |
||
547 | } |
||
548 | |||
549 | /** |
||
550 | * A convenience method to quickly remove a CSS class from an |
||
551 | * `XMLElement`'s existing class attribute. If the attribute does not |
||
552 | * exist, this method will do nothing. |
||
553 | * |
||
554 | * @since Symphony 2.2.2 |
||
555 | * @param string $class |
||
556 | * The CSS classname to remove from this `XMLElement` |
||
557 | */ |
||
558 | public function removeClass($class) |
||
559 | { |
||
560 | $classes = preg_split('%\s+%', $this->getAttribute('class'), 0, PREG_SPLIT_NO_EMPTY); |
||
561 | $removed = preg_split('%\s+%', $class, 0, PREG_SPLIT_NO_EMPTY); |
||
562 | $classes = array_diff($classes, $removed); |
||
563 | $classes = implode(' ', $classes); |
||
564 | |||
565 | $this->setAttribute('class', $classes); |
||
566 | } |
||
567 | |||
568 | /** |
||
569 | * Returns the number of children this `XMLElement` has. |
||
570 | * @return integer |
||
571 | */ |
||
572 | public function getNumberOfChildren() |
||
575 | } |
||
576 | |||
577 | /** |
||
578 | * Given the position of the child in the `$this->_children`, |
||
579 | * this function will unset the child at that position. This function |
||
580 | * is not reversible. This function does not alter the key's of |
||
581 | * `$this->_children` after removing a child |
||
582 | * |
||
583 | * @since Symphony 2.2.2 |
||
584 | * @param integer $index |
||
585 | * The index of the child to be removed. If the index given is negative |
||
586 | * it will be calculated from the end of `$this->_children`. |
||
587 | * @return boolean |
||
588 | * true if child was successfully removed, false otherwise. |
||
589 | */ |
||
590 | public function removeChildAt($index) |
||
605 | } |
||
606 | |||
607 | /** |
||
608 | * Given a desired index, and an `XMLElement`, this function will insert |
||
609 | * the child at that index in `$this->_children` shuffling all children |
||
610 | * greater than `$index` down one. If the `$index` given is greater then |
||
611 | * the number of children for this `XMLElement`, the `$child` will be |
||
612 | * appended to the current `$this->_children` array. |
||
613 | * |
||
614 | * @since Symphony 2.2.2 |
||
615 | * @param integer $index |
||
616 | * The index where the `$child` should be inserted. If this is negative |
||
617 | * the index will be calculated from the end of `$this->_children`. |
||
618 | * @param XMLElement $child |
||
619 | * The XMLElement to insert at the desired `$index` |
||
620 | * @return boolean |
||
621 | */ |
||
622 | public function insertChildAt($index, XMLElement $child = null) |
||
623 | { |
||
624 | if (!is_numeric($index)) { |
||
625 | return false; |
||
626 | } |
||
627 | |||
628 | $this->validateChild($child); |
||
629 | |||
630 | if ($index >= $this->getNumberOfChildren()) { |
||
631 | return $this->appendChild($child); |
||
632 | } |
||
633 | |||
634 | $start = array_slice($this->_children, 0, $index); |
||
635 | $end = array_slice($this->_children, $index); |
||
636 | |||
637 | $merge = array_merge($start, array( |
||
638 | $index => $child |
||
639 | ), $end); |
||
640 | |||
641 | return $this->setChildren($merge); |
||
642 | } |
||
643 | |||
644 | /** |
||
645 | * Given the position of the child to replace, and an `XMLElement` |
||
646 | * of the replacement child, this function will replace one child |
||
647 | * with another |
||
648 | * |
||
649 | * @since Symphony 2.2.2 |
||
650 | * @param integer $index |
||
651 | * The index of the child to be replaced. If the index given is negative |
||
652 | * it will be calculated from the end of `$this->_children`. |
||
653 | * @param XMLElement $child |
||
654 | * An XMLElement of the new child |
||
655 | * @return boolean |
||
656 | */ |
||
657 | public function replaceChildAt($index, XMLElement $child = null) |
||
658 | { |
||
659 | if (!is_numeric($index)) { |
||
660 | return false; |
||
661 | } |
||
662 | |||
663 | $this->validateChild($child); |
||
664 | |||
665 | $index = $this->getRealIndex($index); |
||
666 | |||
667 | if (!isset($this->_children[$index])) { |
||
668 | return false; |
||
669 | } |
||
670 | |||
671 | $this->_children[$index] = $child; |
||
672 | |||
673 | return true; |
||
674 | } |
||
675 | |||
676 | /** |
||
677 | * Given an `$index`, return the real index in `$this->_children` |
||
678 | * depending on if the value is negative or not. Negative values |
||
679 | * will work from the end of an array. |
||
680 | * |
||
681 | * @since Symphony 2.2.2 |
||
682 | * @param integer $index |
||
683 | * Positive indexes are returned as is, negative indexes are deducted |
||
684 | * from the end of `$this->_children` |
||
685 | * @return integer |
||
686 | */ |
||
687 | private function getRealIndex($index) |
||
688 | { |
||
689 | if ($index >= 0) { |
||
690 | return $index; |
||
691 | } |
||
692 | |||
693 | return $this->getNumberOfChildren() + $index; |
||
694 | } |
||
695 | |||
696 | /** |
||
697 | * This function strips characters that are not allowed in XML |
||
698 | * |
||
699 | * @since Symphony 2.3 |
||
700 | * @link http://www.w3.org/TR/xml/#charsets |
||
701 | * @link http://www.phpedit.net/snippet/Remove-Invalid-XML-Characters |
||
702 | * @param string $value |
||
703 | * @return string |
||
704 | */ |
||
705 | public static function stripInvalidXMLCharacters($value) |
||
731 | } |
||
732 | } |
||
733 | |||
734 | /** |
||
735 | * This function will turn the `XMLElement` into a string |
||
736 | * representing the element as it would appear in the markup. |
||
737 | * The result is valid XML. |
||
738 | * |
||
739 | * @param boolean $indent |
||
740 | * Defaults to false |
||
741 | * @param integer $tab_depth |
||
742 | * Defaults to 0, indicates the number of tabs (\t) that this |
||
743 | * element should be indented by in the output string |
||
744 | * @param boolean $has_parent |
||
745 | * Defaults to false, set to true when the children are being |
||
746 | * generated. Only the parent will output an XML declaration |
||
747 | * if `$this->_includeHeader` is set to true. |
||
748 | * @return string |
||
749 | */ |
||
750 | public function generate($indent = false, $tab_depth = 0, $has_parent = false) |
||
751 | { |
||
752 | $result = null; |
||
753 | $newline = ($indent ? PHP_EOL : null); |
||
754 | |||
755 | if (!$has_parent) { |
||
756 | if ($this->_includeHeader) { |
||
757 | $result .= sprintf( |
||
758 | '<?xml version="%s" encoding="%s" ?>%s', |
||
759 | $this->_version, |
||
760 | $this->_encoding, |
||
761 | $newline |
||
762 | ); |
||
763 | } |
||
764 | |||
765 | if ($this->_dtd) { |
||
766 | $result .= $this->_dtd . $newline; |
||
767 | } |
||
768 | |||
769 | if (is_array($this->_processingInstructions) && !empty($this->_processingInstructions)) { |
||
770 | $result .= implode(PHP_EOL, $this->_processingInstructions); |
||
771 | } |
||
772 | } |
||
773 | |||
774 | $result .= ($indent ? str_repeat("\t", $tab_depth) : null) . '<' . $this->getName(); |
||
775 | |||
776 | $attributes = $this->getAttributes(); |
||
777 | |||
778 | if (!empty($attributes)) { |
||
779 | foreach ($attributes as $attribute => $value) { |
||
780 | $length = strlen($value); |
||
781 | if ($length !== 0 || $length === 0 && $this->_allowEmptyAttributes) { |
||
782 | $result .= sprintf(' %s="%s"', $attribute, $value); |
||
783 | } |
||
784 | } |
||
785 | } |
||
786 | |||
787 | $value = $this->getValue(); |
||
788 | $added_newline = false; |
||
789 | |||
790 | if ($this->getNumberOfChildren() > 0 || strlen($value) !== 0 || !$this->_selfclosing) { |
||
791 | $result .= '>'; |
||
792 | |||
793 | foreach ($this->_children as $i => $child) { |
||
794 | if (!($child instanceof XMLElement)) { |
||
795 | $result .= $child; |
||
796 | } else { |
||
797 | if ($added_newline === false) { |
||
798 | $added_newline = true; |
||
799 | $result .= $newline; |
||
800 | } |
||
801 | |||
802 | $child->setElementStyle($this->_elementStyle); |
||
803 | $result .= $child->generate($indent, $tab_depth + 1, true); |
||
804 | } |
||
805 | } |
||
806 | |||
807 | $result .= sprintf( |
||
808 | "%s</%s>%s", |
||
809 | ($indent && $added_newline ? str_repeat("\t", $tab_depth) : null), |
||
810 | $this->getName(), |
||
811 | $newline |
||
812 | ); |
||
813 | |||
814 | // Empty elements: |
||
815 | } else { |
||
816 | if ($this->_elementStyle === 'xml') { |
||
817 | $result .= ' />'; |
||
818 | } elseif (in_array($this->_name, XMLElement::$no_end_tags) || (substr($this->getName(), 0, 3) === '!--')) { |
||
819 | $result .= '>'; |
||
820 | } else { |
||
821 | $result .= sprintf("></%s>", $this->getName()); |
||
822 | } |
||
823 | |||
824 | $result .= $newline; |
||
825 | } |
||
826 | |||
827 | return $result; |
||
828 | } |
||
829 | |||
830 | /** |
||
831 | * Given a string of XML, this function will convert it to an `XMLElement` |
||
832 | * object and return the result. |
||
833 | * |
||
834 | * @since Symphony 2.4 |
||
835 | * @param string $root_element |
||
836 | * @param string $xml |
||
837 | * A string of XML |
||
838 | * @return XMLElement |
||
839 | */ |
||
840 | public static function convertFromXMLString($root_element, $xml) |
||
841 | { |
||
842 | $doc = new DOMDocument('1.0', 'utf-8'); |
||
843 | $doc->loadXML($xml); |
||
844 | |||
845 | return self::convertFromDOMDocument($root_element, $doc); |
||
846 | } |
||
847 | |||
848 | /** |
||
849 | * Given a `DOMDocument`, this function will convert it to an `XMLElement` |
||
850 | * object and return the result. |
||
851 | * |
||
852 | * @since Symphony 2.4 |
||
853 | * @param string $root_element |
||
854 | * @param DOMDOcument $doc |
||
855 | * @return XMLElement |
||
856 | */ |
||
857 | public static function convertFromDOMDocument($root_element, DOMDocument $doc) |
||
858 | { |
||
859 | $xpath = new DOMXPath($doc); |
||
860 | $root = new XMLElement($root_element); |
||
861 | |||
862 | foreach ($xpath->query('.') as $node) { |
||
863 | self::convertNode($root, $node); |
||
864 | } |
||
865 | |||
866 | return $root; |
||
867 | } |
||
868 | |||
869 | /** |
||
870 | * This helper function is used by `XMLElement::convertFromDOMDocument` |
||
871 | * to recursively convert `DOMNode` into an `XMLElement` structure |
||
872 | * |
||
873 | * @since Symphony 2.4 |
||
874 | * @param XMLElement $root |
||
875 | * @param DOMNOde $node |
||
876 | * @return XMLElement |
||
877 | */ |
||
878 | private static function convert(XMLElement $root = null, DOMNode $node) |
||
879 | { |
||
880 | $el = new XMLElement($node->tagName); |
||
881 | |||
882 | self::convertNode($el, $node); |
||
883 | |||
884 | if (is_null($root)) { |
||
885 | return $el; |
||
886 | } else { |
||
887 | $root->appendChild($el); |
||
888 | } |
||
889 | } |
||
890 | |||
891 | /** |
||
892 | * Given a DOMNode, this function will help replicate it as an |
||
893 | * XMLElement object |
||
894 | * |
||
895 | * @since Symphony 2.5.2 |
||
896 | * @param XMLElement $element |
||
897 | * @param DOMNode $node |
||
898 | */ |
||
899 | private static function convertNode(XMLElement $element, DOMNode $node) |
||
917 | } |
||
918 | } |
||
919 | } |
||
920 | } |
||
921 | } |
||
922 | |||
923 | /** |
||
924 | * Creates a filter that only returns XMLElement items |
||
925 | */ |
||
926 | class XMLElementChildrenFilter extends FilterIterator |
||
927 | { |
||
928 | public function accept() |
||
929 | { |
||
930 | $item = $this->getInnerIterator()->current(); |
||
931 | |||
932 | return $item instanceof XMLElement; |
||
935 |