JsonLinter::parse()   F
last analyzed

Complexity

Conditions 40
Paths 907

Size

Total Lines 250
Code Lines 119

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 250
rs 2
cc 40
eloc 119
nc 907
nop 2

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 namespace CMPayments\JsonLint;
2
3
use CMPayments\JsonLint\Exceptions\DuplicateKeyException;
4
use CMPayments\JsonLint\Exceptions\ParseException;
5
use CMPayments\JsonLint\Exceptions\UndefinedException;
6
use stdClass;
7
8
/**
9
 * Class JsonParser
10
 *
11
 * @package CMPayments\JsonLint
12
 */
13
class JsonLinter
14
{
15
    const DETECT_KEY_CONFLICTS = 1;
16
    const ALLOW_DUPLICATE_KEYS = 2;
17
    const PARSE_TO_ASSOC       = 4;
18
19
    /**
20
     * @var
21
     */
22
    private $lexer;
23
24
    /**
25
     * @var
26
     */
27
    private $flags;
28
29
    /**
30
     * @var
31
     */
32
    private $stack;
33
34
    /**
35
     * @var
36
     */
37
    private $vStack; // semantic value stack
38
39
    /**
40
     * @var
41
     */
42
    private $lStack; // location stack
43
44
    /**
45
     * @var array
46
     */
47
    private $symbols = [
48
        'error'              => 2,
49
        'JSONString'         => 3,
50
        'STRING'             => 4,
51
        'JSONNumber'         => 5,
52
        'NUMBER'             => 6,
53
        'JSONNullLiteral'    => 7,
54
        'NULL'               => 8,
55
        'JSONBooleanLiteral' => 9,
56
        'TRUE'               => 10,
57
        'FALSE'              => 11,
58
        'JSONText'           => 12,
59
        'JSONValue'          => 13,
60
        'EOF'                => 14,
61
        'JSONObject'         => 15,
62
        'JSONArray'          => 16,
63
        '{'                  => 17,
64
        '}'                  => 18,
65
        'JSONMemberList'     => 19,
66
        'JSONMember'         => 20,
67
        ':'                  => 21,
68
        ','                  => 22,
69
        '['                  => 23,
70
        ']'                  => 24,
71
        'JSONElementList'    => 25,
72
        '$accept'            => 0,
73
        '$end'               => 1
74
    ];
75
76
    /**
77
     * @var array
78
     */
79
    private $terminals = [2 => "error", 4 => "STRING", 6 => "NUMBER", 8 => "NULL", 10 => "TRUE", 11 => "FALSE", 14 => "EOF", 17 => "{", 18 => "}", 21 => ":", 22 => ",", 23 => "[", 24 => "]"];
80
81
    /**
82
     * @var array
83
     */
84
    private $productions = [0, [3, 1], [5, 1], [7, 1], [9, 1], [9, 1], [12, 2], [13, 1], [13, 1], [13, 1], [13, 1], [13, 1], [13, 1], [15, 2], [15, 3], [20, 3], [19, 1], [19, 3], [16, 2], [16, 3], [25, 1], [25, 3]];
85
86
    /**
87
     * @var array
88
     */
89
    private $table = [
90
        [3 => 5, 4 => [1, 12], 5 => 6, 6 => [1, 13], 7 => 3, 8 => [1, 9], 9 => 4, 10 => [1, 10], 11 => [1, 11], 12 => 1, 13 => 2, 15 => 7, 16 => 8, 17 => [1, 14], 23 => [1, 15]],
91
        [1 => [3]],
92
        [14 => [1, 16]],
93
        [14 => [2, 7], 18 => [2, 7], 22 => [2, 7], 24 => [2, 7]],
94
        [14 => [2, 8], 18 => [2, 8], 22 => [2, 8], 24 => [2, 8]],
95
        [14 => [2, 9], 18 => [2, 9], 22 => [2, 9], 24 => [2, 9]],
96
        [14 => [2, 10], 18 => [2, 10], 22 => [2, 10], 24 => [2, 10]],
97
        [14 => [2, 11], 18 => [2, 11], 22 => [2, 11], 24 => [2, 11]],
98
        [14 => [2, 12], 18 => [2, 12], 22 => [2, 12], 24 => [2, 12]],
99
        [14 => [2, 3], 18 => [2, 3], 22 => [2, 3], 24 => [2, 3]],
100
        [14 => [2, 4], 18 => [2, 4], 22 => [2, 4], 24 => [2, 4]],
101
        [14 => [2, 5], 18 => [2, 5], 22 => [2, 5], 24 => [2, 5]],
102
        [14 => [2, 1], 18 => [2, 1], 21 => [2, 1], 22 => [2, 1], 24 => [2, 1]],
103
        [14 => [2, 2], 18 => [2, 2], 22 => [2, 2], 24 => [2, 2]],
104
        [3 => 20, 4 => [1, 12], 18 => [1, 17], 19 => 18, 20 => 19],
105
        [3 => 5, 4 => [1, 12], 5 => 6, 6 => [1, 13], 7 => 3, 8 => [1, 9], 9 => 4, 10 => [1, 10], 11 => [1, 11], 13 => 23, 15 => 7, 16 => 8, 17 => [1, 14], 23 => [1, 15], 24 => [1, 21], 25 => 22],
106
        [1 => [2, 6]],
107
        [14 => [2, 13], 18 => [2, 13], 22 => [2, 13], 24 => [2, 13]],
108
        [18 => [1, 24], 22 => [1, 25]],
109
        [18 => [2, 16], 22 => [2, 16]],
110
        [21 => [1, 26]],
111
        [14 => [2, 18], 18 => [2, 18], 22 => [2, 18], 24 => [2, 18]],
112
        [22 => [1, 28], 24 => [1, 27]],
113
        [22 => [2, 20], 24 => [2, 20]],
114
        [14 => [2, 14], 18 => [2, 14], 22 => [2, 14], 24 => [2, 14]],
115
        [3 => 20, 4 => [1, 12], 20 => 29],
116
        [3 => 5, 4 => [1, 12], 5 => 6, 6 => [1, 13], 7 => 3, 8 => [1, 9], 9 => 4, 10 => [1, 10], 11 => [1, 11], 13 => 30, 15 => 7, 16 => 8, 17 => [1, 14], 23 => [1, 15]],
117
        [14 => [2, 19], 18 => [2, 19], 22 => [2, 19], 24 => [2, 19]],
118
        [3 => 5, 4 => [1, 12], 5 => 6, 6 => [1, 13], 7 => 3, 8 => [1, 9], 9 => 4, 10 => [1, 10], 11 => [1, 11], 13 => 31, 15 => 7, 16 => 8, 17 => [1, 14], 23 => [1, 15]],
119
        [18 => [2, 17], 22 => [2, 17]],
120
        [18 => [2, 15], 22 => [2, 15]],
121
        [22 => [2, 21], 24 => [2, 21]]
122
    ];
123
124
    /**
125
     * @var array
126
     */
127
    private $defaultActions = [16 => [2, 6]];
128
129
    /**
130
     * @param string $input JSON string
131
     * @param int    $flags
132
     *
133
     * @return boolean|ParseException null if no error is found, a ParseException containing all details otherwise
134
     */
135
    public function lint($input, $flags = 0)
136
    {
137
        try {
138
139
            $this->parse($input, $flags);
140
        } catch (\Exception $e) {
141
142
            return $e;
143
        }
144
145
        return null;
146
    }
147
148
    /**
149
     * @param string $input
150
     * @param int    $flags
151
     *
152
     * @return bool
153
     * @throws DuplicateKeyException
154
     * @throws ParseException
155
     * @throws null
156
     */
157
    public function parse($input, $flags = 0)
158
    {
159
        $this->lexer = new Lexer();
160
        $this->lexer->setInput($input);
161
162
        if (!is_string($input)) {
163
164
            $e = new ParseException(ParseException::ERROR_NOT_A_STRING, [gettype($input)]);
165
166
            // we +1 the line number on which an error occurred to make it human readable
167
            $e->setJsonLineNo(($this->lexer->yLineNo + 1));
168
169
            throw $e;
170
        }
171
172
        $this->failOnBOM($input);
173
174
        $this->flags = $flags;
175
176
        $this->stack  = [0];
177
        $this->vStack = [null];
178
        $this->lStack = [];
179
180
        $yText   = '';
181
        $yLineNo = $recovering = 0;
182
        $eof     = 1;
183
        $terror  = 2;
184
185
        $yLocation      = $this->lexer->yLocation;
186
        $this->lStack[] = $yLocation;
187
188
        $symbol = $preErrorSymbol = $p = null;
189
        $yVal   = new stdClass;
190
191
        $isMultiLine = count(explode("\n", str_replace("\n\r", "\n", $input))) > 1;
192
193
        while (true) {
194
195
            // retrieve state number from top of stack
196
            $state = $this->stack[count($this->stack) - 1];
197
198
            // use default actions if available
199
            if (isset($this->defaultActions[$state])) {
200
201
                $action = $this->defaultActions[$state];
202
            } else {
203
204
                if ($symbol === null) {
205
206
                    $symbol = $this->lex();
207
                }
208
209
                // read action for current state and first input
210
                $action = isset($this->table[$state][$symbol]) ? $this->table[$state][$symbol] : false;
211
            }
212
213
            // handle parse error
214
            if (!$action || !$action[0]) {
215
216
                $e = null;
217
218
                if (!$recovering) {
219
220
                    // Report error
221
                    $expected = [];
222
223
                    foreach ($this->table[$state] as $p => $ignore) {
224
225
                        if (isset($this->terminals[$p]) && $p > 2) {
226
227
                            $expected[] = $this->terminals[$p];
228
                        }
229
                    }
230
231
                    if (in_array("STRING", $expected) && in_array(substr($this->lexer->yText, 0, 1), ['"', "'"])) {
232
233
                        if (substr($this->lexer->yText, 0, 1) === "'") {
234
235
                            $e = new ParseException(ParseException::ERROR_USED_SINGLE_QUOTES, array_merge(['match' => $this->lexer->match], $this->getExceptionArguments($symbol)));
236
                        } elseif (preg_match('{".+?(\\\\[^"bfnrt/\\\\u])}', $this->lexer->getUpcomingInput())) {
237
238
                            $e = new ParseException(ParseException::ERROR_UNESCAPED_BACKSLASH, array_merge(['match' => $this->lexer->match], $this->getExceptionArguments($symbol)));
239
                        } elseif (preg_match('{"(?:[^"]+|\\\\")*$}m', $this->lexer->getUpcomingInput())) {
240
241
                            $e = new ParseException(ParseException::ERROR_NOT_TERMINATED_OR_MULTI_LINE, array_merge(['match' => $this->lexer->match], $this->getExceptionArguments($symbol)));
242
                        } else {
243
244
                            $e = new ParseException(ParseException::ERROR_INVALID_STRING, $this->getExceptionArguments($symbol));
245
                        }
246
                    }
247
248
                    if (is_null($e)) {
249
250
                        $e = new ParseException(ParseException::ERROR_EXPECTED_INPUT_TO_BE_SOMETHING_ELSE, array_merge(
251
                            [
252
                                ((count($expected) > 1) ? ' one of' : ''),
253
                                implode('\', \'', $expected),
254
                                utf8_encode($this->lexer->match) // encode any special characters that might be entered by the user
255
                            ], $this->getExceptionArguments($symbol)
256
                        ));
257
                    }
258
259
                    if (substr(trim($this->lexer->getPastInput()), -1) === ',') {
260
261
                        $e->appendMessage($e->getItemFromVariableArray(ParseException::ERROR_APPEND_TRAILING_COMMA_ERROR));
262
263
                        // usually we +1 the line number on which an error occurred to make it human readable
264
                        // BUT when it involves a trailing comma the error is located on the line above the current one
265
                        // IF however the $input IS multi line, we do NOT increase the line number
266
                        $e->setJsonLineNo((($isMultiLine) ? $this->lexer->yLineNo : ($this->lexer->yLineNo + 1)));
267
                    } else {
268
269
                        // we +1 the line number on which an error occurred to make it human readable
270
                        $e->setJsonLineNo(($this->lexer->yLineNo + 1));
271
                    }
272
273
                    $e->setJsonMatch($this->lexer->match);
274
                    $e->setJsonToken((!empty($this->terminals[$symbol]) ? $this->terminals[$symbol] : $symbol));
275
                    $e->setJsonColumnNo($this->lexer->yColumnNo);
276
                    $e->setJsonExpected($expected);
277
278
                    throw $e;
279
                }
280
281
                // just recovered from another error
282
                if ($recovering == 3) {
283
284
                    if ($symbol == $eof) {
285
286
                        if (is_null($e)) {
287
288
                            throw new ParseException(ParseException::ERROR_PARSING_HALTED);
289
                        }
290
                    }
291
292
                    // discard current lookahead and grab another
293
                    $yText   = $this->lexer->yText;
294
                    $yLineNo = $this->lexer->yLineNo;
295
                    $symbol  = $this->lex();
296
                }
297
298
                // try to recover from error
299
                while (true) {
300
301
                    // check for error recovery rule in this state
302
                    if (array_key_exists($terror, $this->table[$state])) {
303
304
                        break;
305
                    }
306
307
                    if ($state == 0) {
308
309
                        if (is_null($e)) {
310
311
                            throw new ParseException(ParseException::ERROR_PARSING_HALTED);
312
                        }
313
                    }
314
315
                    $this->popStack(1);
316
                    $state = $this->stack[count($this->stack) - 1];
317
                }
318
319
                // save the lookahead token
320
                $preErrorSymbol = $symbol;
321
322
                // insert generic error symbol as new lookahead
323
                $symbol = $terror;
324
325
                // allow 3 real symbols to be shifted before reporting a new error
326
                $recovering = 3;
327
                $state      = $this->stack[count($this->stack) - 1];
328
                $action     = isset($this->table[$state][$terror]) ? $this->table[$state][$terror] : false;
329
            }
330
331
            // this shouldn't happen, unless resolve defaults are off
332
            if (is_array($action[0]) && is_array($action) && count($action) > 1) {
333
334
                throw new ParseException(ParseException::ERROR_PARSING_ERROR_MULTIPLE_ACTIONS, [$state, $symbol]);
335
            }
336
337
            switch ($action[0]) {
338
339
                // shift
340
                case 1:
341
                    $this->stack[]  = $symbol;
342
                    $this->vStack[] = $this->lexer->yText;
343
                    $this->lStack[] = $this->lexer->yLocation;
344
                    $this->stack[]  = $action[1]; // push state
345
                    $symbol         = null;
346
347
                    // normal execution/no error
348
                    if ($preErrorSymbol === null) {
349
350
                        $yText   = $this->lexer->yText;
351
                        $yLineNo = $this->lexer->yLineNo;
352
353
                        if ($recovering > 0) {
354
355
                            $recovering--;
356
                        }
357
                    } else {
358
359
                        // error just occurred, resume old lookahead f/ before error
360
                        $symbol         = $preErrorSymbol;
361
                        $preErrorSymbol = null;
362
                    }
363
                    break;
364
365
                // reduce
366
                case 2:
367
                    $len = $this->productions[$action[1]][1];
368
369
                    // perform semantic action
370
                    $yVal->token = $this->vStack[count($this->vStack) - $len];
371
372
                    // default location, uses first token for firsts, last for lasts
373
                    $yVal->store = [
374
                        'last_line'   => $this->lStack[count($this->lStack) - 1]['last_line'],
375
                        'last_column' => $this->lStack[count($this->lStack) - 1]['last_column'],
376
                    ];
377
378
                    $result = $this->performAction($yVal, $yText, $yLineNo, $action[1], $this->vStack);
379
380
                    if (!$result instanceof UndefinedException) {
381
382
                        return $result;
383
                    }
384
385
                    if ($len) {
386
387
                        $this->popStack($len);
388
                    }
389
390
                    // push non terminal (reduce)
391
                    $this->stack[]  = $this->productions[$action[1]][0];
392
                    $this->vStack[] = $yVal->token;
393
                    $this->lStack[] = $yVal->store;
394
                    $newState       = $this->table[$this->stack[count($this->stack) - 2]][$this->stack[count($this->stack) - 1]];
395
                    $this->stack[]  = $newState;
396
                    break;
397
398
                // accept
399
                case 3:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
400
401
                    return true;
402
            }
403
        }
404
405
        return true;
406
    }
407
408
    /**
409
     * @param null|string $symbol
410
     *
411
     * @return array
412
     */
413
    private function getExceptionArguments($symbol = null)
414
    {
415
        $arr = [
416
            'lineNo'   => ($this->lexer->yLineNo + 1),
417
            'columnNo' => $this->lexer->yColumnNo,
418
            'match'    => $this->lexer->match
419
        ];
420
421
        if (!is_null($symbol)) {
422
423
            $arr['token'] = (!empty($this->terminals[$symbol]) ? $this->terminals[$symbol] : $symbol);
424
        }
425
426
        return $arr;
427
    }
428
429
    /**
430
     * @param stdClass $yVal
431
     * @param string   $yText
432
     * @param int      $yLineNo
433
     * @param int      $yState
434
     * @param array    $tokens
435
     *
436
     * @return UndefinedException
437
     * @throws DuplicateKeyException
438
     */
439
    private function performAction(stdClass $yVal, $yText, $yLineNo, $yState, &$tokens)
440
    {
441
        $len = count($tokens) - 1;
442
443
        switch ($yState) {
444
445
            case 1:
446
                $yText       = preg_replace_callback('{(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4})}', [$this, 'stringInterpolation'], $yText);
447
                $yVal->token = $yText;
448
                break;
449
450
            case 2:
451
                if (strpos($yText, 'e') !== false || strpos($yText, 'E') !== false) {
452
453
                    $yVal->token = floatval($yText);
454
                } else {
455
456
                    $yVal->token = strpos($yText, '.') === false ? intval($yText) : floatval($yText);
457
                }
458
                break;
459
460
            case 3:
461
                $yVal->token = null;
462
                break;
463
464
            case 4:
465
                $yVal->token = true;
466
                break;
467
468
            case 5:
469
                $yVal->token = false;
470
                break;
471
472
            case 6:
473
                return $yVal->token = $tokens[$len - 1];
474
475
            case 13:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
476
477
                if ($this->flags & self::PARSE_TO_ASSOC) {
478
479
                    $yVal->token = [];
480
                } else {
481
482
                    $yVal->token = new stdClass;
483
                }
484
                break;
485
486
            case 14:
487
                $yVal->token = $tokens[$len - 1];
488
                break;
489
490
            case 15:
491
                $yVal->token = [$tokens[$len - 2], $tokens[$len]];
492
                break;
493
494
            case 16:
495
                $property = $tokens[$len][0] === '' ? '_empty_' : $tokens[$len][0];
496
497
                if ($this->flags & self::PARSE_TO_ASSOC) {
498
499
                    $yVal->token            = [];
500
                    $yVal->token[$property] = $tokens[$len][1];
501
                } else {
502
503
                    $yVal->token            = new stdClass;
504
                    $yVal->token->$property = $tokens[$len][1];
505
                }
506
                break;
507
508
            case 17:
509
                if ($this->flags & self::PARSE_TO_ASSOC) {
510
511
                    $yVal->token = & $tokens[$len - 2];
512
                    $key         = $tokens[$len][0];
513
514
                    if (($this->flags & self::DETECT_KEY_CONFLICTS) && isset($tokens[$len - 2][$key])) {
515
516
                        $args = ['line' => ($yLineNo + 1), 'key' => $tokens[$len][0]];
517
                        $e    = new DuplicateKeyException(DuplicateKeyException::ERROR_PARSE_ERROR_DUPLICATE_KEY, $tokens[$len][0], $args);
518
519
                        $e->setJsonLineNo($args['line']);
520
                        $e->setJsonMatch($args['key']);
521
                        $e->setJsonColumnNo($this->lexer->yColumnNo);
522
523
                        throw $e;
524 View Code Duplication
                    } elseif (($this->flags & self::ALLOW_DUPLICATE_KEYS) && isset($tokens[$len - 2][$key])) {
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...
525
526
                        $duplicateCount = 1;
527
528
                        do {
529
530
                            $duplicateKey = $key . '.' . $duplicateCount++;
531
                        } while (isset($tokens[$len - 2][$duplicateKey]));
532
533
                        $key = $duplicateKey;
534
                    }
535
536
                    $tokens[$len - 2][$key] = $tokens[$len][1];
537
                } else {
538
539
                    $yVal->token = $tokens[$len - 2];
540
                    $key         = $tokens[$len][0] === '' ? '_empty_' : $tokens[$len][0];
541
542
                    if (($this->flags & self::DETECT_KEY_CONFLICTS) && isset($tokens[$len - 2]->{$key})) {
543
544
                        $args = ['line' => ($yLineNo + 1), 'key' => $tokens[$len][0]];
545
                        $e    = new DuplicateKeyException(DuplicateKeyException::ERROR_PARSE_ERROR_DUPLICATE_KEY, $tokens[$len][0], $args);
546
547
                        $e->setJsonLineNo($args['line']);
548
                        $e->setJsonMatch($args['key']);
549
                        $e->setJsonColumnNo($this->lexer->yColumnNo);
550
551
                        throw $e;
552 View Code Duplication
                    } elseif (($this->flags & self::ALLOW_DUPLICATE_KEYS) && isset($tokens[$len - 2]->{$key})) {
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...
553
554
                        $duplicateCount = 1;
555
556
                        do {
557
558
                            $duplicateKey = $key . '.' . $duplicateCount++;
559
                        } while (isset($tokens[$len - 2]->$duplicateKey));
560
561
                        $key = $duplicateKey;
562
                    }
563
564
                    $tokens[$len - 2]->$key = $tokens[$len][1];
565
                }
566
                break;
567
568
            case 18:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
569
570
                $yVal->token = [];
571
                break;
572
573
            case 19:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
574
575
                $yVal->token = $tokens[$len - 1];
576
                break;
577
578
            case 20:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
579
580
                $yVal->token = [$tokens[$len]];
581
                break;
582
583
            case 21:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
584
585
                $tokens[$len - 2][] = $tokens[$len];
586
                $yVal->token        = $tokens[$len - 2];
587
                break;
588
        }
589
590
        return new UndefinedException(UndefinedException::ERROR_UNDEFINED_VALIDATION);
591
    }
592
593
    /**
594
     * alter $match[0] to a new value
595
     *
596
     * @param array $match
597
     *
598
     * @return string
599
     */
600
    private function stringInterpolation($match)
601
    {
602
        switch ($match[0]) {
603
604
            case '\\\\':
605
                return '\\';
606
607
            case '\"':
608
                return '"';
609
610
            case '\b':
611
                return chr(8);
612
613
            case '\f':
614
                return chr(12);
615
616
            case '\n':
617
                return "\n";
618
619
            case '\r':
620
                return "\r";
621
622
            case '\t':
623
                return "\t";
624
625
            case '\/':
626
                return "/";
627
628
            default:
629
                return html_entity_decode('&#x' . ltrim(substr($match[0], 2), '0') . ';', 0, 'UTF-8');
630
        }
631
    }
632
633
    /**
634
     * Returns the sequence of elements from the arrays as specified by the offset and length ($n) parameters.
635
     *
636
     * @param int $n
637
     */
638
    private function popStack($n)
639
    {
640
        $this->stack  = array_slice($this->stack, 0, -(2 * $n));
641
        $this->vStack = array_slice($this->vStack, 0, -$n);
642
        $this->lStack = array_slice($this->lStack, 0, -$n);
643
    }
644
645
    /**
646
     * @return int
647
     */
648
    private function lex()
649
    {
650
        $token = $this->lexer->lex() ?: 1;
651
652
        // if token isn't its numeric value, convert
653
        if (!is_numeric($token)) {
654
655
            $token = isset($this->symbols[$token]) ? $this->symbols[$token] : $token;
656
        }
657
658
        return $token;
659
    }
660
661
    /**
662
     * The byte order mark (BOM) is a Unicode character, which is not allowed
663
     *
664
     * @param string $input
665
     *
666
     * @throws ParseException
667
     */
668
    private function failOnBOM($input)
669
    {
670
        // UTF-8 ByteOrderMark sequence
671
        $bom = "\xEF\xBB\xBF";
672
673
        if (substr($input, 0, 3) === $bom) {
674
675
            throw new ParseException(ParseException::ERROR_BYTE_ORDER_MARK_DETECTED);
676
        }
677
    }
678
}
679