GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — 2.x ( 10ff98...11a5f0 )
by Patrick D
02:36 queued 12s
created

src/DOMDoc.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php namespace BetterDOMDocument;
2
3
use Symfony\Component\CssSelector\CssSelectorConverter;
4
5
/**
6
 * Highwire Better DOM Document
7
 *
8
 * Copyright (c) 2010-2011 Board of Trustees, Leland Stanford Jr. University
9
 * This software is open-source licensed under the GNU Public License Version 2 or later
10
 */
11
class DOMDoc extends \DOMDocument {
12
13
  private $auto_ns = FALSE;
14
  public  $ns = array();
15
  public  $default_ns = FALSE;
16
  public  $error_checking = 'strict'; // Can be 'strict', 'warning', 'none' / FALSE
17
  
18
  /**
19
   * Create a new DOMDoc
20
   *
21
   * @param mixed $xml
22
   *  $xml can either be an XML string, a DOMDocument, or a DOMElement. 
23
   *  You can also pass FALSE or NULL (or omit it) and load XML later using loadXML or loadHTML
24
   * 
25
   * @param mixed $auto_register_namespaces 
26
   *  Auto-register namespaces. All namespaces in the root element will be registered for use in xpath queries.
27
   *  Namespaces that are not declared in the root element will not be auto-registered
28
   *  Defaults to TRUE (Meaning it will auto register all auxiliary namespaces but not the default namespace).
29
   *  Pass a prefix string to automatically register the default namespace.
30
   *  Pass FALSE to disable auto-namespace registeration
31
   * 
32
   * @param bool $error_checking
33
   *  Can be 'strict', 'warning', or 'none. Defaults to 'strict'.
34
   *  'none' supresses all errors
35
   *  'warning' is the default behavior in DOMDocument
36
   *  'strict' corresponds to DOMDocument strictErrorChecking TRUE
37
   */
38 23
  public function __construct($xml = FALSE, $auto_register_namespaces = TRUE, $error_checking = 'strict') {
39 23
    parent::__construct();
40
41 23
    $this->setErrorChecking($error_checking);
42
    
43 23
    if(is_object($xml)){
44 16
      $class = get_class($xml);
45 16
      if ($class == 'DOMElement') {
46 8
        $this->appendChild($this->importNode($xml, true));
47
      }
48 16
      else if ($class == 'DOMDocument') {
49 16
        if ($xml->documentElement) {
50 16
          $this->appendChild($this->importNode($xml->documentElement, true));
51
        }
52
      }
53
      else if ($class == 'BetterDOMDocument\DOMDoc') {
54
        if ($xml->documentElement) {
55
          $this->appendChild($this->importNode($xml->documentElement, true));
56
        }
57 16
        $this->ns = $xml->ns;
58
      }
59
    }
60 17
    else if (is_string($xml) && !empty($xml)) {
61 14
      if ($this->error_checking == 'none') {
62
        @$this->loadXML($xml, LIBXML_COMPACT);
63
      }
64 14
      else if (!$this->loadXML($xml, LIBXML_COMPACT)) {
65
        trigger_error('BetterDOMDocument\DOMDoc: Could not load: ' . htmlspecialchars($xml), E_USER_WARNING);
66
      }
67
    }
68
69 23
    if ($auto_register_namespaces) {
70 23
      $this->AutoRegisterNamespace($auto_register_namespaces);
71
    }
72 23
  }
73
74
  /**
75
   * Register a namespace to be used in xpath queries
76
   *
77
   * @param string $prefix
78
   *  Namespace prefix to register
79
   *
80
   * @param string $url
81
   *  Connonical URL for this namespace prefix
82
   */
83 13
  public function registerNamespace($prefix, $url) {
84 13
    $this->ns[$prefix] = $url;
85 13
  }
86
87
  /**
88
   * Get the list of registered namespaces as an array
89
   */
90 7
  public function getNamespaces() {
91 7
    return $this->ns;
92
  }
93
94
  /**
95
   * Given a namespace URL, get the prefix
96
   * 
97
   * @param string $url
98
   *  Connonical URL for this namespace prefix
99
   * 
100
   * @return string|false
101
   *  The namespace prefix or FALSE if there is no namespace with that URL
102
   */
103 1
  public function lookupPrefix($url) {
104 1
    return array_search($url, $this->ns);
105
  }
106
107
  /**
108
   * Given a namespace prefix, get the URL
109
   * 
110
   * @param string $prefix
111
   *  namespace prefix
112
   * 
113
   * return string|false
114
   *  The namespace URL or FALSE if there is no namespace with that prefix
115
   */
116 1
  public function lookupURL($prefix) {
117 1
    if (isset($this->ns[$prefix])) {
118 1
      return $this->ns[$prefix];
119
    }
120
    else {
121
      return FALSE;
122
    }
123
  }
124
125
  /**
126
   * Given an xpath, get a list of nodes.
127
   * 
128
   * @param string $xpath
129
   *  xpath to be used for query
130
   * 
131
   * @param mixed $context
132
   *  $context can either be an xpath string, or a DOMElement
133
   *  Provides context for the xpath query
134
   * 
135
   * @return DOMList|false
136
   *  A DOMList object, which is very similar to a DOMNodeList, but with better iterabilility.
137
   */
138 18
  public function xpath($xpath, $context = NULL) {
139 18
    $this->createContext($context, 'xpath', FALSE);
140
141 18
    if ($context === FALSE) {
142
      return FALSE;
143
    }
144
    
145 18
    $xob = new \DOMXPath($this);
146
147
    // Register the namespaces
148 18
    foreach ($this->ns as $namespace => $url) {
149 9
      $xob->registerNamespace($namespace, $url);
150
    }
151
152 18
    if ($context) {
153 3
      $result = $xob->query($xpath, $context);
154
    }
155
    else {
156 18
      $result = $xob->query($xpath);
157
    }
158
159 18
    if ($result) {
160 18
      return new DOMList($result, $this);
161
    }
162
    else {
163
      return FALSE;
164
    }
165
  }
166
167
168
  /**
169
   * Given an xpath, get a single node (first one found)
170
   * 
171
   * @param string $xpath
172
   *  xpath to be used for query
173
   * 
174
   * @param mixed $context
175
   *  $context can either be an xpath string, or a DOMElement
176
   *  Provides context for the xpath query
177
   * 
178
   * @return mixed
179
   *  The first node found by the xpath query
180
   */
181 18
  public function xpathSingle($xpath, $context = NULL) {
182 18
    $result = $this->xpath($xpath, $context);
183
    
184 18
    if (empty($result) || !count($result)) {
185 2
      return FALSE;
186
    }
187
    else {
188 17
      return $result->item(0);
189
    }
190
  }
191
192
193
  /**
194
   * Given an CSS selector, get a list of nodes.
195
   * 
196
   * @param string $css_selector
197
   *  CSS Selector to be used for query
198
   * 
199
   * @param mixed $context
200
   *  $context can either be an xpath string, or a DOMElement
201
   *  Provides context for the CSS selector
202
   * 
203
   * @return DOMList|false
204
   *  A DOMList object, which is very similar to a DOMNodeList, but with better iterabilility.
205
   */
206 1
  public function select($css_selector, $context = NULL) {
207 1
    $converter = new CssSelectorConverter();
208 1
    $xpath = $converter->toXPath($css_selector);
209
210 1
    return $this->xpath($xpath, $context);
211
  }
212
213
  /**
214
   * Given an CSS selector, get a single node.
215
   * 
216
   * @param string $css_selector
217
   *  CSS Selector to be used for query
218
   * 
219
   * @param mixed $context
220
   *  $context can either be an xpath string, or a DOMElement
221
   *  Provides context for the CSS selector
222
   * 
223
   * @return DOMList
224
   *  A DOMList object, which is very similar to a DOMNodeList, but with better iterabilility.
225
   */
226 1
  public function selectSingle($css_selector, $context = NULL) {
227 1
    $converter = new CssSelectorConverter();
228 1
    $xpath = $converter->toXPath($css_selector);
229
230 1
    return $this->xpathSingle($xpath, $context);
231
  }
232
233
  /**
234
   * Get the document (or an element) as an array
235
   *
236
   * @param string $raw
237
   *  Can be either FALSE, 'full', or 'inner'. Defaults to FALSE.
238
   *  When set to 'full' every node's full XML is also attached to the array
239
   *  When set to 'inner' every node's inner XML is attached to the array.
240
   * 
241
   * @param mixed $context 
242
   *  Optional context node. Can pass an DOMElement object or an xpath string.
243
   *  If passed, only the given node will be used when generating the array 
244
   */
245
  public function getArray($raw = FALSE, $context = NULL) {
246
    $array = false;
247
248
    $this->createContext($context, 'xpath', FALSE);
249
    
250
    if ($context) {
251
      if ($raw == 'full') {
252
        $array['#raw'] = $this->saveXML($context);
253
      }
254
      if ($raw == 'inner') {
255
        $array['#raw'] = $this->innerText($context);
256
      }
257
      if ($context->hasAttributes()) {
258
        foreach ($context->attributes as $attr) {
259
          $array['@'.$attr->nodeName] = $attr->nodeValue;
260
        }
261
      }
262
  
263
      if ($context->hasChildNodes()) {
264
        if ($context->childNodes->length == 1 && $context->firstChild->nodeType == XML_TEXT_NODE) {
265
          $array['#text'] = $context->firstChild->nodeValue;
266
        }
267
        else {
268
          foreach ($context->childNodes as $childNode) {
269
            if ($childNode->nodeType == XML_ELEMENT_NODE) {
270
              $array[$childNode->nodeName][] = $this->getArray($raw, $childNode);
271
            }
272
            elseif ($childNode->nodeType == XML_CDATA_SECTION_NODE) {
273
              $array['#text'] = $childNode->textContent;
274
            }
275
          }
276
        }
277
      }
278
    }
279
    // Else no node was passed, which means we are processing the entire domDocument
280
    else {
281
      foreach ($this->childNodes as $childNode) {
282
        if ($childNode->nodeType == XML_ELEMENT_NODE) {
283
          $array[$childNode->nodeName][] = $this->getArray($raw, $childNode);
284
        }
285
      }
286
    }
287
288
    return $array;
289
  }
290
  
291
  /**
292
   * Get the inner text of an element
293
   * 
294
   * @param mixed $context 
295
   *  Optional context node. Can pass an DOMElement object or an xpath string.
296
   */
297
  public function innerText($context = NULL) {
298
    $this->createContext($context, 'xpath');
299
    
300
    $pattern = "/<".preg_quote($context->nodeName)."\b[^>]*>(.*)<\/".preg_quote($context->nodeName).">/s";
301
    $matches = array();
302
    if (preg_match($pattern, $this->saveXML($context), $matches)) {
303
      return $matches[1];
304
    }
305
    else {
306
      return '';
307
    }
308
  }
309
310
  /**
311
   * Create an DOMElement from XML and attach it to the DOMDocument
312
   * 
313
   * Note that this does not place it anywhere in the dom tree, it merely imports it.
314
   * 
315
   * @param string $xml 
316
   *  XML string to import
317
   */
318 5
  public function createElementFromXML($xml) {
319
    
320
    // To make thing easy and make sure namespaces work properly, we add the root namespace delcarations if it is not declared
321 5
    $namespaces = $this->ns;
322 5
    $xml = preg_replace_callback('/<[^\?^!].+?>/s', function($root_match) use ($namespaces) {
323 5
      preg_match('/<([^ <>]+)[\d\s]?.*?>/s', $root_match[0], $root_tag);
324 5
      $new_root = $root_tag[1];
325 5
      if (strpos($new_root, ':')) {
326
        $parts = explode(':', $new_root);
327
        $prefix = $parts[0]; 
328
        if (isset($namespaces[$prefix])) {
329
          if (!strpos($root_match[0], "xmlns:$prefix")) {
330
            $new_root .= " xmlns:$prefix='" . $namespaces[$prefix] . "'";             
331
          }
332
        }
333
      }
334 5
      return str_replace($root_tag[1], $new_root, $root_match[0]);
335 5
    }, $xml, 1);
336
337 5
    $dom = new DOMDoc($xml, $this->auto_ns);
338 5
    if (!$dom->documentElement) {
339
      trigger_error('BetterDomDocument\DOMDoc Error: Invalid XML: ' . $xml);
340
    }
341 5
    $element = $dom->documentElement;
342
    
343
    // Merge the namespaces
344 5
    foreach ($dom->getNamespaces() as $prefix => $url) {
345
      $this->registerNamespace($prefix, $url);
346
    }
347
    
348 5
    return $this->importNode($element, true);
349
  }
350
351
  /**
352
   * Append a child to the context node, make it the last child
353
   * 
354
   * @param mixed $newnode
355
   *  $newnode can either be an XML string, a DOMDocument, or a DOMElement. 
356
   * 
357
   * @param mixed $context
358
   *  $context can either be an xpath string, or a DOMElement
359
   *  Omiting $context results in using the root document element as the context
360
   * 
361
   * @return DOMElement|false
362
   *  The $newnode, properly attached to DOMDocument. If you passed $newnode as a DOMElement
363
   *  then you should replace your DOMElement with the returned one.
364
   */
365 1
  public function append($newnode, $context = NULL) {
366 1
    $this->createContext($newnode, 'xml');
367 1
    $this->createContext($context, 'xpath');
368
    
369 1
    if (!$context || !$newnode) {
370
      return FALSE;
371
    }
372
  
373 1 View Code Duplication
    if ($newnode->ownerDocument === $this) {
374 1
      $appendnode = $newnode;
375
    }
376
    else {
377
      $appendnode = $this->importNode($newnode, true);
378
    }
379
380 1
    return $context->appendChild($appendnode);
381
  }
382
  
383
  /**
384
   * Append a child to the context node, make it the first child
385
   * 
386
   * @param mixed $newnode
387
   *  $newnode can either be an XML string, a DOMDocument, or a DOMElement. 
388
   * 
389
   * @param mixed $context
390
   *  $context can either be an xpath string, or a DOMElement
391
   *  Omiting $context results in using the root document element as the context
392
   *
393
   * @return DOMElement|false
394
   *  The $newnode, properly attached to DOMDocument. If you passed $newnode as a DOMElement
395
   *  then you should replace your DOMElement with the returned one.
396
   */
397 1 View Code Duplication
  public function prepend($newnode, $context = NULL) {
398 1
    $this->createContext($newnode, 'xml');
399 1
    $this->createContext($context, 'xpath');
400
    
401 1
    if (!$context || !$newnode) {
402
      return FALSE;
403
    }
404
    
405 1
    return $context->insertBefore($newnode, $context->firstChild);
406
  }
407
408
  /**
409
   * Prepend a sibling to the context node, put it just before the context node
410
   * 
411
   * @param mixed $newnode
412
   *  $newnode can either be an XML string, a DOMDocument, or a DOMElement. 
413
   * 
414
   * @param mixed $context
415
   *  $context can either be an xpath string, or a DOMElement
416
   *  Omiting $context results in using the root document element as the context 
417
   * 
418
   * @return DOMElement|false
419
   *  The $newnode, properly attached to DOMDocument. If you passed $newnode as a DOMElement
420
   *  then you should replace your DOMElement with the returned one.
421
   */
422 1 View Code Duplication
  public function prependSibling($newnode, $context = NULL) {
423 1
    $this->createContext($newnode, 'xml');
424 1
    $this->createContext($context, 'xpath');
425
    
426 1
    if (!$context || !$newnode) {
427
      return FALSE;
428
    }
429
    
430 1
    return $context->parentNode->insertBefore($newnode, $context);
431
  }
432
  
433
  /**
434
   * Append a sibling to the context node, put it just after the context node
435
   * 
436
   * @param mixed $newnode
437
   *  $newnode can either be an XML string, a DOMDocument, or a DOMElement. 
438
   * 
439
   * @param mixed $context
440
   *  $context can either be an xpath string, or a DOMElement
441
   *  Omiting $context results in using the root document element as the context 
442
   * 
443
   * @return DOMElement|false
444
   *  The $newnode, properly attached to DOMDocument. If you passed $newnode as a DOMElement
445
   *  then you should replace your DOMElement with the returned one.
446
   */
447 1
  public function appendSibling($newnode, $context) {
448 1
    $this->createContext($newnode, 'xml');
449 1
    $this->createContext($context, 'xpath');
450
    
451 1
    if (!$context){
452
      return FALSE;
453
    }
454
    
455 1
    if ($context->nextSibling) { 
456
      // $context has an immediate sibling : insert newnode before this one 
457 1
      return $context->parentNode->insertBefore($newnode, $context->nextSibling); 
458
    }
459
    else { 
460
      // $context has no sibling next to it : insert newnode as last child of it's parent 
461 View Code Duplication
      if ($newnode->ownerDocument === $this) {
462
        $appendnode = $newnode;
463
      }
464
      else {
465
        $appendnode = $this->importNode($newnode, true);
466
      }
467
      return $context->parentNode->appendChild($appendnode); 
468
    }
469
  }
470
  
471
  /**
472
   * Given an xpath or DOMElement, return a new DOMDoc.
473
   * 
474
   * @param mixed $node
475
   *  $node can either be an xpath string or a DOMElement. 
476
   * 
477
   * @return DOMDoc
478
   *  A new DOMDoc created from the xpath or DOMElement
479
   */
480 7
  public function extract($node, $auto_register_namespaces = TRUE, $error_checking = 'none') {
481 7
    $this->createContext($node, 'xpath');
482 7
    $dom = new DOMDoc($node, $auto_register_namespaces, $error_checking);
483 7
    $dom->ns = $this->ns;
484 7
    return $dom;
485
  }
486
  
487
  /**
488
   * Given a pair of nodes, replace the first with the second
489
   * 
490
   * @param mixed $node
491
   *  Node to be replaced. Can either be an xpath string or a DOMDocument (or even a DOMNode).
492
   * 
493
   * @param mixed $replace
494
   *  Replace $node with $replace. Replace can be an XML string, or a DOMNode
495
   * 
496
   * @return mixed
497
   *   The overwritten / replaced node.
498
   */
499 2
  public function replace($node, $replace) {
500 2
    $this->createContext($node, 'xpath');
501 2
    $this->createContext($replace, 'xml');
502
    
503 2
    if (!$node || !$replace) {
504
      return FALSE;
505
    }
506
        
507 2
    if (!$replace->ownerDocument->documentElement->isSameNode($this->documentElement)) {
508 1
      $replace = $this->importNode($replace, true);
509
    }
510 2
    $node->parentNode->replaceChild($replace, $node);
511 2
    $node = $replace;
512 2
    return $node;
513
  }
514
515
  /**
516
   * Given a node(s), remove / delete them
517
   * 
518
   * @param mixed $node
519
   *  Can pass a DOMNode, a NodeList, DOMNodeList, an xpath string, or an array of any of these.
520
   */
521 1
  public function remove($node) {
522
    // We can't use createContext here because we want to use the entire nodeList (not just a single element)
523 1
    if (is_string($node)) {
524 1
      $node = $this->xpath($node);
525
    }
526
    
527 1
    if ($node) {
528 1
      if (is_array($node) || get_class($node) == 'BetterDOMDocument\DOMList') {
529 1
        foreach($node as $item) {
530 1
          $this->remove($item);
531
        }
532
      }
533 1
      else if (get_class($node) == 'DOMNodeList') {
534
        $this->remove(new DOMList($node, $this));
535
      }
536
      else {
537 1
        $parent = $node->parentNode;
538 1
        $parent->removeChild($node);
539
      }
540
    }
541 1
  }
542
  
543
  /**
544
   * Given an XSL string, transform the DOMDoc (or a passed context node)
545
   * 
546
   * @param string $xsl
547
   *   XSL Transormation
548
   * 
549
   * @param mixed $context
550
   *   $context can either be an xpath string, or a DOMElement. Ommiting it
551
   *   results in transforming the entire document
552
   * 
553
   * @return a new DOMDoc
554
   */
555 4
  public function tranform($xsl, $context = NULL) {
556 4
    if (!$context) {
557 2
      $doc = $this;
558
    }
559
    else {
560 2
      if (is_string($context)) {
561 1
        $context = $this->xpathSingle($context);
562
      }
563 2
      $doc = new DOMDoc($context);
564
    }
565
    
566 4
    $xslDoc = new DOMDoc($xsl);
567 4
    $xslt = new \XSLTProcessor();
568 4
    $xslt->importStylesheet($xslDoc);
569
    
570 4
    return new DOMDoc($xslt->transformToDoc($doc));
571
  }
572
573
  /**
574
   * Given a node, change it's namespace to the specified namespace in situ
575
   * 
576
   * @param mixed $node
577
   *  Node to be changed. Can either be an xpath string or a DOMElement.
578
   * 
579
   * @param mixed $prefix
580
   *   prefix for the new namespace
581
   * 
582
   * @param mixed $url
583
   *   The URL for the new namespace
584
   * 
585
   * @return mixed
586
   *   The node with the new namespace. The node will also be changed in-situ in the document as well.
587
   */
588 1
  public function changeNamespace($node, $prefix, $url) {
589 1
    $this->createContext($node, 'xpath');
590
    
591 1
    if (!$node) {
592
      return FALSE;
593
    }
594
    
595 1
    $this->registerNamespace($prefix, $url);
596
597 1
    if (get_class($node) == 'DOMElement') {
598
      $xsl = '
599
        <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
600
          <xsl:template match="*">
601 1
            <xsl:element name="' . $prefix . ':{local-name()}" namespace="' . $url . '">
602
             <xsl:copy-of select="@*"/>
603
             <xsl:apply-templates/>
604
            </xsl:element>
605
          </xsl:template>
606 1
        </xsl:stylesheet>';
607
608 1
      $transformed = $this->tranform($xsl, $node);
609 1
      return $this->replace($node, $transformed->documentElement);   
610
    }
611
    else {
612
      // @@TODO: Report the correct calling file and number
613
      throw new Exception("Changing the namespace of a " . get_class($node) . " is not supported");
614
    }
615
  }
616
617
  /**
618
   * Get a lossless HTML representation of the XML
619
   *
620
   * Transforms the document (or passed context) into a set of HTML spans.
621
   * The element name becomes the class, all other attributes become HTML5
622
   * "data-" attributes.
623
   * 
624
   * @param mixed $context
625
   *   $context can either be an xpath string, or a DOMElement. Ommiting it
626
   *   results in transforming the entire document
627
   * 
628
   * @param array $options
629
   *   Options for transforming the HTML into XML. The following options are supported:
630
   *   'xlink' => {TRUE or xpath}
631
   *     Transform xlink links into <a href> elements. If you specify 'xlink' => TRUE then 
632
   *     it will transform all elements with xlink:type = simple into a <a href> element. 
633
   *     Alternatively you may specify your own xpath for selecting which elements get transformed
634
   *     into <a href> tags. 
635
   * @return HTML string
636
   */  
637 3
  public function asHTML($context = NULL, $options = array()) {
638
    $xslSimple = '
639
      <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
640
      <xsl:template match="*">
641
        <span class="{translate(name(.),\':\',\'-\')}">
642
          <xsl:for-each select="./@*">
643
            <xsl:attribute name="data-{translate(name(.),\':\',\'-\')}">
644
              <xsl:value-of select="." />
645
            </xsl:attribute>
646
          </xsl:for-each>
647
          <xsl:apply-templates/>
648
        </span>
649
      </xsl:template>
650 3
      </xsl:stylesheet>';
651
652
    $xslOptions = '
653
      <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xlink="http://www.w3.org/1999/xlink" ||namespaces||>
654
      <xsl:template match="*">
655
        <xsl:choose>
656
          <xsl:when test="||xlink||">
657
            <a class="{translate(name(.),\':\',\'-\')}">
658
              <xsl:for-each select="./@*">
659
                <xsl:attribute name="data-{translate(name(.),\':\',\'-\')}">
660
                  <xsl:value-of select="."/>
661
                </xsl:attribute>
662
              </xsl:for-each>
663
              <xsl:attribute name="href">
664
                <xsl:value-of select="@xlink:href"/>
665
              </xsl:attribute>
666
              <xsl:apply-templates/>
667
            </a>
668
          </xsl:when>
669
          <xsl:otherwise>
670
            <span class="{translate(name(.),\':\',\'-\')}">
671
              <xsl:for-each select="./@*">
672
                <xsl:attribute name="data-{translate(name(.),\':\',\'-\')}">
673
                  <xsl:value-of select="." />
674
                </xsl:attribute>
675
              </xsl:for-each>
676
              <xsl:apply-templates/>
677
            </span>
678
          </xsl:otherwise>
679
        </xsl:choose>
680
      </xsl:template>
681 3
      </xsl:stylesheet>';
682
683 3
    if (!empty($options)) {
684
      // Add in the namespaces
685 1
      foreach ($this->getNamespaces() as $prefix => $url) {
686 1
        $namespaces = '';
687 1
        if ($prefix != 'xsl' && $prefix != 'xlink') {
688 1
          $namespaces .= 'xmlns:' . $prefix . '="' . $url. '" ';
689
        }
690 1
        $xslOptions = str_replace("||namespaces||", $namespaces, $xslOptions);
691
      }
692
693
      // Add in xlink options
694 1
      if ($options['xlink'] === TRUE) {
695 1
        $options['xlink'] = "@xlink:type = 'simple'";
696
      }
697
      else if (empty($options['xlink'])) {
698
        $options['xlink'] = "false()";
699
      }
700 1
      $xslOptions = str_replace("||xlink||", $options['xlink'], $xslOptions);
701 1
      $transformed = $this->tranform($xslOptions, $context);
702
    }
703
    else {
704 2
      $transformed = $this->tranform($xslSimple, $context);
705
    }
706
    
707 3
    return $transformed->out();
708
  }
709
710
  /**
711
   * Output the DOMDoc as an XML string
712
   * 
713
   * @param mixed $context
714
   *   $context can either be an xpath string, or a DOMElement. Ommiting it
715
   *   results in outputting the entire document
716
   * 
717
   * @return XML string
718
   */  
719 12
  public function out($context = NULL) {
720 12
    $this->createContext($context, 'xpath');
721 12
    if (!$context) {
722 2
      return '';
723
    }
724
725
    // Copy namespace prefixes
726 10
    foreach ($this->ns as $prefix => $namespace) {
727
      if (!$context->hasAttribute('xmlns:' . $prefix)) {
728
        $context->setAttribute('xmlns:' . $prefix, $namespace);
729
      }
730
    }
731
    
732
    // Check to seee if it's HTML, if it is we need to fix broken html void elements.
733 10
    if ($this->documentElement->lookupNamespaceURI(NULL) == 'http://www.w3.org/1999/xhtml' || $this->documentElement->tagName == 'html') {
734 1
      $output = $this->saveXML($context, LIBXML_NOEMPTYTAG);
735
      // The types listed are html "void" elements. 
736
      // Find any of these elements that have no child nodes and are therefore candidates for self-closing, replace them with a self-closed version. 
737 1
      $pattern = '<(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)(\b[^<]*)><\/\1>';
738 1
      return preg_replace('/' . $pattern . '/', '<$1$2/>', $output);
739
    }
740
    else {
741 9
      return $this->saveXML($context, LIBXML_NOEMPTYTAG);
742
    }
743
  }
744
745
  /**
746
   * Magic method for casting a DOMDoc as a string
747
   */ 
748 1
  public function __toString() {
749 1
    return $this->out();
750
  }
751
752 23
  public function setErrorChecking($error_checking) {
753
    // Check up error-checking
754 23
    if ($error_checking == FALSE) {
755
      $this->error_checking = 'none';
756
    }
757
    else {
758 23
      $this->error_checking = $error_checking;
759
    }
760 23
    if ($this->error_checking != 'strict') {
761 7
      $this->strictErrorChecking = FALSE;
762
    }
763 23
  }
764
765 13
  public static function loadFile($file_or_url, $auto_register_namespaces = TRUE) {
766 13
    $dom = @parent::load($file_or_url, LIBXML_COMPACT);
767 13
    if (empty($dom)) {
768
      return FALSE;
769
    }
770
771 13
    return new DOMDoc($dom, $auto_register_namespaces);
772
  }
773
774 1
  public function loadHTML($source, $options = NULL) {
775 1
    $success = parent::loadHTML($source, $options);
776 1
    $this->AutoRegisterNamespace(TRUE);
777
778 1
    return $success;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $success; (DOMDocument) is incompatible with the return type of the parent method DOMDocument::loadHTML of type boolean.

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:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
779
  }
780
781 14
  public function loadXML($source, $options = NULL) {
782 14
    $success = parent::loadXML($source, $options);
783 14
    $this->AutoRegisterNamespace(TRUE);
784
785 14
    return $success;
786
  }
787
788 23
  private function AutoRegisterNamespace($auto_register_namespaces) {
789 23
    $this->auto_ns = TRUE;
790
791
    // If it's an "XML" document, then get namespaces via xpath
792 23
    $xpath = new \DOMXPath($this);
793 23
    foreach($xpath->query('namespace::*') as $namespace) {
794 22
      if (!empty($namespace->prefix)) {
795 22
        if ($namespace->prefix != 'xml' && $namespace->nodeValue != 'http://www.w3.org/XML/1998/namespace') {
796 22
          $this->registerNamespace($namespace->prefix, $namespace->nodeValue);
797
        }
798
      }
799 View Code Duplication
      else {
800 3
        $this->default_ns = $namespace->nodeValue;
801 3
        if (is_string($auto_register_namespaces)) {
802
          $this->registerNamespace($auto_register_namespaces, $namespace->nodeValue);
803
        }
804
        // Otherwise, automatically set-up the root element tag name as the prefix for the default namespace
805
        else {
806 3
          $tagname = $this->documentElement->tagName;
807 3
          if (empty($this->ns[$tagname])) {
808 22
            $this->registerNamespace($tagname, $this->documentElement->getAttribute('xmlns'));
809
          }
810
        }
811
      }
812
    }
813
814
    // If it's an "HTML" document, we get namespaces via attributes
815 23
    if (empty($this->ns) && !empty($this->documentElement)) {
816 14
      foreach ($this->documentElement->attributes as $attr) {
817 4
        if ($attr->name == 'xmlns') {
818 1
          $this->default_ns = $attr->value;
819
          // If auto_register_namespaces is a prefix string, then we register the default namespace to that string
820 1 View Code Duplication
          if (is_string($auto_register_namespaces)) {
821
            $this->registerNamespace($auto_register_namespaces, $attr->value);
822
          }
823
          // Otherwise, automatically set-up the root element tag name as the prefix for the default namespace
824
          else {
825 1
            $tagname = $this->documentElement->tagName;
826 1
            if (empty($this->ns[$tagname])) {
827 1
              $this->registerNamespace($tagname, $attr->value);
828
            } 
829
          }
830
        }
831 3
        else if (substr($attr->name,0,6) == 'xmlns:') {
832
          $prefix = substr($attr->name,6);
833 4
          $this->registerNamespace($prefix, $attr->value); 
834
        }
835
      }
836
    }
837 23
  }
838
  
839 21
  private function createContext(&$context, $type = 'xpath', $createDocument = TRUE) {
840 21
    if (!$context && $createDocument) {
841 11
      $context = $this->documentElement;
842 11
      return;
843
    }
844
845 18
    if (!$context) {
846 18
      return FALSE;
847
    }
848
849 12
    if ($context && is_string($context)) {
850 12
      if ($type == 'xpath') {
851 12
        $context = $this->xpathSingle($context);
852 12
        return;
853
      }
854 5
      if ($type == 'xml') {
855 5
        $context = $this->createElementFromXML($context);
856 5
        return;
857
      }
858
    }
859
860 1
    if (is_object($context)) {
861 1
      if (is_a($context, 'DOMElement')) {
862 1
        return $context;
863
      }
864
      if (is_a($context, 'DOMDocument')) {
865
        return $context->documentElement;
866
      }
867
    }
868
  }
869
}
870
871
872
873