Completed
Push — master ( 6aef24...286712 )
by Márk
06:17
created

XmlReaderParser   F

Complexity

Total Complexity 83

Size/Duplication

Total Lines 319
Duplicated Lines 16.61 %

Coupling/Cohesion

Components 0
Dependencies 2

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 83
c 1
b 0
f 0
lcom 0
cbo 2
dl 53
loc 319
ccs 0
cts 249
cp 0
rs 1.5789

1 Method

Rating   Name   Duplication   Size   Complexity  
D parse() 53 313 83

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like XmlReaderParser 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 XmlReaderParser, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Fxmlrpc\Serialization\Parser;
4
5
use Fxmlrpc\Serialization\Exception\UnexpectedTagException;
6
use Fxmlrpc\Serialization\Parser;
7
use Fxmlrpc\Serialization\Value\Base64Value;
8
9
/**
10
 * Parser to parse XML responses into its PHP representation using XML Reader extension.
11
 *
12
 * @author Lars Strojny <[email protected]>
13
 */
14
final class XmlReaderParser implements Parser
15
{
16
    /**
17
     * {@inheritdoc}
18
     */
19
    public function parse($xmlString, &$isFault)
20
    {
21
        $useErrors = libxml_use_internal_errors(true);
22
23
        $xml = new \XMLReader();
24
        $xml->xml($xmlString, 'UTF-8', LIBXML_COMPACT | LIBXML_NOCDATA | LIBXML_NOBLANKS  | LIBXML_PARSEHUGE);
25
        $xml->setParserProperty(\XMLReader::VALIDATE, false);
26
        $xml->setParserProperty(\XMLReader::LOADDTD, false);
27
28
// This following assignments are auto-generated using Fxmlrpc\Serialization\CodeGenerator\XmlReaderParserBitmaskGenerator
29
// Don’t edit manually
30
        static $flagmethodResponse = 0b000000000000000000000000001;
31
        static $flagparams = 0b000000000000000000000000010;
32
        static $flagfault = 0b000000000000000000000000100;
33
        static $flagparam = 0b000000000000000000000001000;
34
        static $flagvalue = 0b000000000000000000000010000;
35
        static $flagarray = 0b000000000000000000000100000;
36
        static $flagmember = 0b000000000000000000001000000;
37
        static $flagname = 0b000000000000000000010000000;
38
        ${'flag#text'} = 0b000000000000000000100000000;
39
        static $flagstring = 0b000000000000000001000000000;
40
        static $flagstruct = 0b000000000000000010000000000;
41
        static $flagint = 0b000000000000000100000000000;
42
        static $flagbiginteger = 0b000000000000001000000000000;
43
        static $flagi8 = 0b000000000000010000000000000;
44
        static $flagi4 = 0b000000000000100000000000000;
45
        static $flagi2 = 0b000000000001000000000000000;
46
        static $flagi1 = 0b000000000010000000000000000;
47
        static $flagboolean = 0b000000000100000000000000000;
48
        static $flagdouble = 0b000000001000000000000000000;
49
        static $flagfloat = 0b000000010000000000000000000;
50
        static $flagbigdecimal = 0b000000100000000000000000000;
51
        ${'flagdateTime.iso8601'} = 0b000001000000000000000000000;
52
        static $flagdateTime = 0b000010000000000000000000000;
53
        static $flagbase64 = 0b000100000000000000000000000;
54
        static $flagnil = 0b001000000000000000000000000;
55
        static $flagdom = 0b010000000000000000000000000;
56
        static $flagdata = 0b100000000000000000000000000;
57
// End of auto-generated code
58
59
        $aggregates = [];
60
        $depth = 0;
61
        $nextExpectedElements = 0b000000000000000000000000001;
62
        $i = 0;
63
        while ($xml->read()) {
64
            ++$i;
65
            $nodeType = $xml->nodeType;
66
67
            if (($nodeType === \XMLReader::COMMENT || $nodeType === \XMLReader::DOC_TYPE) ||
68
                (
69
                   $nodeType === \XMLReader::SIGNIFICANT_WHITESPACE &&
70
                   ($nextExpectedElements & 0b000000000000000000100000000) !== 0b000000000000000000100000000)
71
                ) {
72
                continue;
73
            }
74
75
            if ($nodeType === \XMLReader::ENTITY_REF) {
76
                return '';
77
            }
78
79
            $tagName = $xml->localName;
80
            if ($nextExpectedElements !== null &&
81
                ($flag = isset(${'flag'.$tagName}) ? ${'flag'.$tagName} : -1) &&
82
                ($nextExpectedElements & $flag) !== $flag) {
83
                throw new UnexpectedTagException(
84
                    $tagName,
85
                    $nextExpectedElements,
86
                    get_defined_vars(),
87
                    $xml->depth,
88
                    $xml->readOuterXml()
89
                );
90
            }
91
92
            processing:
93
            switch ($nodeType) {
94
                case \XMLReader::ELEMENT:
95
                    switch ($tagName) {
96
                        case 'methodResponse':
97
                            // Next: params, fault
98
                            $nextExpectedElements = 0b000000000000000000000000110;
99
                            break;
100
101
                        case 'params':
102
                            // Next: param
103
                            $nextExpectedElements = 0b000000000000000000000001000;
104
                            $aggregates[$depth] = [];
105
                            $isFault = false;
106
                            break;
107
108
                        case 'fault':
109
                            $isFault = true;
110
                            // Break intentionally omitted
111
                        case 'param':
112
                            // Next: value
113
                            $nextExpectedElements = 0b000000000000000000000010000;
114
                            break;
115
116
                        case 'array':
117
                            $aggregates[++$depth] = [];
118
                            // Break intentionally omitted
119
                        case 'data':
120
                            // Next: array, data, value
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
121
                            $nextExpectedElements = 0b100000000000000000000110000;
122
                            break;
123
124
                        case 'struct':
125
                            // Next: struct, member, value
126
                            $nextExpectedElements = 0b000000000000000010001010000;
127
                            $aggregates[++$depth] = [];
128
                            break;
129
130
                        case 'member':
131
                            // Next: name, value
132
                            $nextExpectedElements = 0b000000000000000000010010000;
133
                            $aggregates[++$depth] = [];
134
                            break;
135
136
                        case 'name':
137
                            // Next: #text
138
                            $nextExpectedElements = 0b000000000000000000100000000;
139
                            $type = 'name';
140
                            break;
141
142
                        case 'value':
143
                            $nextExpectedElements = 0b011111111111111111100110000;
144
                            $type = 'value';
145
                            $aggregates[$depth + 1] = '';
146
                            break;
147
148
                        case 'base64':
149
                        case 'string':
150
                        case 'biginteger':
151
                        case 'i8':
152
                        case 'dateTime.iso8601':
153 View Code Duplication
                        case 'dateTime':
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...
154
                            // Next: value, $tagName, #text
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
155
                            $nextExpectedElements = 0b000000000000000000100010000 | ${'flag'.$tagName};
156
                            $type = $tagName;
157
                            $aggregates[$depth + 1] = '';
158
                            break;
159
160 View Code Duplication
                        case 'nil':
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...
161
                            // Next: value, $tagName
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
162
                            $nextExpectedElements = 0b001000000000000000000010000 | ${'flag'.$tagName};
163
                            $type = $tagName;
164
                            $aggregates[$depth + 1] = null;
165
                            break;
166
167
                        case 'int':
168
                        case 'i4':
169
                        case 'i2':
170 View Code Duplication
                        case 'i1':
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...
171
                            // Next: value, #text, $tagName
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
172
                            $nextExpectedElements = 0b000000000000000000100010000 | ${'flag'.$tagName};
173
                            $type = $tagName;
174
                            $aggregates[$depth + 1] = 0;
175
                            break;
176
177 View Code Duplication
                        case 'boolean':
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...
178
                            // Next: value, #text, $tagName
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
179
                            $nextExpectedElements = 0b000000000000000000100010000 | ${'flag'.$tagName};
180
                            $type = 'boolean';
181
                            $aggregates[$depth + 1] = false;
182
                            break;
183
184
                        case 'double':
185
                        case 'float':
186 View Code Duplication
                        case 'bigdecimal':
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...
187
                            // Next: value, #text, $tagName
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
188
                            $nextExpectedElements = 0b000000000000000000100010000 | ${'flag'.$tagName};
189
                            $type = $tagName;
190
                            $aggregates[$depth + 1] = 0.0;
191
                            break;
192
193
                        case 'dom':
194
                            $type = 'dom';
195
                            // Disable type checking
196
                            $nextExpectedElements = null;
197
                            $aggregates[$depth + 1] = $xml->readInnerXml();
198
                            break;
199
                    }
200
                    break;
201
202
                case \XMLReader::END_ELEMENT:
203
                    switch ($tagName) {
204
                        case 'params':
205
                        case 'fault':
206
                            break 3;
207
208
                        case 'param':
209
                            // Next: params, param
210
                            $nextExpectedElements = 0b000000000000000000000001010;
211
                            break;
212
213 View Code Duplication
                        case 'value':
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...
214
                            $nextExpectedElements = 0b100100000011100100011011100;
215
                            $aggregates[$depth][] = $aggregates[$depth + 1];
216
                            break;
217
218
                        case 'array':
219
                        case 'struct':
220
                            --$depth;
221
                            // Break intentionally omitted
222
                        case 'string':
223
                        case 'int':
224
                        case 'biginteger':
225
                        case 'i8':
226
                        case 'i4':
227
                        case 'i2':
228
                        case 'i1':
229
                        case 'boolean':
230
                        case 'double':
231
                        case 'float':
232
                        case 'bigdecimal':
233
                        case 'dateTime.iso8601':
234
                        case 'dateTime':
235
                        case 'base64':
236
                        case 'nil':
237
                            // Next: value
238
                            $nextExpectedElements = 0b000000000000000000000010000;
239
                            break;
240
241
                        case 'data':
242
                            // Next: array
243
                            $nextExpectedElements = 0b000000000000000000000100000;
244
                            break;
245
246 View Code Duplication
                        case 'name':
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...
247
                            // Next: value, member
248
                            $nextExpectedElements = 0b000000000000000000001010000;
249
                            $aggregates[$depth]['name'] = $aggregates[$depth + 1];
250
                            break;
251
252
                        case 'member':
253
                            // Next: struct, member
254
                            $nextExpectedElements = 0b000000000000000010001000000;
255
                            $aggregates[$depth - 1][$aggregates[$depth]['name']] = $aggregates[$depth][0];
256
                            unset($aggregates[$depth], $aggregates[$depth + 1]);
257
                            --$depth;
258
                            break;
259
                    }
260
                    break;
261
262
                case \XMLReader::TEXT:
263
                case \XMLReader::SIGNIFICANT_WHITESPACE:
264
                    switch ($type) {
0 ignored issues
show
Bug introduced by
The variable $type does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
265
                        case 'int':
266
                        case 'i4':
267
                        case 'i2':
268
                        case 'i1':
269
                            $value = (int) $xml->value;
270
                            break;
271
272
                        case 'boolean':
273
                            $value = $xml->value === '1';
274
                            break;
275
276
                        case 'double':
277
                        case 'float':
278
                        case 'bigdecimal':
279
                            $value = (float) $xml->value;
280
                            break;
281
282 View Code Duplication
                        case 'dateTime.iso8601':
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...
283
                            $value = \DateTime::createFromFormat(
284
                                'Ymd\TH:i:s',
285
                                $xml->value,
286
                                isset($timezone) ? $timezone : $timezone = new \DateTimeZone('UTC')
287
                            );
288
                            break;
289
290 View Code Duplication
                        case 'dateTime':
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...
291
                            $value = \DateTime::createFromFormat(
292
                                'Y-m-d\TH:i:s.uP',
293
                                $xml->value,
294
                                isset($timezone) ? $timezone : $timezone = new \DateTimeZone('UTC')
295
                            );
296
                            break;
297
298
                        case 'base64':
299
                            $value = Base64Value::deserialize($xml->value);
300
                            break;
301
302
                        case 'dom':
303
                            $doc = new \DOMDocument('1.0', 'UTF-8');
304
                            $doc->loadXML($aggregates[$depth + 1]);
305
                            $value = $doc;
306
                            break;
307
308
                        default:
309
                            $value = &$xml->value;
310
                            break;
311
                    }
312
313
                    $aggregates[$depth + 1] = $value;
314
                    if ($nextExpectedElements === null) {
315
                        break;
316
                    }
317
                    // Next: any
318
                    $nextExpectedElements = 0b111111111111111111111111111;
319
                    break;
320
            }
321
322
            if ($xml->isEmptyElement && $nodeType !== \XMLReader::END_ELEMENT) {
323
                $nodeType = \XMLReader::END_ELEMENT;
324
                goto processing;
325
            }
326
        }
327
328
        libxml_use_internal_errors($useErrors);
329
330
        return $aggregates ? array_pop($aggregates[0]) : null;
331
    }
332
}
333