Passed
Push — master ( 4f7e5e...eeb5c0 )
by Thomas
04:51
created

DocumentFragment::appendXml()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 21
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 16
nc 10
nop 2
dl 0
loc 21
ccs 16
cts 16
cp 1
crap 7
rs 7.551
c 0
b 0
f 0
1
<?php
2
/**
3
 * FluentDOM\DOM\DocumentFragment extends PHPs DOMDocumentFragment class. It adds some namespace handling.
4
 *
5
 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
6
 * @copyright Copyright (c) 2009-2017 FluentDOM Contributors
7
 */
8
9
namespace FluentDOM\DOM {
10
11
  use FluentDOM\Utility\Namespaces;
12
13
  /**
14
   * FluentDOM\DOM\DocumentFragment extends PHPs DOMDocumentFragment class. It adds some namespace handling and
15
   * some standard interfaces for convenience.
16
   *
17
   * Be aware that a fragment is empty after it was appended.
18
   *
19
   * @property-read Document $ownerDocument
20
   * @property-read Element $firstElementChild
21
   * @property-read Element $lastElementChild
22
   */
23
  class DocumentFragment
24
    extends \DOMDocumentFragment
25
    implements
26
      \Countable,
27
      \IteratorAggregate,
28
      Node,
29
      Node\ParentNode {
30
31
    use
32
      /** @noinspection TraitsPropertiesConflictsInspection */
33
      Node\ParentNode\Properties,
34
      Node\QuerySelector\Implementation,
35
      /** @noinspection TraitsPropertiesConflictsInspection */
36
      Node\Xpath;
37
38
    /**
39
     * @var Namespaces
40
     */
41
    private $_namespaces;
42
43
    /**
44
     * Casting the fragment to string will return the text content of all nodes
45
     *
46
     * @return string
47
     */
48 1
    public function __toString(): string {
49 1
      $result = '';
50 1
      foreach ($this->childNodes as $child) {
51 1
        $result .= (string)$child;
52
      }
53 1
      return $result;
54
    }
55
56
    /**
57
     * @return int
58
     */
59 1
    public function count(): int {
60 1
      return $this->childNodes->length;
61
    }
62
63
    /**
64
     * @return \Iterator
65
     */
66 1
    public function getIterator(): \Iterator {
67 1
      return new \ArrayIterator(iterator_to_array($this->childNodes));
68
    }
69
70
    /**
71
     * Get/Set the namespace definition used for the fragment strings.
72
     *
73
     * You can use an [prefix => $namespaceURI, ...] or an element node
74
     * to set the namespaces. If the list is empty, the namespaces from
75
     * the document object will be used.
76
     *
77
     * @param NULL|array|\Traversable|\DOMElement $namespaces
78
     * @return Namespaces
79
     * @throws \InvalidArgumentException
80
     */
81 18
    public function namespaces($namespaces = NULL): Namespaces {
82 18
      if (NULL !== $namespaces || (!$this->_namespaces instanceof Namespaces)) {
83 18
        $this->_namespaces = new Namespaces();
84
      }
85 18
      if (NULL !== $namespaces) {
86 7
        if ($namespaces instanceof \DOMElement) {
87 2
          $xpath = new Xpath($namespaces->ownerDocument);
88
          /** @noinspection CallableParameterUseCaseInTypeContextInspection */
89 2
          $namespaces = $xpath('namespace::*', $namespaces);
90
        }
91 7
        if (is_array($namespaces) || $namespaces instanceof \Traversable) {
92
          /** @noinspection ForeachSourceInspection */
93 5
          foreach ($namespaces as $key => $namespaceURI) {
94 5
            if ($namespaceURI instanceof \DOMNameSpaceNode) {
95 2
              if ($namespaceURI->nodeName === 'xmlns') {
96 1
                $this->registerNamespace('#default', $namespaceURI->nodeValue);
97 2
              } elseif ($namespaceURI->localName !== 'xml') {
98 1
                $this->registerNamespace($namespaceURI->localName, $namespaceURI->nodeValue);
99
              }
100
            } else {
101 5
              $this->registerNamespace($key, $namespaceURI);
102
            }
103
          }
104 2
        } elseif (NULL !== $namespaces) {
105 2
          throw new \InvalidArgumentException(
106 2
            '$namespaces needs to be a list of namespaces or an element node to fetch the namespaces from.'
107
          );
108
        }
109
      }
110 16
      return count($this->_namespaces) > 0 ? $this->_namespaces : $this->ownerDocument->namespaces();
111
    }
112
113
    /**
114
     * Register a namespace prefix to use it in appendXml()
115
     *
116
     * @param string $prefix
117
     * @param string $namespaceURI
118
     * @throws \InvalidArgumentException
119
     */
120 6
    public function registerNamespace(string $prefix, string $namespaceURI) {
121 6
      $this->namespaces()[empty($prefix) ? '#default' : $prefix] = $namespaceURI;
122 6
    }
123
124
    /**
125
     * Append an xml to the fragment, it can use namespace prefixes defined on the fragment object.
126
     *
127
     * @param string $data
128
     * @param NULL|array|\Traversable|\DOMElement $namespaces
129
     * @return bool
130
     * @throws \InvalidArgumentException
131
     */
132 18
    public function appendXml($data, $namespaces = NULL): bool {
133 18
      $namespaces = $this->namespaces($namespaces);
134 16
      if (count($namespaces) === 0) {
135 9
        return parent::appendXML($data);
136
      }
137 7
      $fragment = '<fragment';
138 7
      foreach ($namespaces as $key => $xmlns) {
139 7
        $prefix = $key === '#default' ? '' : $key;
140 7
        $fragment .= ' '.htmlspecialchars(empty($prefix) ? 'xmlns' : 'xmlns:'.$prefix);
141 7
        $fragment .= '="'.htmlspecialchars($xmlns).'"';
142
      }
143 7
      $fragment .= '>'.$data.'</fragment>';
144 7
      $source = new Document();
145 7
      if ($source->loadXML($fragment)) {
146 6
        foreach ($source->documentElement->childNodes as $child) {
147 6
          $this->appendChild($this->ownerDocument->importNode($child, TRUE));
148
        }
149 6
        return TRUE;
150
      }
151 1
      return FALSE;
152
    }
153
154
    /**
155
     * Append an child element
156
     *
157
     * @param string $name
158
     * @param string $content
159
     * @param array $attributes
160
     * @return Element
161
     * @throws \LogicException
162
     */
163 1
    public function appendElement(string $name, $content = '', array $attributes = NULL): Element {
164 1
      $this->appendChild(
165 1
        $node = $this->ownerDocument->createElement($name, $content, $attributes)
166
      );
167 1
      return $node;
168
    }
169
170
171
    /**
172
     * Save as XML string
173
     *
174
     * @return string
175
     */
176 3
    public function saveXmlFragment(): string {
177 3
      return $this->ownerDocument->saveXML($this);
178
    }
179
  }
180
}
181
182