Completed
Push — master ( 00fe6a...ae518a )
by Lars
02:28 queued 46s
created

XmlDomParser::getElementsById()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 1
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace voku\helper;
6
7
/**
8
 * @property-read string $plaintext
9
 *                                 <p>Get dom node's plain text.</p>
10
 *
11
 * @method static XmlDomParser file_get_xml($xml, $libXMLExtraOptions = null)
12
 *                                 <p>Load XML from file.</p>
13
 * @method static XmlDomParser str_get_xml($xml, $libXMLExtraOptions = null)
14
 *                                 <p>Load XML from string.</p>
15
 */
16
class XmlDomParser extends AbstractDomParser
17
{
18
    /**
19
     * @param \DOMNode|SimpleXmlDomInterface|string $element HTML code or SimpleXmlDomInterface, \DOMNode
20
     */
21 3 View Code Duplication
    public function __construct($element = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
22
    {
23 3
        $this->document = new \DOMDocument('1.0', $this->getEncoding());
24
25
        // DOMDocument settings
26 3
        $this->document->preserveWhiteSpace = true;
27 3
        $this->document->formatOutput = true;
28
29 3
        if ($element instanceof SimpleXmlDomInterface) {
30
            $element = $element->getNode();
31
        }
32
33 3
        if ($element instanceof \DOMNode) {
34
            $domNode = $this->document->importNode($element, true);
35
36
            if ($domNode instanceof \DOMNode) {
37
                /** @noinspection UnusedFunctionResultInspection */
38
                $this->document->appendChild($domNode);
39
            }
40
41
            return;
42
        }
43
44 3
        if ($element !== null) {
45
            $this->loadXml($element);
46
        }
47 3
    }
48
49
    /**
50
     * @param string $name
51
     * @param array  $arguments
52
     *
53
     * @throws \BadMethodCallException
54
     * @throws \RuntimeException
55
     *
56
     * @return XmlDomParser
57
     */
58 3 View Code Duplication
    public static function __callStatic($name, $arguments)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
59
    {
60 3
        $arguments0 = $arguments[0] ?? '';
61
62 3
        $arguments1 = $arguments[1] ?? null;
63
64 3
        if ($name === 'str_get_xml') {
65 1
            $parser = new static();
66
67 1
            return $parser->loadXml($arguments0, $arguments1);
68
        }
69
70 2
        if ($name === 'file_get_xml') {
71 2
            $parser = new static();
72
73 2
            return $parser->loadXmlFile($arguments0, $arguments1);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $parser->loadXmlF...guments0, $arguments1); (self) is incompatible with the return type declared by the abstract method voku\helper\AbstractDomParser::__callStatic of type voku\helper\AbstractDomParser.

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...
74
        }
75
76
        throw new \BadMethodCallException('Method does not exist');
77
    }
78
79
    /** @noinspection MagicMethodsValidityInspection */
80
81
    /**
82
     * @param string $name
83
     *
84
     * @return string|null
85
     */
86
    public function __get($name)
87
    {
88
        $name = \strtolower($name);
89
90
        if ($name === 'plaintext') {
91
            return $this->text();
92
        }
93
94
        return null;
95
    }
96
97
    /**
98
     * @return string
99
     */
100 2
    public function __toString()
101
    {
102 2
        return $this->xml(false, false, true, 0);
103
    }
104
105
    /**
106
     * Create DOMDocument from XML.
107
     *
108
     * @param string   $xml
109
     * @param int|null $libXMLExtraOptions
110
     *
111
     * @return \DOMDocument
112
     */
113 3
    protected function createDOMDocument(string $xml, $libXMLExtraOptions = null): \DOMDocument
114
    {
115
        // set error level
116 3
        $internalErrors = \libxml_use_internal_errors(true);
117 3
        $disableEntityLoader = \libxml_disable_entity_loader(true);
118 3
        \libxml_clear_errors();
119
120 3
        $optionsXml = \LIBXML_DTDLOAD | \LIBXML_DTDATTR | \LIBXML_NONET;
121
122 3
        if (\defined('LIBXML_BIGLINES')) {
123 3
            $optionsXml |= \LIBXML_BIGLINES;
124
        }
125
126 3
        if (\defined('LIBXML_COMPACT')) {
127 3
            $optionsXml |= \LIBXML_COMPACT;
128
        }
129
130 3
        if ($libXMLExtraOptions !== null) {
131
            $optionsXml |= $libXMLExtraOptions;
132
        }
133
134 3
        $xml = self::replaceToPreserveHtmlEntities($xml);
135
136 3
        $documentFound = false;
137 3
        $sxe = \simplexml_load_string($xml, \SimpleXMLElement::class, $optionsXml);
138 3 View Code Duplication
        if ($sxe !== false && \count(\libxml_get_errors()) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
139 3
            $domElementTmp = \dom_import_simplexml($sxe);
140
            if (
141 3
                $domElementTmp
142
                &&
143 3
                $domElementTmp->ownerDocument
144
            ) {
145 3
                $documentFound = true;
146 3
                $this->document = $domElementTmp->ownerDocument;
147
            }
148
        }
149
150 3 View Code Duplication
        if ($documentFound === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
151
152
            // UTF-8 hack: http://php.net/manual/en/domdocument.loadhtml.php#95251
153
            $xmlHackUsed = false;
154
            /** @noinspection StringFragmentMisplacedInspection */
155
            if (\stripos('<?xml', $xml) !== 0) {
156
                $xmlHackUsed = true;
157
                $xml = '<?xml encoding="' . $this->getEncoding() . '" ?>' . $xml;
158
            }
159
160
            $this->document->loadXML($xml, $optionsXml);
161
162
            // remove the "xml-encoding" hack
163
            if ($xmlHackUsed) {
164
                foreach ($this->document->childNodes as $child) {
165
                    if ($child->nodeType === \XML_PI_NODE) {
166
                        /** @noinspection UnusedFunctionResultInspection */
167
                        $this->document->removeChild($child);
168
169
                        break;
170
                    }
171
                }
172
            }
173
        }
174
175
        // set encoding
176 3
        $this->document->encoding = $this->getEncoding();
177
178
        // restore lib-xml settings
179 3
        \libxml_clear_errors();
180 3
        \libxml_use_internal_errors($internalErrors);
181 3
        \libxml_disable_entity_loader($disableEntityLoader);
182
183 3
        return $this->document;
184
    }
185
186
    /**
187
     * Find list of nodes with a CSS selector.
188
     *
189
     * @param string   $selector
190
     * @param int|null $idx
191
     *
192
     * @return SimpleXmlDomInterface|SimpleXmlDomInterface[]|SimpleXmlDomNodeInterface<SimpleXmlDomInterface>
0 ignored issues
show
Documentation introduced by
The doc-type SimpleXmlDomInterface|Si...<SimpleXmlDomInterface> could not be parsed: Expected "|" or "end of type", but got "<" at position 71. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
193
     */
194 1 View Code Duplication
    public function find(string $selector, $idx = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
195
    {
196 1
        $xPathQuery = SelectorConverter::toXPath($selector);
197
198 1
        $xPath = new \DOMXPath($this->document);
199 1
        $nodesList = $xPath->query($xPathQuery);
200 1
        $elements = new SimpleXmlDomNode();
201
202 1
        if ($nodesList) {
203 1
            foreach ($nodesList as $node) {
204 1
                $elements[] = new SimpleXmlDom($node);
205
            }
206
        }
207
208
        // return all elements
209 1
        if ($idx === null) {
210 1
            if (\count($elements) === 0) {
211 1
                return new SimpleXmlDomNodeBlank();
212
            }
213
214 1
            return $elements;
215
        }
216
217
        // handle negative values
218 1
        if ($idx < 0) {
219
            $idx = \count($elements) + $idx;
220
        }
221
222
        // return one element
223 1
        return $elements[$idx] ?? new SimpleXmlDomBlank();
224
    }
225
226
    /**
227
     * Find nodes with a CSS selector.
228
     *
229
     * @param string $selector
230
     *
231
     * @return SimpleXmlDomInterface[]|SimpleXmlDomNodeInterface<SimpleXmlDomInterface>
0 ignored issues
show
Documentation introduced by
The doc-type SimpleXmlDomInterface[]|...<SimpleXmlDomInterface> could not be parsed: Expected "|" or "end of type", but got "<" at position 49. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
232
     */
233 1
    public function findMulti(string $selector): SimpleXmlDomNodeInterface
234
    {
235 1
        return $this->find($selector, null);
236
    }
237
238
    /**
239
     * Find nodes with a CSS selector or false, if no element is found.
240
     *
241
     * @param string $selector
242
     *
243
     * @return false|SimpleXmlDomInterface[]|SimpleXmlDomNodeInterface<SimpleXmlDomInterface>
0 ignored issues
show
Documentation introduced by
The doc-type false|SimpleXmlDomInterf...<SimpleXmlDomInterface> could not be parsed: Expected "|" or "end of type", but got "<" at position 55. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
244
     */
245 1
    public function findMultiOrFalse(string $selector)
246
    {
247 1
        $return = $this->find($selector, null);
248
249 1
        if ($return instanceof SimpleXmlDomNodeBlank) {
250 1
            return false;
251
        }
252
253
        return $return;
254
    }
255
256
    /**
257
     * Find one node with a CSS selector.
258
     *
259
     * @param string $selector
260
     *
261
     * @return SimpleXmlDomInterface
262
     */
263 1
    public function findOne(string $selector): SimpleXmlDomInterface
264
    {
265 1
        return $this->find($selector, 0);
266
    }
267
268
    /**
269
     * Find one node with a CSS selector or false, if no element is found.
270
     *
271
     * @param string $selector
272
     *
273
     * @return false|SimpleXmlDomInterface
274
     */
275 1
    public function findOneOrFalse(string $selector)
276
    {
277 1
        $return = $this->find($selector, 0);
278
279 1
        if ($return instanceof SimpleXmlDomBlank) {
280 1
            return false;
281
        }
282
283
        return $return;
284
    }
285
286
    /**
287
     * @param string $content
288
     * @param bool   $multiDecodeNewHtmlEntity
289
     *
290
     * @return string
291
     */
292
    public function fixHtmlOutput(string $content, bool $multiDecodeNewHtmlEntity = false): string
293
    {
294
        $content = $this->decodeHtmlEntity($content, $multiDecodeNewHtmlEntity);
295
296
        return self::putReplacedBackToPreserveHtmlEntities($content);
297
    }
298
299
    /**
300
     * Return elements by ".class".
301
     *
302
     * @param string $class
303
     *
304
     * @return SimpleXmlDomInterface[]|SimpleXmlDomNodeInterface<SimpleXmlDomInterface>
0 ignored issues
show
Documentation introduced by
The doc-type SimpleXmlDomInterface[]|...<SimpleXmlDomInterface> could not be parsed: Expected "|" or "end of type", but got "<" at position 49. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
305
     */
306
    public function getElementByClass(string $class): SimpleXmlDomNodeInterface
307
    {
308
        return $this->findMulti(".${class}");
309
    }
310
311
    /**
312
     * Return element by #id.
313
     *
314
     * @param string $id
315
     *
316
     * @return SimpleXmlDomInterface
317
     */
318
    public function getElementById(string $id): SimpleXmlDomInterface
319
    {
320
        return $this->findOne("#${id}");
321
    }
322
323
    /**
324
     * Return element by tag name.
325
     *
326
     * @param string $name
327
     *
328
     * @return SimpleXmlDomInterface
329
     */
330
    public function getElementByTagName(string $name): SimpleXmlDomInterface
331
    {
332
        $node = $this->document->getElementsByTagName($name)->item(0);
333
334
        if ($node === null) {
335
            return new SimpleXmlDomBlank();
336
        }
337
338
        return new SimpleXmlDom($node);
339
    }
340
341
    /**
342
     * Returns elements by "#id".
343
     *
344
     * @param string   $id
345
     * @param int|null $idx
346
     *
347
     * @return SimpleXmlDomInterface|SimpleXmlDomInterface[]|SimpleXmlDomNodeInterface<SimpleXmlDomInterface>
0 ignored issues
show
Documentation introduced by
The doc-type SimpleXmlDomInterface|Si...<SimpleXmlDomInterface> could not be parsed: Expected "|" or "end of type", but got "<" at position 71. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
348
     */
349
    public function getElementsById(string $id, $idx = null)
350
    {
351
        return $this->find("#${id}", $idx);
352
    }
353
354
    /**
355
     * Returns elements by tag name.
356
     *
357
     * @param string   $name
358
     * @param int|null $idx
359
     *
360
     * @return SimpleXmlDomInterface|SimpleXmlDomInterface[]|SimpleXmlDomNodeInterface<SimpleXmlDomInterface>
0 ignored issues
show
Documentation introduced by
The doc-type SimpleXmlDomInterface|Si...<SimpleXmlDomInterface> could not be parsed: Expected "|" or "end of type", but got "<" at position 71. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
361
     */
362 View Code Duplication
    public function getElementsByTagName(string $name, $idx = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
363
    {
364
        $nodesList = $this->document->getElementsByTagName($name);
365
366
        $elements = new SimpleXmlDomNode();
367
368
        foreach ($nodesList as $node) {
369
            $elements[] = new SimpleXmlDom($node);
370
        }
371
372
        // return all elements
373
        if ($idx === null) {
374
            if (\count($elements) === 0) {
375
                return new SimpleXmlDomNodeBlank();
376
            }
377
378
            return $elements;
379
        }
380
381
        // handle negative values
382
        if ($idx < 0) {
383
            $idx = \count($elements) + $idx;
384
        }
385
386
        // return one element
387
        return $elements[$idx] ?? new SimpleXmlDomNodeBlank();
388
    }
389
390
    /**
391
     * Get dom node's outer html.
392
     *
393
     * @param bool $multiDecodeNewHtmlEntity
394
     *
395
     * @return string
396
     */
397
    public function html(bool $multiDecodeNewHtmlEntity = false): string
398
    {
399
        if (static::$callback !== null) {
400
            \call_user_func(static::$callback, [$this]);
401
        }
402
403
        $content = $this->document->saveHTML();
404
405
        if ($content === false) {
406
            return '';
407
        }
408
409
        return $this->fixHtmlOutput($content, $multiDecodeNewHtmlEntity);
410
    }
411
412
    /**
413
     * Load HTML from string.
414
     *
415
     * @param string   $html
416
     * @param int|null $libXMLExtraOptions
417
     *
418
     * @return self
419
     */
420
    public function loadHtml(string $html, $libXMLExtraOptions = null): DomParserInterface
421
    {
422
        $this->document = $this->createDOMDocument($html, $libXMLExtraOptions);
423
424
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (voku\helper\XmlDomParser) is incompatible with the return type declared by the interface voku\helper\DomParserInterface::loadHtml of type self.

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...
425
    }
426
427
    /**
428
     * Load HTML from file.
429
     *
430
     * @param string   $filePath
431
     * @param int|null $libXMLExtraOptions
432
     *
433
     * @throws \RuntimeException
434
     *
435
     * @return XmlDomParser
436
     */
437 View Code Duplication
    public function loadHtmlFile(string $filePath, $libXMLExtraOptions = null): DomParserInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
438
    {
439
        if (
440
            !\preg_match("/^https?:\/\//i", $filePath)
441
            &&
442
            !\file_exists($filePath)
443
        ) {
444
            throw new \RuntimeException("File ${filePath} not found");
445
        }
446
447
        try {
448
            if (\class_exists('\voku\helper\UTF8')) {
449
                /** @noinspection PhpUndefinedClassInspection */
450
                $html = UTF8::file_get_contents($filePath);
451
            } else {
452
                $html = \file_get_contents($filePath);
453
            }
454
        } catch (\Exception $e) {
455
            throw new \RuntimeException("Could not load file ${filePath}");
456
        }
457
458
        if ($html === false) {
459
            throw new \RuntimeException("Could not load file ${filePath}");
460
        }
461
462
        return $this->loadHtml($html, $libXMLExtraOptions);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->loadHtml($..., $libXMLExtraOptions); (voku\helper\XmlDomParser) is incompatible with the return type declared by the interface voku\helper\DomParserInterface::loadHtmlFile of type self.

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...
463
    }
464
465
    /**
466
     * @param string $selector
467
     * @param int    $idx
468
     *
469
     * @return SimpleXmlDomInterface|SimpleXmlDomInterface[]|SimpleXmlDomNodeInterface<SimpleXmlDomInterface>
0 ignored issues
show
Documentation introduced by
The doc-type SimpleXmlDomInterface|Si...<SimpleXmlDomInterface> could not be parsed: Expected "|" or "end of type", but got "<" at position 71. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
470
     */
471
    public function __invoke($selector, $idx = null)
472
    {
473
        return $this->find($selector, $idx);
474
    }
475
476
    /**
477
     * Load XML from string.
478
     *
479
     * @param string   $xml
480
     * @param int|null $libXMLExtraOptions
481
     *
482
     * @return XmlDomParser
483
     */
484
    public function loadXml(string $xml, $libXMLExtraOptions = null): self
485
    {
486 3
        $this->document = $this->createDOMDocument($xml, $libXMLExtraOptions);
487
488 3
        return $this;
489
    }
490
491
    /**
492
     * Load XML from file.
493
     *
494
     * @param string   $filePath
495
     * @param int|null $libXMLExtraOptions
496
     *
497
     * @throws \RuntimeException
498
     *
499
     * @return XmlDomParser
500
     */
501 View Code Duplication
    public function loadXmlFile(string $filePath, $libXMLExtraOptions = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
502
    {
503
        if (
504 2
            !\preg_match("/^https?:\/\//i", $filePath)
505
            &&
506 2
            !\file_exists($filePath)
507
        ) {
508
            throw new \RuntimeException("File ${filePath} not found");
509
        }
510
511
        try {
512 2
            if (\class_exists('\voku\helper\UTF8')) {
513
                /** @noinspection PhpUndefinedClassInspection */
514
                $xml = UTF8::file_get_contents($filePath);
515
            } else {
516 2
                $xml = \file_get_contents($filePath);
517
            }
518
        } catch (\Exception $e) {
519
            throw new \RuntimeException("Could not load file ${filePath}");
520
        }
521
522 2
        if ($xml === false) {
523
            throw new \RuntimeException("Could not load file ${filePath}");
524
        }
525
526 2
        return $this->loadXml($xml, $libXMLExtraOptions);
527
    }
528
529
    /**
530
     * @param callable      $callback
531
     * @param \DOMNode|null $domNode
532
     *
533
     * @return void
534
     */
535
    public function replaceTextWithCallback($callback, \DOMNode $domNode = null)
536
    {
537 1
        if ($domNode === null) {
538 1
            $domNode = $this->document;
539
        }
540
541 1
        if ($domNode->hasChildNodes()) {
542 1
            $children = [];
543
544
            // since looping through a DOM being modified is a bad idea we prepare an array:
545 1
            foreach ($domNode->childNodes as $child) {
546 1
                $children[] = $child;
547
            }
548
549 1
            foreach ($children as $child) {
550 1
                if ($child->nodeType === \XML_TEXT_NODE) {
551
                    /** @noinspection PhpSillyAssignmentInspection */
552
                    /** @var \DOMText $child */
553 1
                    $child = $child;
0 ignored issues
show
Bug introduced by
Why assign $child to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
554
555 1
                    $oldText = self::putReplacedBackToPreserveHtmlEntities($child->wholeText);
556 1
                    $newText = $callback($oldText);
557 1
                    if ($domNode->ownerDocument) {
558 1
                        $newTextNode = $domNode->ownerDocument->createTextNode(self::replaceToPreserveHtmlEntities($newText));
559 1
                        $domNode->replaceChild($newTextNode, $child);
560
                    }
561
                } else {
562 1
                    $this->replaceTextWithCallback($callback, $child);
563
                }
564
            }
565
        }
566 1
    }
567
}
568