Completed
Push — master ( 593209...b6a8dc )
by Lars
02:22
created

XmlDomParser::createDOMDocument()   F

Complexity

Conditions 16
Paths 864

Size

Total Lines 76

Duplication

Lines 35
Ratio 46.05 %

Code Coverage

Tests 27
CRAP Score 22.2113

Importance

Changes 0
Metric Value
cc 16
nc 864
nop 2
dl 35
loc 76
ccs 27
cts 38
cp 0.7105
crap 22.2113
rs 1.5888
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
        if (\PHP_VERSION_ID < 80000) {
118 3
            $disableEntityLoader = \libxml_disable_entity_loader(true);
119
        }
120 3
        \libxml_clear_errors();
121
122 3
        $optionsXml = \LIBXML_DTDLOAD | \LIBXML_DTDATTR | \LIBXML_NONET;
123
124 3
        if (\defined('LIBXML_BIGLINES')) {
125 3
            $optionsXml |= \LIBXML_BIGLINES;
126
        }
127
128 3
        if (\defined('LIBXML_COMPACT')) {
129 3
            $optionsXml |= \LIBXML_COMPACT;
130
        }
131
132 3
        if ($libXMLExtraOptions !== null) {
133
            $optionsXml |= $libXMLExtraOptions;
134
        }
135
136 3
        $xml = self::replaceToPreserveHtmlEntities($xml);
137
138 3
        $documentFound = false;
139 3
        $sxe = \simplexml_load_string($xml, \SimpleXMLElement::class, $optionsXml);
140 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...
141 3
            $domElementTmp = \dom_import_simplexml($sxe);
142
            if (
143 3
                $domElementTmp
144
                &&
145 3
                $domElementTmp->ownerDocument
146
            ) {
147 3
                $documentFound = true;
148 3
                $this->document = $domElementTmp->ownerDocument;
149
            }
150
        }
151
152 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...
153
154
            // UTF-8 hack: http://php.net/manual/en/domdocument.loadhtml.php#95251
155
            $xmlHackUsed = false;
156
            /** @noinspection StringFragmentMisplacedInspection */
157
            if (\stripos('<?xml', $xml) !== 0) {
158
                $xmlHackUsed = true;
159
                $xml = '<?xml encoding="' . $this->getEncoding() . '" ?>' . $xml;
160
            }
161
162
            $this->document->loadXML($xml, $optionsXml);
163
164
            // remove the "xml-encoding" hack
165
            if ($xmlHackUsed) {
166
                foreach ($this->document->childNodes as $child) {
167
                    if ($child->nodeType === \XML_PI_NODE) {
168
                        /** @noinspection UnusedFunctionResultInspection */
169
                        $this->document->removeChild($child);
170
171
                        break;
172
                    }
173
                }
174
            }
175
        }
176
177
        // set encoding
178 3
        $this->document->encoding = $this->getEncoding();
179
180
        // restore lib-xml settings
181 3
        \libxml_clear_errors();
182 3
        \libxml_use_internal_errors($internalErrors);
183 3
        if (\PHP_VERSION_ID < 80000 && isset($disableEntityLoader)) {
184 3
            \libxml_disable_entity_loader($disableEntityLoader);
185
        }
186
187 3
        return $this->document;
188
    }
189
190
    /**
191
     * Find list of nodes with a CSS selector.
192
     *
193
     * @param string   $selector
194
     * @param int|null $idx
195
     *
196
     * @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...
197
     */
198 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...
199
    {
200 1
        $xPathQuery = SelectorConverter::toXPath($selector);
201
202 1
        $xPath = new \DOMXPath($this->document);
203 1
        $nodesList = $xPath->query($xPathQuery);
204 1
        $elements = new SimpleXmlDomNode();
205
206 1
        if ($nodesList) {
207 1
            foreach ($nodesList as $node) {
208 1
                $elements[] = new SimpleXmlDom($node);
209
            }
210
        }
211
212
        // return all elements
213 1
        if ($idx === null) {
214 1
            if (\count($elements) === 0) {
215 1
                return new SimpleXmlDomNodeBlank();
216
            }
217
218 1
            return $elements;
219
        }
220
221
        // handle negative values
222 1
        if ($idx < 0) {
223
            $idx = \count($elements) + $idx;
224
        }
225
226
        // return one element
227 1
        return $elements[$idx] ?? new SimpleXmlDomBlank();
228
    }
229
230
    /**
231
     * Find nodes with a CSS selector.
232
     *
233
     * @param string $selector
234
     *
235
     * @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...
236
     */
237 1
    public function findMulti(string $selector): SimpleXmlDomNodeInterface
238
    {
239 1
        return $this->find($selector, null);
240
    }
