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

XmlReaderParser::parse()   D

Complexity

Conditions 83
Paths 2

Size

Total Lines 313
Code Lines 232

Duplication

Lines 53
Ratio 16.93 %

Code Coverage

Tests 0
CRAP Score 6972

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 53
loc 313
ccs 0
cts 249
cp 0
rs 4.1818
cc 83
eloc 232
nc 2
nop 2
crap 6972

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
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