Passed
Pull Request — master (#6921)
by
unknown
09:03
created

XMLGenericDocument::chkxpath()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 4
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 6
rs 10
1
<?php
2
3
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/xmlbase.php under GNU/GPL license */
4
5
declare(strict_types=1);
6
7
namespace Chamilo\CourseBundle\Component\CourseCopy\CommonCartridge\Export\Base;
8
9
use DOMDocument;
10
use DOMNode;
11
use DOMXPath;
12
13
use const ENT_NOQUOTES;
14
use const ENT_QUOTES;
15
use const XML_CDATA_SECTION_NODE;
16
use const XML_TEXT_NODE;
17
18
/**
19
 * Base XML class.
20
 */
21
class XMLGenericDocument
22
{
23
    /**
24
     * Document.
25
     *
26
     * @var DOMDocument
27
     */
28
    public $doc;
29
30
    /**
31
     * Xpath.
32
     *
33
     * @var DOMXPath
34
     */
35
    protected $dxpath;
36
    protected $filename;
37
    private $charset;
38
    private $filepath;
39
    private $isloaded = false;
40
    private $arrayPrefixNS = [];
41
    private $isHtml = false;
42
43
    public function __construct($ch = 'UTF-8', $validatenow = true)
44
    {
45
        $this->charset = $ch;
46
        $this->documentInit();
47
        $this->doc->validateOnParse = $validatenow;
48
    }
49
50
    public function __destruct()
51
    {
52
        $this->dxpath = null;
53
        $this->doc = null;
54
    }
55
56
    /**
57
     * @param string $value
58
     *
59
     * @return string
60
     */
61
    public static function safexml($value)
62
    {
63
        return htmlspecialchars(
64
            html_entity_decode($value, ENT_QUOTES, 'UTF-8'),
65
            ENT_NOQUOTES,
66
            'UTF-8',
67
            false
68
        );
69
    }
70
71
    public function viewXML()
72
    {
73
        return $this->doc->saveXML();
74
    }
75
76
    public function registerNS($prefix, $nsuri): void
77
    {
78
        $this->arrayPrefixNS[$prefix] = $nsuri;
79
    }
80
81
    public function load($fname)
82
    {
83
        // Sine xml will remain loaded should the repeated load fail we should recreate document to be empty.
84
        $this->documentInit(false);
85
        $this->isloaded = $this->doc->load($fname);
86
        if ($this->isloaded) {
87
            $this->filename = $fname;
88
            $this->processPath();
89
            $this->isHtml = false;
90
        }
91
92
        return $this->onLoad();
93
    }
94
95
    public function loadUrl($url)
96
    {
97
        $this->documentInit();
98
        $this->isloaded = true;
99
        $this->doc->loadXML(file_get_contents($url));
100
        $this->isHtml = false;
101
102
        return $this->onLoad();
103
    }
104
105
    public function loadHTML($content)
106
    {
107
        $this->documentInit();
108
        $this->doc->validateOnParse = false;
109
        $this->isloaded = true;
110
        @$this->doc->loadHTML($content);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for loadHTML(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

110
        /** @scrutinizer ignore-unhandled */ @$this->doc->loadHTML($content);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
111
        $this->isHtml = true;
112
113
        return $this->onLoad();
114
    }
115
116
    public function loadXML($content)
117
    {
118
        $this->documentInit();
119
        $this->doc->validateOnParse = false;
120
        $this->isloaded = true;
121
        $this->doc->load($content);
122
        $this->isHtml = true;
123
124
        return $this->onLoad();
125
    }
126
127
    public function loadHTMLFile($fname)
128
    {
129
        // Sine xml will remain loaded should the repeated load fail
130
        // we should recreate document to be empty.
131
        $this->documentInit();
132
        $this->doc->validateOnParse = false;
133
        $this->isloaded = $this->doc->loadHTMLFile($fname);
134
        if ($this->isloaded) {
135
            $this->filename = $fname;
136
            $this->processPath();
137
            $this->isHtml = true;
138
        }
139
140
        return $this->onLoad();
141
    }
142
143
    public function loadXMLFile($fname)
144
    {
145
        // Sine xml will remain loaded should the repeated load fail
146
        // we should recreate document to be empty.
147
        $this->documentInit();
148
        $this->doc->validateOnParse = false;
149
        $this->isloaded = $this->doc->load($fname);
150
        if ($this->isloaded) {
151
            $this->filename = $fname;
152
            $this->processPath();
153
            $this->isHtml = true;
154
        }
155
156
        return $this->onLoad();
157
    }
158
159
    public function loadString($content)
160
    {
161
        $this->doc = new DOMDocument('1.0', $this->charset);
162
        $content = '<virtualtag>'.$content.'</virtualtag>';
163
        $this->doc->loadXML($content);
164
165
        return true;
166
    }
167
168
    public function save(): void
169
    {
170
        $this->saveTo($this->filename);
171
    }
172
173
    public function saveTo($fname)
174
    {
175
        $status = false;
176
        if ($this->onSave()) {
177
            if ($this->isHtml) {
178
                $this->doc->saveHTMLFile($fname);
179
            } else {
180
                $this->doc->save($fname);
181
            }
182
            $this->filename = $fname;
183
            $this->processPath();
184
            $status = true;
185
        }
186
187
        return $status;
188
    }
189
190
    public function validate()
191
    {
192
        return $this->doc->validate();
193
    }
194
195
    public function attributeValue($path, $attrname, $node = null)
196
    {
197
        $this->chkxpath();
198
        $result = null;
199
        $resultlist = null;
200
        if (null === $node) {
201
            $resultlist = $this->dxpath->query($path);
202
        } else {
203
            $resultlist = $this->dxpath->query($path, $node);
204
        }
205
        if (\is_object($resultlist) && ($resultlist->length > 0) && $resultlist->item(0)->hasAttribute($attrname)) {
0 ignored issues
show
Bug introduced by
The method hasAttribute() does not exist on DOMNode. Did you maybe mean hasAttributes()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

205
        if (\is_object($resultlist) && ($resultlist->length > 0) && $resultlist->item(0)->/** @scrutinizer ignore-call */ hasAttribute($attrname)) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
206
            $result = $resultlist->item(0)->getAttribute($attrname);
207
        }
208
209
        return $result;
210
    }
211
212
    /**
213
     * Get's text value of the node based on xpath query.
214
     *
215
     * @param string  $path
216
     * @param DOMNode $node
217
     * @param int     $count
218
     *
219
     * @return string
220
     */
221
    public function nodeValue($path, $node = null, $count = 1)
222
    {
223
        $nd = $this->node($path, $node, $count);
224
225
        return $this->nodeTextValue($nd);
226
    }
227
228
    /**
229
     * Get's text value of the node.
230
     *
231
     * @param DOMNode $node
232
     *
233
     * @return string
234
     */
235
    public function nodeTextValue($node)
236
    {
237
        $result = '';
238
        if (\is_object($node)) {
239
            if ($node->hasChildNodes()) {
240
                $chnodesList = $node->childNodes;
241
                $types = [XML_TEXT_NODE, XML_CDATA_SECTION_NODE];
242
                foreach ($chnodesList as $chnode) {
243
                    if (\in_array($chnode->nodeType, $types)) {
244
                        $result .= $chnode->wholeText;
245
                    }
246
                }
247
            }
248
        }
249
250
        return $result;
251
    }
252
253
    /**
254
     * Get the nodes from a path.
255
     *
256
     * @param string  $path
257
     * @param DOMNode $nd
258
     * @param int     $count
259
     *
260
     * @return DOMNode
261
     */
262
    public function node($path, $nd = null, $count = 1)
263
    {
264
        $result = null;
265
        $resultlist = $this->nodeList($path, $nd);
266
        if (\is_object($resultlist) && ($resultlist->length > 0)) {
267
            $result = $resultlist->item($count - 1);
268
        }
269
270
        return $result;
271
    }
272
273
    /**
274
     * Get a list of nodes from a path.
275
     *
276
     * @param string  $path
277
     * @param DOMNode $node
278
     *
279
     * @return DOMNodeList
0 ignored issues
show
Bug introduced by
The type Chamilo\CourseBundle\Com...Export\Base\DOMNodeList was not found. Did you mean DOMNodeList? If so, make sure to prefix the type with \.
Loading history...
280
     */
281
    public function nodeList($path, $node = null)
282
    {
283
        $this->chkxpath();
284
        $resultlist = null;
285
        if (null === $node) {
286
            $resultlist = $this->dxpath->query($path);
287
        } else {
288
            $resultlist = $this->dxpath->query($path, $node);
289
        }
290
291
        return $resultlist;
292
    }
293
294
    /**
295
     * Create new attribute.
296
     *
297
     * @param string $namespace
298
     * @param string $name
299
     * @param string $value
300
     *
301
     * @return DOMAttr
0 ignored issues
show
Bug introduced by
The type Chamilo\CourseBundle\Com...dge\Export\Base\DOMAttr was not found. Did you mean DOMAttr? If so, make sure to prefix the type with \.
Loading history...
302
     */
303
    public function createAttributeNs($namespace, $name, $value = null)
304
    {
305
        $result = $this->doc->createAttributeNS($namespace, $name);
306
        if (null !== $value) {
307
            $result->nodeValue = $value;
308
        }
309
310
        return $result;
311
    }
312
313
    /**
314
     * Create new attribute.
315
     *
316
     * @param string $name
317
     * @param string $value
318
     *
319
     * @return DOMAttr
320
     */
321
    public function createAttribute($name, $value = null)
322
    {
323
        $result = $this->doc->createAttribute($name);
324
        if (null !== $value) {
325
            $result->nodeValue = $value;
326
        }
327
328
        return $result;
329
    }
330
331
    /**
332
     * Adds new node.
333
     *
334
     * @param string $namespace
335
     * @param string $name
336
     * @param string $value
337
     *
338
     * @return DOMNode
339
     */
340
    public function appendNewElementNs(DOMNode &$parentnode, $namespace, $name, $value = null)
341
    {
342
        $newnode = null;
343
        if (null === $value) {
344
            $newnode = $this->doc->createElementNS($namespace, $name);
345
        } else {
346
            $newnode = $this->doc->createElementNS($namespace, $name, $value);
347
        }
348
349
        return $parentnode->appendChild($newnode);
350
    }
351
352
    /**
353
     * New node with CDATA content.
354
     *
355
     * @param string $namespace
356
     * @param string $name
357
     * @param string $value
358
     */
359
    public function appendNewElementNsCdata(DOMNode &$parentnode, $namespace, $name, $value = null)
360
    {
361
        $newnode = $this->doc->createElementNS($namespace, $name);
362
        if (null !== $value) {
363
            $cdata = $this->doc->createCDATASection($value);
364
            $newnode->appendChild($cdata);
365
        }
366
367
        return $parentnode->appendChild($newnode);
368
    }
369
370
    /**
371
     * Adds new node.
372
     *
373
     * @param string $name
374
     * @param string $value
375
     *
376
     * @return DOMNode
377
     */
378
    public function appendNewElement(DOMNode &$parentnode, $name, $value = null)
379
    {
380
        $newnode = null;
381
        if (null === $value) {
382
            $newnode = $this->doc->createElement($name);
383
        } else {
384
            $newnode = $this->doc->createElement($name, $value);
385
        }
386
387
        return $parentnode->appendChild($newnode);
388
    }
389
390
    /**
391
     * Adds new attribute.
392
     *
393
     * @param string $name
394
     * @param string $value
395
     *
396
     * @return DOMNode
397
     */
398
    public function appendNewAttribute(DOMNode &$node, $name, $value = null)
399
    {
400
        return $node->appendChild($this->createAttribute($name, $value));
401
    }
402
403
    /**
404
     * Adds new attribute.
405
     *
406
     * @param string $namespace
407
     * @param string $name
408
     * @param string $value
409
     *
410
     * @return DOMNode
411
     */
412
    public function appendNewAttributeNs(DOMNode &$node, $namespace, $name, $value = null)
413
    {
414
        return $node->appendChild($this->createAttributeNs($namespace, $name, $value));
415
    }
416
417
    public function fileName()
418
    {
419
        return $this->filename;
420
    }
421
422
    public function filePath()
423
    {
424
        return $this->filepath;
425
    }
426
427
    public function resetXpath(): void
428
    {
429
        $this->dxpath = null;
430
        $this->chkxpath();
431
    }
432
433
    protected function onLoad()
434
    {
435
        return $this->isloaded;
436
    }
437
438
    protected function onSave()
439
    {
440
        return true;
441
    }
442
443
    protected function onCreate()
444
    {
445
        return true;
446
    }
447
448
    protected function processPath(): void
449
    {
450
        $path_parts = pathinfo($this->filename);
451
        $this->filepath = \array_key_exists('dirname', $path_parts) ? $path_parts['dirname'].'/' : '';
452
    }
453
454
    private function documentInit($withonCreate = true): void
455
    {
456
        $hg = false;
457
        if ($this->isloaded) {
458
            $guardstate = $this->doc->validateOnParse;
459
            $hg = true;
460
            unset($this->dxpath, $this->doc);
461
462
            $this->isloaded = false;
463
        }
464
        $this->doc = new DOMDocument('1.0', $this->charset);
465
        $this->doc->strictErrorChecking = true;
466
        if ($hg) {
467
            $this->doc->validateOnParse = $guardstate;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $guardstate does not seem to be defined for all execution paths leading up to this point.
Loading history...
468
        }
469
        $this->doc->formatOutput = true;
470
        $this->doc->preserveWhiteSpace = true;
471
        if ($withonCreate) {
472
            $this->onCreate();
473
        }
474
    }
475
476
    private function chkxpath(): void
477
    {
478
        if (!isset($this->dxpath) || null === $this->dxpath) {
479
            $this->dxpath = new DOMXPath($this->doc);
480
            foreach ($this->arrayPrefixNS as $nskey => $nsuri) {
481
                $this->dxpath->registerNamespace($nskey, $nsuri);
482
            }
483
        }
484
    }
485
}
486