241
242
    /**
243
     * Find nodes with a CSS selector or false, if no element is found.
244
     *
245
     * @param string $selector
246
     *
247
     * @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...
248
     */
249 1
    public function findMultiOrFalse(string $selector)
250
    {
251 1
        $return = $this->find($selector, null);
252
253 1
        if ($return instanceof SimpleXmlDomNodeBlank) {
254 1
            return false;
255
        }
256
257
        return $return;
258
    }
259
260
    /**
261
     * Find one node with a CSS selector.
262
     *
263
     * @param string $selector
264
     *
265
     * @return SimpleXmlDomInterface
266
     */
267 1
    public function findOne(string $selector): SimpleXmlDomInterface
268
    {
269 1
        return $this->find($selector, 0);
270
    }
271
272
    /**
273
     * Find one node with a CSS selector or false, if no element is found.
274
     *
275
     * @param string $selector
276
     *
277
     * @return false|SimpleXmlDomInterface
278
     */
279 1
    public function findOneOrFalse(string $selector)
280
    {
281 1
        $return = $this->find($selector, 0);
282
283 1
        if ($return instanceof SimpleXmlDomBlank) {
284 1
            return false;
285
        }
286
287
        return $return;
288
    }
289
290
    /**
291
     * @param string $content
292
     * @param bool   $multiDecodeNewHtmlEntity
293
     *
294
     * @return string
295
     */
296
    public function fixHtmlOutput(string $content, bool $multiDecodeNewHtmlEntity = false): string
297
    {
298
        $content = $this->decodeHtmlEntity($content, $multiDecodeNewHtmlEntity);
299
300
        return self::putReplacedBackToPreserveHtmlEntities($content);
301
    }
302
303
    /**
304
     * Return elements by ".class".
305
     *
306
     * @param string $class
307
     *
308
     * @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...
309
     */
310
    public function getElementByClass(string $class): SimpleXmlDomNodeInterface
311
    {
312
        return $this->findMulti(".${class}");
313
    }
314
315
    /**
316
     * Return element by #id.
317
     *
318
     * @param string $id
319
     *
320
     * @return SimpleXmlDomInterface
321
     */
322
    public function getElementById(string $id): SimpleXmlDomInterface
323
    {
324
        return $this->findOne("#${id}");
325
    }
326
327
    /**
328
     * Return element by tag name.
329
     *
330
     * @param string $name
331
     *
332
     * @return SimpleXmlDomInterface
333
     */
334
    public function getElementByTagName(string $name): SimpleXmlDomInterface
335
    {
336
        $node = $this->document->getElementsByTagName($name)->item(0);
337
338
        if ($node === null) {
339
            return new SimpleXmlDomBlank();
340
        }
341
342
        return new SimpleXmlDom($node);
343
    }
344
345
    /**
346
     * Returns elements by "#id".
347
     *
348
     * @param string   $id
349
     * @param int|null $idx
350
     *
351
     * @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...
352
     */
353
    public function getElementsById(string $id, $idx = null)
354
    {
355
        return $this->find("#${id}", $idx);
356
    }
357
358
    /**
359
     * Returns elements by tag name.
360
     *
361
     * @param string   $name
362
     * @param int|null $idx
363
     *
364
     * @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...
365
     */
366 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...
367
    {
368
        $nodesList = $this->document->getElementsByTagName($name);
369
370
        $elements = new SimpleXmlDomNode();
371
372
        foreach ($nodesList as $node) {
373
            $elements[] = new SimpleXmlDom($node);
374
        }
375
376
        // return all elements
377
        if ($idx === null) {
378
            if (\count($elements) === 0) {
379
                return new SimpleXmlDomNodeBlank();
380
            }
381
382
            return $elements;
383
        }
384
385
        // handle negative values
386
        if ($idx < 0) {
387
            $idx = \count($elements) + $idx;
388
        }
389
390
        // return one element
391
        return $elements[$idx] ?? new SimpleXmlDomNodeBlank();
392
    }
393
394
    /**
395
     * Get dom node's outer html.
396
     *
397
     * @param bool $multiDecodeNewHtmlEntity
398
     *
399
     * @return string
400
     */
401
    public function html(bool $multiDecodeNewHtmlEntity = false): string
402
    {
403
        if (static::$callback !== null) {
404
            \call_user_func(static::$callback, [$this]);
405
        }
406
407
        $content = $this->document->saveHTML();
408
409
        if ($content === false) {
410
            return '';
411
        }
412
413
        return $this->fixHtmlOutput($content, $multiDecodeNewHtmlEntity);
414
    }
