Completed
Push — master ( f043ba...d4445f )
by Dan Michael O.
01:05
created

SimpleXMLElementWrapper::asDOMElement()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/*
3
 * (c) Dan Michael O. Heggø (2013)
4
 *
5
 * QuiteSimpleXMLElement is a wrapper around SimpleXMLElement to add a quite simple
6
 * feature not present in SimpleXMLElement: inheritance of namespaces.
7
 *
8
 * My first attempt was to extend the original SimpleXMLElement class, but
9
 * unfortunately the constructor is static and cannot be overriden!
10
 *
11
 * It's easier to understand with a simple example:
12
 *
13
 *     $xml = '<root xmlns:dc="http://purl.org/dc/elements/1.1/">
14
 *         <dc:a>
15
 *           <dc:b >
16
 *             1.9
17
 *           </dc:a>
18
 *         </dc:b>
19
 *       </root>';
20
 *
21
 *     $root = new SimpleXMLElement($xml);
22
 *     $root->registerXPathNamespace('d', 'http://purl.org/dc/elements/1.1/');
23
 *     $a = $root->xpath('d:a');
24
 *     $a[0]->registerXPathNamespace('d', 'http://purl.org/dc/elements/1.1/');
25
 *     $b = $a[0]->xpath('d:b');
26
 *     echo trim((string)$b[0]);
27
 *
28
 * Since namespaces are not inherited, we have to register them over and over again.
29
 * Using QuiteSimpleXMLElement instead;
30
 *
31
 *     $root = new QuiteSimpleXMLElement($xml);
32
 *     $root->registerXPathNamespace('d', 'http://purl.org/dc/elements/1.1/');
33
 *     $a = $root->xpath('d:a');
34
 *     $b = $a->xpath('d:b');
35
 *     echo trim((string)$b[0]);
36
 *
37
 * And while we're at it, we can add a few convenience methods.
38
 *
39
 * SimpleXmlElement reference:
40
 * https://github.com/draffter/FollowFunctionPHP/blob/master/_php/SimpleXML.php
41
 */
