These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * @package toolkit |
||
4 | */ |
||
5 | /** |
||
6 | * `XMLElement` is a class used to simulate PHP's `DOMElement` |
||
7 | * class. Each object is a representation of a HTML element |
||
8 | * and can store it's children in an array. When an `XMLElement` |
||
9 | * is generated, it is output as an XML string. |
||
10 | */ |
||
11 | |||
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); |
||
0 ignored issues
–
show
|
|||
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') |
||
325 | { |
||
326 | $this->_elementStyle = $style; |
||
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) |
||
338 | { |
||
339 | $this->_includeHeader = $value; |
||
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); |
||
0 ignored issues
–
show
It seems like
$child defined by parameter $child on line 495 can also be of type string ; however, XMLElement::validateChild() does only seem to accept object<XMLElement> , maybe add an additional type check?
This check looks at variables that have been passed in as parameters and are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble.
Loading history...
|
|||
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 | View Code Duplication | 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 | View Code Duplication | 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() |
||
573 | { |
||
574 | return count($this->_children); |
||
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) |
||
591 | { |
||
592 | if (!is_numeric($index)) { |
||
593 | return false; |
||
594 | } |
||
595 | |||
596 | $index = $this->getRealIndex($index); |
||
597 | |||
598 | if (!isset($this->_children[$index])) { |
||
599 | return false; |
||
600 | } |
||
601 | |||
602 | unset($this->_children[$index]); |
||
603 | |||
604 | return true; |
||
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); |
||
0 ignored issues
–
show
It seems like
$child defined by parameter $child on line 622 can be null ; however, XMLElement::validateChild() does not accept null , maybe add an additional type check?
It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null. We recommend to add an additional type check (or disallow null for the parameter): function notNullable(stdClass $x) { }
// Unsafe
function withoutCheck(stdClass $x = null) {
notNullable($x);
}
// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
if ($x instanceof stdClass) {
notNullable($x);
}
}
// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
notNullable($x);
}
Loading history...
|
|||
629 | |||
630 | if ($index >= $this->getNumberOfChildren()) { |
||
631 | return $this->appendChild($child); |
||
0 ignored issues
–
show
It seems like
$child defined by parameter $child on line 622 can be null ; however, XMLElement::appendChild() does not accept null , maybe add an additional type check?
It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null. We recommend to add an additional type check (or disallow null for the parameter): function notNullable(stdClass $x) { }
// Unsafe
function withoutCheck(stdClass $x = null) {
notNullable($x);
}
// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
if ($x instanceof stdClass) {
notNullable($x);
}
}
// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
notNullable($x);
}
Loading history...
|
|||
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); |
||
0 ignored issues
–
show
It seems like
$child defined by parameter $child on line 657 can be null ; however, XMLElement::validateChild() does not accept null , maybe add an additional type check?
It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null. We recommend to add an additional type check (or disallow null for the parameter): function notNullable(stdClass $x) { }
// Unsafe
function withoutCheck(stdClass $x = null) {
notNullable($x);
}
// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
if ($x instanceof stdClass) {
notNullable($x);
}
}
// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
notNullable($x);
}
Loading history...
|
|||
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) |
||
706 | { |
||
707 | if (Lang::isUnicodeCompiled()) { |
||
708 | return preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $value); |
||
709 | } else { |
||
710 | $ret = ''; |
||
711 | |||
712 | if (empty($value)) { |
||
713 | return $ret; |
||
714 | } |
||
715 | |||
716 | $length = strlen($value); |
||
717 | |||
718 | for ($i=0; $i < $length; $i++) { |
||
719 | $current = ord($value{$i}); |
||
720 | if (($current == 0x9) || |
||
721 | ($current == 0xA) || |
||
722 | ($current == 0xD) || |
||
723 | (($current >= 0x20) && ($current <= 0xD7FF)) || |
||
724 | (($current >= 0xE000) && ($current <= 0xFFFD)) || |
||
725 | (($current >= 0x10000) && ($current <= 0x10FFFF))) { |
||
726 | $ret .= chr($current); |
||
727 | } |
||
728 | } |
||
729 | |||
730 | return $ret; |
||
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) |
||
900 | { |
||
901 | if ($node->hasAttributes()) { |
||
902 | foreach ($node->attributes as $name => $attrEl) { |
||
903 | $element->setAttribute($name, General::sanitize($attrEl->value)); |
||
904 | } |
||
905 | } |
||
906 | |||
907 | if ($node->hasChildNodes()) { |
||
908 | foreach ($node->childNodes as $childNode) { |
||
909 | if ($childNode instanceof DOMCdataSection) { |
||
910 | $element->setValue(General::wrapInCDATA($childNode->data)); |
||
911 | } elseif ($childNode instanceof DOMText) { |
||
912 | if ($childNode->isWhitespaceInElementContent() === false) { |
||
913 | $element->setValue(General::sanitize($childNode->data)); |
||
914 | } |
||
915 | } elseif ($childNode instanceof DOMElement) { |
||
916 | self::convert($element, $childNode); |
||
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; |
||
933 | } |
||
934 | } |
||
935 |
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.