415
416
    /**
417
     * Load HTML from string.
418
     *
419
     * @param string   $html
420
     * @param int|null $libXMLExtraOptions
421
     *
422
     * @return self
423
     */
424
    public function loadHtml(string $html, $libXMLExtraOptions = null): DomParserInterface
425
    {
426
        $this->document = $this->createDOMDocument($html, $libXMLExtraOptions);
427
428
        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...
429
    }
430
431
    /**
432
     * Load HTML from file.
433
     *
434
     * @param string   $filePath
435
     * @param int|null $libXMLExtraOptions
436
     *
437
     * @throws \RuntimeException
438
     *
439
     * @return XmlDomParser
440
     */
441 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...
442
    {
443
        if (
444
            !\preg_match("/^https?:\/\//i", $filePath)
445
            &&
446
            !\file_exists($filePath)
447
        ) {
448
            throw new \RuntimeException("File ${filePath} not found");
449
        }
450
451
        try {
452
            if (\class_exists('\voku\helper\UTF8')) {
453
                /** @noinspection PhpUndefinedClassInspection */
454
                $html = UTF8::file_get_contents($filePath);
455
            } else {
456
                $html = \file_get_contents($filePath);
457
            }
458
        } catch (\Exception $e) {
459
            throw new \RuntimeException("Could not load file ${filePath}");
460
        }
461
462
        if ($html === false) {
463
            throw new \RuntimeException("Could not load file ${filePath}");
464
        }
465
466
        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...
467
    }
468
469
    /**
470
     * @param string $selector
471
     * @param int    $idx
472
     *
473
     * @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...
474
     */
475
    public function __invoke($selector, $idx = null)
476
    {
477
        return $this->find($selector, $idx);
478
    }
479
480
    /**
481
     * Load XML from string.
482
     *
483
     * @param string   $xml
484
     * @param int|null $libXMLExtraOptions
485
     *
486
     * @return XmlDomParser
487
     */
488 3
    public function loadXml(string $xml, $libXMLExtraOptions = null): self
489
    {
490 3
        $this->document = $this->createDOMDocument($xml, $libXMLExtraOptions);
491
492 3
        return $this;
493
    }
494
495
    /**
496
     * Load XML from file.
497
     *
498
     * @param string   $filePath
499
     * @param int|null $libXMLExtraOptions
500
     *
501
     * @throws \RuntimeException
502
     *
503
     * @return XmlDomParser
504
     */
505 2 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...
506
    {
507
        if (
508 2
            !\preg_match("/^https?:\/\//i", $filePath)
509
            &&
510 2
            !\file_exists($filePath)
511
        ) {
512
            throw new \RuntimeException("File ${filePath} not found");
513
        }
514
515
        try {
516 2
            if (\class_exists('\voku\helper\UTF8')) {
517
                /** @noinspection PhpUndefinedClassInspection */
518
                $xml = UTF8::file_get_contents($filePath);
519
            } else {
520 2
                $xml = \file_get_contents($filePath);
521
            }
522
        } catch (\Exception $e) {
523
            throw new \RuntimeException("Could not load file ${filePath}");
524
        }
525
526 2
        if ($xml === false) {
527
            throw new \RuntimeException("Could not load file ${filePath}");
528
        }
529
530 2
        return $this->loadXml($xml, $libXMLExtraOptions);
531
    }
532
533
    /**
534
     * @param callable      $callback
535
     * @param \DOMNode|null $domNode
536
     *
537
     * @return void
538
     */
539 1
    public function replaceTextWithCallback($callback, \DOMNode $domNode = null)
540
    {
541 1
        if ($domNode === null) {
542 1
            $domNode = $this->document;
543
        }
544
545 1
        if ($domNode->hasChildNodes()) {
546 1
            $children = [];
547
548
            // since looping through a DOM being modified is a bad idea we prepare an array:
549 1
            foreach ($domNode->childNodes as $child) {
550 1
                $children[] = $child;
551
            }
552
553 1
            foreach ($children as $child) {
554 1
                if ($child->nodeType === \XML_TEXT_NODE) {
555
                    /** @noinspection PhpSillyAssignmentInspection */
556
                    /** @var \DOMText $child */
557 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...
558
559 1
                    $oldText = self::putReplacedBackToPreserveHtmlEntities($child->wholeText);
560 1
                    $newText = $callback($oldText);
561 1
                    if ($domNode->ownerDocument) {
562 1
                        $newTextNode = $domNode->ownerDocument->createTextNode(self::replaceToPreserveHtmlEntities($newText));
563 1
                        $domNode->replaceChild($newTextNode, $child);
564
                    }
565
                } else {
566 1
                    $this->replaceTextWithCallback($callback, $child);
567
                }
568
            }
569
        }
570 1
    }
571
}
572