42
43
namespace Danmichaelo\QuiteSimpleXMLElement;
44
45
use Exception;
46
use InvalidArgumentException;
47
use SimpleXMLElement;
48
49
abstract class SimpleXMLElementWrapper
50
{
51
    /**
52
     * @var array
53
     */
54
    public $namespaces;
55
56
    /**
57
     * @var SimpleXMLElement
58
     */
59
    public $el;
60
61
    /**
62
     * QuiteSimpleXMLElement constructor.
63
     *
64
     * @param string|SimpleXMLElement|QuiteSimpleXMLElement    $elem
65
     * @param QuiteSimpleXMLElement                            $inherit_from
66
     * @throws InvalidXMLException
67
     * @throws InvalidArgumentException
68
     */
69
    public function __construct($elem, QuiteSimpleXMLElement $inherit_from = null)
70
    {
71
        $this->namespaces = [];
72
        $this->el = $this->getSimpleXMLElement($elem);
73
74
        if (is_null($this->el)) {
75
            throw new InvalidArgumentException('QuiteSimpleXMLElement expects a string or a QuiteSimpleXMLElement/SimpleXMLElement object.');
76
        }
77
78
        if (is_null($inherit_from)) {
79
            $this->namespaces = $this->el->getNamespaces(true);
80
        } else {
81
            foreach ($inherit_from->namespaces as $prefix => $uri) {
82
                $this->registerXPathNamespace($prefix, $uri);
83
            }
84
        }
85
    }
86
87
    /**
88
     * Internal helper method to get a SimpleXMLElement from either a string
89
     * or a SimpleXMLElement/QuiteSimpleXMLElement object.
90
     *
91
     * @param string|SimpleXMLElement|QuiteSimpleXMLElement $elem
92
     * @return SimpleXMLElement
93
     * @throws InvalidXMLException
94
     * @throws InvalidArgumentException
95
     */
96
    private function getSimpleXMLElement($elem)
97
    {
98
        if (gettype($elem) == 'string') {
99
            return $this->initFromString($elem);
100
        }
101
102
        if (gettype($elem) == 'object') {
103
            return $this->initFromObject($elem);
104
        }
105
    }
106
107
    /**
108
     * Internal helper method to parse content from string.
109
     *
110
     * @param string $content
111
     * @return SimpleXMLElement
112
     */
113
    private function initFromString($content)
114
    {
115
        try {
116
            return new SimpleXMLElement($content);
117
        } catch (Exception $e) {
118
            throw new InvalidXMLException('Invalid XML encountered: ' . $content);
119
        }
120
    }
121
122
    /**
123
     * Internal helper method to parse content from string.
124
     *
125
     * @param QuiteSimpleXMLElement|SimpleXMLElement $elem
126
     * @return SimpleXMLElement
127
     */
128
    private function initFromObject($elem)
129
    {
130
        switch (get_class($elem)) {
131
            case 'Danmichaelo\QuiteSimpleXMLElement\QuiteSimpleXMLElement':
132
                return $elem->el();
133
            case 'SimpleXMLElement':
134
                return $elem;
135
        }
136
    }
137
138
    /**
139
     * Register a new xpath namespace.
140
     *
141
     * @param string $prefix
142
     * @param string $uri
143
     */
144
    public function registerXPathNamespace($prefix, $uri)
145
    {
146
        $this->el->registerXPathNamespace($prefix, $uri);
147
        $this->namespaces[$prefix] = $uri;
148
    }
149
150
    /**
151
     * Register an array of new xpath namespaces.
152
     *
153
     * @param array $namespaces
154
     */
155
    public function registerXPathNamespaces($namespaces)
156
    {
157
        // Convenience method to add multiple namespaces at once
158
        foreach ($namespaces as $prefix => $uri) {
159
            $this->registerXPathNamespace($prefix, $uri);
160
        }
161
    }
162
163
    /**
164
     * Get the wrapped SimpleXMLElement object.
165
     *
166
     * @return SimpleXMLElement
167
     */
168
    public function el()
169
    {
170
        return $this->el;
171
    }
172
173
    /**
174
     * Returns the *untrimmed* text content of the node.
175
     * @return string
176
     */
177
    public function __toString()
178
    {
179
        return (string) $this->el;
180
    }
181
182
    /**
183
     * Returns the child elements.
184
     *
185
     * Note: By default, only children without namespace will be returned. You can
186
     * specify a namespace prefix to get children with that namespace prefix.
187
     *
188
     * Tip: You could also use `xpath('child::node()')`.
189
     *
190
     * @param null $ns
191
     * @return QuiteSimpleXMLElement[]
192
     */
193
    public function children($ns=null)
194
    {
195
        $ch = is_null($ns)
196
            ? $this->el->children()
197
            : $this->el->children($this->namespaces[$ns]);
198
199
        $o = [];
200
        foreach ($ch as $c) {
201
            $o[] = new static($c, $this);
0 ignored issues
show
Documentation introduced by
$this is of type this<Danmichaelo\QuiteSi...impleXMLElementWrapper>, but the function expects a null|object<Danmichaelo\...\QuiteSimpleXMLElement>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
202
        }
203
204
        return $o;
205
    }
206
207
    /**
208
     * Returns the number of child elements.
209
     *
210
     * Note: By default, only children without namespace will be counted. You can
211
     * specify a namespace prefix to count children with that namespace prefix.
212
     *
213
     * @param null $ns
214
     * @return int
215
     */
216
    public function count($ns = null)
217
    {
218
        return count($this->children($ns));
219
    }
220
221
    /**
222
     * Returns and element's attributes.
223
     *
224
     * @return SimpleXMLElement a `SimpleXMLElement` object that can be
225
     * iterated over to loop through the attributes on the tag.
226
     */
227
    public function attributes()
228
    {
229
        return $this->el->attributes();
230
    }
231
232
    /**
233
     * Returns the XML as text.
234
     *
235
     * @return string
236
     */
237
    public function asXML()
238
    {
239
        return $this->el->asXML();
240
    }
241
242
    /**
243
     * Get the element name.
244
     *
245
     * @return string
246
     */
247
    public function getName()
248
    {
249
        return $this->el->getName();
250
    }
251
252
    /**
253
     * Return a namespace array.
254
     *
255
     * @return array
256
     */
257
    public function getNamespaces()
258
    {
259
        return $this->namespaces;
260
    }
261
262
    /**
263
     * Set the node value.
264
     *
265
     * @param string $value
266
     */
267
    public function setValue($value)
268
    {
269
        $this->el[0] = $value;
270
    }
271
272
    /**
273
     * Return the current object as DOMElement.
274
     *
275
     * @return \DOMElement
276
     */
277
    public function asDOMElement()
278
    {
279
        return dom_import_simplexml($this->el);
280
    }
281
282
    /**
283
     * Replaces the current node. Thanks to @hakre
284
     * <http://stackoverflow.com/questions/17661167/how-to-replace-xml-node-with-simplexmlelement-php>.
285
     *
286
     * @param QuiteSimpleXMLElement $element
287
     */
288
    public function replace(QuiteSimpleXMLElement $element)
289
    {
290
        $oldNode = $this->asDOMElement();
291
        $newNode = $oldNode->ownerDocument->importNode(
292
            $element->asDOMElement(),
293
            true
294
        );
295
        $oldNode->parentNode->replaceChild($newNode, $oldNode);
296
    }
297
298
    /**
299
     * Static helper method to make initialization easier.
300
     *
301
     * @param       $input
302
     * @param array $ns
303
     * @return QuiteSimpleXMLElement
304
     */
305
    public static function make($input, $ns = [])
306
    {
307
        $elem = new static($input);
308
        $elem->registerXPathNamespaces($ns);
309
310
        return $elem;
311
    }
312
}
313