XMLGenericDocument   F
last analyzed

Complexity

Total Complexity 64

Size/Duplication

Total Lines 460
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 165
c 1
b 0
f 0
dl 0
loc 460
rs 3.28
wmc 64

36 Methods

Rating   Name   Duplication   Size   Complexity  
A onCreate() 0 3 1
A validate() 0 3 1
A nodeValue() 0 5 1
A createAttributeNs() 0 8 2
A saveTo() 0 15 3
A chkxpath() 0 6 4
A appendNewElementNs() 0 10 2
A appendNewElementNsCdata() 0 9 2
A filePath() 0 3 1
A fileName() 0 3 1
A appendNewElement() 0 10 2
A save() 0 3 1
A createAttribute() 0 8 2
A __construct() 0 5 1
A safexml() 0 8 1
A nodeList() 0 11 2
A viewXML() 0 3 1
A appendNewAttribute() 0 3 1
A appendNewAttributeNs() 0 3 1
A loadHTMLFile() 0 14 2
A loadHTML() 0 9 1
A loadString() 0 7 1
A node() 0 9 3
A onSave() 0 3 1
A processPath() 0 4 2
A registerNS() 0 3 1
A attributeValue() 0 15 5
A documentInit() 0 19 4
A loadXML() 0 9 1
A load() 0 12 2
A resetXpath() 0 4 1
A loadUrl() 0 8 1
A onLoad() 0 3 1
A nodeTextValue() 0 16 5
A __destruct() 0 4 1
A loadXMLFile() 0 14 2

How to fix   Complexity   

Complex Class

Complex classes like XMLGenericDocument often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use XMLGenericDocument, and based on these observations, apply Extract Interface, too.

1
<?php
2
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/xmlbase.php under GNU/GPL license */
3
4
/**
5
 * Base XML class.
6
 */
7
class XMLGenericDocument
8
{
9
    /**
10
     * Document.
11
     *
12
     * @var DOMDocument
13
     */
14
    public $doc = null;
15
    /**
16
     * Xpath.
17
     *
18
     * @var DOMXPath
19
     */
20
    protected $dxpath = null;
21
    protected $filename;
22
    private $charset;
23
    private $filepath;
24
    private $isloaded = false;
25
    private $arrayPrefixNS = [];
26
    private $isHtml = false;
27
28
    public function __construct($ch = 'UTF-8', $validatenow = true)
29
    {
30
        $this->charset = $ch;
31
        $this->documentInit();
32
        $this->doc->validateOnParse = $validatenow;
33
    }
34
35
    public function __destruct()
36
    {
37
        $this->dxpath = null;
38
        $this->doc = null;
39
    }
40
41
    /**
42
     * @param string $value
43
     *
44
     * @return string
45
     */
46
    public static function safexml($value)
47
    {
48
        $result = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'),
49
                                   ENT_NOQUOTES,
50
                                   'UTF-8',
51
                                   false);
52
53
        return $result;
54
    }
55
56
    public function viewXML()
57
    {
58
        return $this->doc->saveXML();
59
    }
60
61
    public function registerNS($prefix, $nsuri)
62
    {
63
        $this->arrayPrefixNS[$prefix] = $nsuri;
64
    }
65
66
    public function load($fname)
67
    {
68
        // Sine xml will remain loaded should the repeated load fail we should recreate document to be empty.
69
        $this->documentInit(false);
70
        $this->isloaded = $this->doc->load($fname);
71
        if ($this->isloaded) {
72
            $this->filename = $fname;
73
            $this->processPath();
74
            $this->isHtml = false;
75
        }
76
77
        return $this->onLoad();
78
    }
79
80
    public function loadUrl($url)
81
    {
82
        $this->documentInit();
83
        $this->isloaded = true;
84
        $this->doc->loadXML(file_get_contents($url));
85
        $this->isHtml = false;
86
87
        return $this->onLoad();
88
    }
89
90
    public function loadHTML($content)
91
    {
92
        $this->documentInit();
93
        $this->doc->validateOnParse = false;
94
        $this->isloaded = true;
95
        $this->doc->loadHTML($content);
96
        $this->isHtml = true;
97
98
        return $this->onLoad();
99
    }
100
101
    public function loadXML($content)
102
    {
103
        $this->documentInit();
104
        $this->doc->validateOnParse = false;
105
        $this->isloaded = true;
106
        $this->doc->load($content);
107
        $this->isHtml = true;
108
109
        return $this->onLoad();
110
    }
111
112
    public function loadHTMLFile($fname)
113
    {
114
        // Sine xml will remain loaded should the repeated load fail
115
        // we should recreate document to be empty.
116
        $this->documentInit();
117
        $this->doc->validateOnParse = false;
118
        $this->isloaded = $this->doc->loadHTMLFile($fname);
119
        if ($this->isloaded) {
120
            $this->filename = $fname;
121
            $this->processPath();
122
            $this->isHtml = true;
123
        }
124
125
        return $this->onLoad();
126
    }
127
128
    public function loadXMLFile($fname)
129
    {
130
        // Sine xml will remain loaded should the repeated load fail
131
        // we should recreate document to be empty.
132
        $this->documentInit();
133
        $this->doc->validateOnParse = false;
134
        $this->isloaded = $this->doc->load($fname);
135
        if ($this->isloaded) {
136
            $this->filename = $fname;
137
            $this->processPath();
138
            $this->isHtml = true;
139
        }
140
141
        return $this->onLoad();
142
    }
143
144
    public function loadString($content)
145
    {
146
        $this->doc = new DOMDocument("1.0", $this->charset);
147
        $content = '<virtualtag>'.$content.'</virtualtag>';
148
        $this->doc->loadXML($content);
149
150
        return true;
151
    }
152
153
    public function save()
154
    {
155
        $this->saveTo($this->filename);
156
    }
157
158
    public function saveTo($fname)
159
    {
160
        $status = false;
161
        if ($this->onSave()) {
162
            if ($this->isHtml) {
163
                $this->doc->saveHTMLFile($fname);
164
            } else {
165
                $this->doc->save($fname);
166
            }
167
            $this->filename = $fname;
168
            $this->processPath();
169
            $status = true;
170
        }
171
172
        return $status;
173
    }
174
175
    public function validate()
176
    {
177
        return $this->doc->validate();
178
    }
179
180
    public function attributeValue($path, $attrname, $node = null)
181
    {
182
        $this->chkxpath();
183
        $result = null;
184
        $resultlist = null;
185
        if (is_null($node)) {
186
            $resultlist = $this->dxpath->query($path);
187
        } else {
188
            $resultlist = $this->dxpath->query($path, $node);
189
        }
190
        if (is_object($resultlist) && ($resultlist->length > 0) && $resultlist->item(0)->hasAttribute($attrname)) {
0 ignored issues
show
Bug introduced by Christian
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

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