Completed
Branch development (b1b115)
by Johannes
10:28
created

PHP   F

Complexity

Total Complexity 331

Size/Duplication

Total Lines 1959
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 331
c 0
b 0
f 0
dl 0
loc 1959
rs 0.8

How to fix   Complexity   

Complex Class

Complex classes like PHP 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 PHP, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Tokenizes PHP code.
4
 *
5
 * @author    Greg Sherwood <[email protected]>
6
 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
7
 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8
 */
9
10
namespace PHP_CodeSniffer\Tokenizers;
11
12
use PHP_CodeSniffer\Util;
13
14
class PHP extends Tokenizer
15
{
16
17
18
    /**
19
     * A list of tokens that are allowed to open a scope.
20
     *
21
     * This array also contains information about what kind of token the scope
22
     * opener uses to open and close the scope, if the token strictly requires
23
     * an opener, if the token can share a scope closer, and who it can be shared
24
     * with. An example of a token that shares a scope closer is a CASE scope.
25
     *
26
     * @var array
27
     */
28
    public $scopeOpeners = [
29
        T_IF            => [
30
            'start'  => [
31
                T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
32
                T_COLON              => T_COLON,
33
            ],
34
            'end'    => [
35
                T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
36
                T_ENDIF               => T_ENDIF,
37
                T_ELSE                => T_ELSE,
38
                T_ELSEIF              => T_ELSEIF,
39
            ],
40
            'strict' => false,
41
            'shared' => false,
42
            'with'   => [
43
                T_ELSE   => T_ELSE,
44
                T_ELSEIF => T_ELSEIF,
45
            ],
46
        ],
47
        T_TRY           => [
48
            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
49
            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
50
            'strict' => true,
51
            'shared' => false,
52
            'with'   => [],
53
        ],
54
        T_CATCH         => [
55
            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
56
            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
57
            'strict' => true,
58
            'shared' => false,
59
            'with'   => [],
60
        ],
61
        T_FINALLY       => [
62
            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
63
            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
64
            'strict' => true,
65
            'shared' => false,
66
            'with'   => [],
67
        ],
68
        T_ELSE          => [
69
            'start'  => [
70
                T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
71
                T_COLON              => T_COLON,
72
            ],
73
            'end'    => [
74
                T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
75
                T_ENDIF               => T_ENDIF,
76
            ],
77
            'strict' => false,
78
            'shared' => false,
79
            'with'   => [
80
                T_IF     => T_IF,
81
                T_ELSEIF => T_ELSEIF,
82
            ],
83
        ],
84
        T_ELSEIF        => [
85
            'start'  => [
86
                T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
87
                T_COLON              => T_COLON,
88
            ],
89
            'end'    => [
90
                T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
91
                T_ENDIF               => T_ENDIF,
92
                T_ELSE                => T_ELSE,
93
                T_ELSEIF              => T_ELSEIF,
94
            ],
95
            'strict' => false,
96
            'shared' => false,
97
            'with'   => [
98
                T_IF   => T_IF,
99
                T_ELSE => T_ELSE,
100
            ],
101
        ],
102
        T_FOR           => [
103
            'start'  => [
104
                T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
105
                T_COLON              => T_COLON,
106
            ],
107
            'end'    => [
108
                T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
109
                T_ENDFOR              => T_ENDFOR,
110
            ],
111
            'strict' => false,
112
            'shared' => false,
113
            'with'   => [],
114
        ],
115
        T_FOREACH       => [
116
            'start'  => [
117
                T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
118
                T_COLON              => T_COLON,
119
            ],
120
            'end'    => [
121
                T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
122
                T_ENDFOREACH          => T_ENDFOREACH,
123
            ],
124
            'strict' => false,
125
            'shared' => false,
126
            'with'   => [],
127
        ],
128
        T_INTERFACE     => [
129
            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
130
            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
131
            'strict' => true,
132
            'shared' => false,
133
            'with'   => [],
134
        ],
135
        T_FUNCTION      => [
136
            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
137
            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
138
            'strict' => true,
139
            'shared' => false,
140
            'with'   => [],
141
        ],
142
        T_CLASS         => [
143
            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
144
            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
145
            'strict' => true,
146
            'shared' => false,
147
            'with'   => [],
148
        ],
149
        T_TRAIT         => [
150
            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
151
            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
152
            'strict' => true,
153
            'shared' => false,
154
            'with'   => [],
155
        ],
156
        T_USE           => [
157
            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
158
            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
159
            'strict' => false,
160
            'shared' => false,
161
            'with'   => [],
162
        ],
163
        T_DECLARE       => [
164
            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
165
            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
166
            'strict' => false,
167
            'shared' => false,
168
            'with'   => [],
169
        ],
170
        T_NAMESPACE     => [
171
            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
172
            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
173
            'strict' => false,
174
            'shared' => false,
175
            'with'   => [],
176
        ],
177
        T_WHILE         => [
178
            'start'  => [
179
                T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
180
                T_COLON              => T_COLON,
181
            ],
182
            'end'    => [
183
                T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
184
                T_ENDWHILE            => T_ENDWHILE,
185
            ],
186
            'strict' => false,
187
            'shared' => false,
188
            'with'   => [],
189
        ],
190
        T_DO            => [
191
            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
192
            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
193
            'strict' => true,
194
            'shared' => false,
195
            'with'   => [],
196
        ],
197
        T_SWITCH        => [
198
            'start'  => [
199
                T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
200
                T_COLON              => T_COLON,
201
            ],
202
            'end'    => [
203
                T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
204
                T_ENDSWITCH           => T_ENDSWITCH,
205
            ],
206
            'strict' => true,
207
            'shared' => false,
208
            'with'   => [],
209
        ],
210
        T_CASE          => [
211
            'start'  => [
212
                T_COLON     => T_COLON,
213
                T_SEMICOLON => T_SEMICOLON,
214
            ],
215
            'end'    => [
216
                T_BREAK    => T_BREAK,
217
                T_RETURN   => T_RETURN,
218
                T_CONTINUE => T_CONTINUE,
219
                T_THROW    => T_THROW,
220
                T_EXIT     => T_EXIT,
221
            ],
222
            'strict' => true,
223
            'shared' => true,
224
            'with'   => [
225
                T_DEFAULT => T_DEFAULT,
226
                T_CASE    => T_CASE,
227
                T_SWITCH  => T_SWITCH,
228
            ],
229
        ],
230
        T_DEFAULT       => [
231
            'start'  => [
232
                T_COLON     => T_COLON,
233
                T_SEMICOLON => T_SEMICOLON,
234
            ],
235
            'end'    => [
236
                T_BREAK    => T_BREAK,
237
                T_RETURN   => T_RETURN,
238
                T_CONTINUE => T_CONTINUE,
239
                T_THROW    => T_THROW,
240
                T_EXIT     => T_EXIT,
241
            ],
242
            'strict' => true,
243
            'shared' => true,
244
            'with'   => [
245
                T_CASE   => T_CASE,
246
                T_SWITCH => T_SWITCH,
247
            ],
248
        ],
249
        T_START_HEREDOC => [
250
            'start'  => [T_START_HEREDOC => T_START_HEREDOC],
251
            'end'    => [T_END_HEREDOC => T_END_HEREDOC],
252
            'strict' => true,
253
            'shared' => false,
254
            'with'   => [],
255
        ],
256
        T_START_NOWDOC  => [
257
            'start'  => [T_START_NOWDOC => T_START_NOWDOC],
258
            'end'    => [T_END_NOWDOC => T_END_NOWDOC],
259
            'strict' => true,
260
            'shared' => false,
261
            'with'   => [],
262
        ],
263
    ];
264
265
    /**
266
     * A list of tokens that end the scope.
267
     *
268
     * This array is just a unique collection of the end tokens
269
     * from the _scopeOpeners array. The data is duplicated here to
270
     * save time during parsing of the file.
271
     *
272
     * @var array
273
     */
274
    public $endScopeTokens = [
275
        T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
276
        T_ENDIF               => T_ENDIF,
277
        T_ENDFOR              => T_ENDFOR,
278
        T_ENDFOREACH          => T_ENDFOREACH,
279
        T_ENDWHILE            => T_ENDWHILE,
280
        T_ENDSWITCH           => T_ENDSWITCH,
281
        T_BREAK               => T_BREAK,
282
        T_END_HEREDOC         => T_END_HEREDOC,
283
    ];
284
285
    /**
286
     * Known lengths of tokens.
287
     *
288
     * @var array<int, int>
289
     */
290
    public $knownLengths = [
291
        T_ABSTRACT                 => 8,
292
        T_AND_EQUAL                => 2,
293
        T_ARRAY                    => 5,
294
        T_AS                       => 2,
295
        T_BOOLEAN_AND              => 2,
296
        T_BOOLEAN_OR               => 2,
297
        T_BREAK                    => 5,
298
        T_CALLABLE                 => 8,
299
        T_CASE                     => 4,
300
        T_CATCH                    => 5,
301
        T_CLASS                    => 5,
302
        T_CLASS_C                  => 9,
303
        T_CLONE                    => 5,
304
        T_CONCAT_EQUAL             => 2,
305
        T_CONST                    => 5,
306
        T_CONTINUE                 => 8,
307
        T_CURLY_OPEN               => 2,
308
        T_DEC                      => 2,
309
        T_DECLARE                  => 7,
310
        T_DEFAULT                  => 7,
311
        T_DIR                      => 7,
312
        T_DIV_EQUAL                => 2,
313
        T_DO                       => 2,
314
        T_DOLLAR_OPEN_CURLY_BRACES => 2,
315
        T_DOUBLE_ARROW             => 2,
316
        T_DOUBLE_COLON             => 2,
317
        T_ECHO                     => 4,
318
        T_ELSE                     => 4,
319
        T_ELSEIF                   => 6,
320
        T_EMPTY                    => 5,
321
        T_ENDDECLARE               => 10,
322
        T_ENDFOR                   => 6,
323
        T_ENDFOREACH               => 10,
324
        T_ENDIF                    => 5,
325
        T_ENDSWITCH                => 9,
326
        T_ENDWHILE                 => 8,
327
        T_EVAL                     => 4,
328
        T_EXTENDS                  => 7,
329
        T_FILE                     => 8,
330
        T_FINAL                    => 5,
331
        T_FINALLY                  => 7,
332
        T_FOR                      => 3,
333
        T_FOREACH                  => 7,
334
        T_FUNCTION                 => 8,
335
        T_FUNC_C                   => 12,
336
        T_GLOBAL                   => 6,
337
        T_GOTO                     => 4,
338
        T_HALT_COMPILER            => 15,
339
        T_IF                       => 2,
340
        T_IMPLEMENTS               => 10,
341
        T_INC                      => 2,
342
        T_INCLUDE                  => 7,
343
        T_INCLUDE_ONCE             => 12,
344
        T_INSTANCEOF               => 10,
345
        T_INSTEADOF                => 9,
346
        T_INTERFACE                => 9,
347
        T_ISSET                    => 5,
348
        T_IS_EQUAL                 => 2,
349
        T_IS_GREATER_OR_EQUAL      => 2,
350
        T_IS_IDENTICAL             => 3,
351
        T_IS_NOT_EQUAL             => 2,
352
        T_IS_NOT_IDENTICAL         => 3,
353
        T_IS_SMALLER_OR_EQUAL      => 2,
354
        T_LINE                     => 8,
355
        T_LIST                     => 4,
356
        T_LOGICAL_AND              => 3,
357
        T_LOGICAL_OR               => 2,
358
        T_LOGICAL_XOR              => 3,
359
        T_METHOD_C                 => 10,
360
        T_MINUS_EQUAL              => 2,
361
        T_POW_EQUAL                => 3,
362
        T_MOD_EQUAL                => 2,
363
        T_MUL_EQUAL                => 2,
364
        T_NAMESPACE                => 9,
365
        T_NS_C                     => 13,
366
        T_NS_SEPARATOR             => 1,
367
        T_NEW                      => 3,
368
        T_OBJECT_OPERATOR          => 2,
369
        T_OPEN_TAG_WITH_ECHO       => 3,
370
        T_OR_EQUAL                 => 2,
371
        T_PLUS_EQUAL               => 2,
372
        T_PRINT                    => 5,
373
        T_PRIVATE                  => 7,
374
        T_PUBLIC                   => 6,
375
        T_PROTECTED                => 9,
376
        T_REQUIRE                  => 7,
377
        T_REQUIRE_ONCE             => 12,
378
        T_RETURN                   => 6,
379
        T_STATIC                   => 6,
380
        T_SWITCH                   => 6,
381
        T_THROW                    => 5,
382
        T_TRAIT                    => 5,
383
        T_TRAIT_C                  => 9,
384
        T_TRY                      => 3,
385
        T_UNSET                    => 5,
386
        T_USE                      => 3,
387
        T_VAR                      => 3,
388
        T_WHILE                    => 5,
389
        T_XOR_EQUAL                => 2,
390
        T_YIELD                    => 5,
391
        T_OPEN_CURLY_BRACKET       => 1,
392
        T_CLOSE_CURLY_BRACKET      => 1,
393
        T_OPEN_SQUARE_BRACKET      => 1,
394
        T_CLOSE_SQUARE_BRACKET     => 1,
395
        T_OPEN_PARENTHESIS         => 1,
396
        T_CLOSE_PARENTHESIS        => 1,
397
        T_COLON                    => 1,
398
        T_STRING_CONCAT            => 1,
399
        T_INLINE_THEN              => 1,
400
        T_INLINE_ELSE              => 1,
401
        T_NULLABLE                 => 1,
402
        T_NULL                     => 4,
403
        T_FALSE                    => 5,
404
        T_TRUE                     => 4,
405
        T_SEMICOLON                => 1,
406
        T_EQUAL                    => 1,
407
        T_MULTIPLY                 => 1,
408
        T_DIVIDE                   => 1,
409
        T_PLUS                     => 1,
410
        T_MINUS                    => 1,
411
        T_MODULUS                  => 1,
412
        T_POW                      => 2,
413
        T_SPACESHIP                => 3,
414
        T_COALESCE                 => 2,
415
        T_COALESCE_EQUAL           => 3,
416
        T_BITWISE_AND              => 1,
417
        T_BITWISE_OR               => 1,
418
        T_BITWISE_XOR              => 1,
419
        T_SL                       => 2,
420
        T_SR                       => 2,
421
        T_SL_EQUAL                 => 3,
422
        T_SR_EQUAL                 => 3,
423
        T_ARRAY_HINT               => 5,
424
        T_GREATER_THAN             => 1,
425
        T_LESS_THAN                => 1,
426
        T_BOOLEAN_NOT              => 1,
427
        T_SELF                     => 4,
428
        T_PARENT                   => 6,
429
        T_COMMA                    => 1,
430
        T_THIS                     => 4,
431
        T_CLOSURE                  => 8,
432
        T_BACKTICK                 => 1,
433
        T_OPEN_SHORT_ARRAY         => 1,
434
        T_CLOSE_SHORT_ARRAY        => 1,
435
    ];
436
437
438
    /**
439
     * A cache of different token types, resolved into arrays.
440
     *
441
     * @var array
442
     * @see standardiseToken()
443
     */
444
    private static $resolveTokenCache = [];
445
446
447
    /**
448
     * Creates an array of tokens when given some PHP code.
449
     *
450
     * Starts by using token_get_all() but does a lot of extra processing
451
     * to insert information about the context of the token.
452
     *
453
     * @param string $string The string to tokenize.
454
     *
455
     * @return array
456
     */
457
    protected function tokenize($string)
458
    {
459
        if (PHP_CODESNIFFER_VERBOSITY > 1) {
460
            echo "\t*** START PHP TOKENIZING ***".PHP_EOL;
461
            $isWin = false;
462
            if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
463
                $isWin = true;
464
            }
465
        }
466
467
        $tokens      = @token_get_all($string);
468
        $finalTokens = [];
469
470
        $newStackPtr       = 0;
471
        $numTokens         = count($tokens);
472
        $lastNotEmptyToken = 0;
473
474
        $insideInlineIf = [];
475
        $insideUseGroup = false;
476
477
        $commentTokenizer = new Comment();
478
479
        for ($stackPtr = 0; $stackPtr < $numTokens; $stackPtr++) {
480
            $token        = (array) $tokens[$stackPtr];
481
            $tokenIsArray = isset($token[1]);
482
483
            if (PHP_CODESNIFFER_VERBOSITY > 1) {
484
                if ($tokenIsArray === true) {
485
                    $type    = Util\Tokens::tokenName($token[0]);
486
                    $content = Util\Common::prepareForOutput($token[1]);
487
                } else {
488
                    $newToken = self::resolveSimpleToken($token[0]);
489
                    $type     = $newToken['type'];
490
                    $content  = Util\Common::prepareForOutput($token[0]);
491
                }
492
493
                echo "\tProcess token ";
494
                if ($tokenIsArray === true) {
495
                    echo "[$stackPtr]";
496
                } else {
497
                    echo " $stackPtr ";
498
                }
499
500
                echo ": $type => $content";
501
            }//end if
502
503
            if ($newStackPtr > 0 && $finalTokens[($newStackPtr - 1)]['code'] !== T_WHITESPACE) {
504
                $lastNotEmptyToken = ($newStackPtr - 1);
505
            }
506
507
            /*
508
                If we are using \r\n newline characters, the \r and \n are sometimes
509
                split over two tokens. This normally occurs after comments. We need
510
                to merge these two characters together so that our line endings are
511
                consistent for all lines.
512
            */
513
514
            if ($tokenIsArray === true && substr($token[1], -1) === "\r") {
515
                if (isset($tokens[($stackPtr + 1)]) === true
516
                    && is_array($tokens[($stackPtr + 1)]) === true
517
                    && $tokens[($stackPtr + 1)][1][0] === "\n"
518
                ) {
519
                    $token[1] .= "\n";
520
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
521
                        if ($isWin === true) {
522
                            echo '\n';
523
                        } else {
524
                            echo "\033[30;1m\\n\033[0m";
525
                        }
526
                    }
527
528
                    if ($tokens[($stackPtr + 1)][1] === "\n") {
529
                        // This token's content has been merged into the previous,
530
                        // so we can skip it.
531
                        $tokens[($stackPtr + 1)] = '';
532
                    } else {
533
                        $tokens[($stackPtr + 1)][1] = substr($tokens[($stackPtr + 1)][1], 1);
534
                    }
535
                }
536
            }//end if
537
538
            if (PHP_CODESNIFFER_VERBOSITY > 1) {
539
                echo PHP_EOL;
540
            }
541
542
            /*
543
                Parse doc blocks into something that can be easily iterated over.
544
            */
545
546
            if ($tokenIsArray === true
547
                && ($token[0] === T_DOC_COMMENT
548
                || ($token[0] === T_COMMENT && strpos($token[1], '/**') === 0))
549
            ) {
550
                $commentTokens = $commentTokenizer->tokenizeString($token[1], $this->eolChar, $newStackPtr);
551
                foreach ($commentTokens as $commentToken) {
552
                    $finalTokens[$newStackPtr] = $commentToken;
553
                    $newStackPtr++;
554
                }
555
556
                continue;
557
            }
558
559
            /*
560
                If this is a double quoted string, PHP will tokenize the whole
561
                thing which causes problems with the scope map when braces are
562
                within the string. So we need to merge the tokens together to
563
                provide a single string.
564
            */
565
566
            if ($tokenIsArray === false && ($token[0] === '"' || $token[0] === 'b"')) {
567
                // Binary casts need a special token.
568
                if ($token[0] === 'b"') {
569
                    $finalTokens[$newStackPtr] = [
570
                        'code'    => T_BINARY_CAST,
571
                        'type'    => 'T_BINARY_CAST',
572
                        'content' => 'b',
573
                    ];
574
                    $newStackPtr++;
575
                }
576
577
                $tokenContent = '"';
578
                $nestedVars   = [];
579
                for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
580
                    $subToken        = (array) $tokens[$i];
581
                    $subTokenIsArray = isset($subToken[1]);
582
583
                    if ($subTokenIsArray === true) {
584
                        $tokenContent .= $subToken[1];
585
                        if ($subToken[1] === '{'
586
                            && $subToken[0] !== T_ENCAPSED_AND_WHITESPACE
587
                        ) {
588
                            $nestedVars[] = $i;
589
                        }
590
                    } else {
591
                        $tokenContent .= $subToken[0];
592
                        if ($subToken[0] === '}') {
593
                            array_pop($nestedVars);
594
                        }
595
                    }
596
597
                    if ($subTokenIsArray === false
598
                        && $subToken[0] === '"'
599
                        && empty($nestedVars) === true
600
                    ) {
601
                        // We found the other end of the double quoted string.
602
                        break;
603
                    }
604
                }//end for
605
606
                $stackPtr = $i;
607
608
                // Convert each line within the double quoted string to a
609
                // new token, so it conforms with other multiple line tokens.
610
                $tokenLines = explode($this->eolChar, $tokenContent);
611
                $numLines   = count($tokenLines);
612
                $newToken   = [];
613
614
                for ($j = 0; $j < $numLines; $j++) {
615
                    $newToken['content'] = $tokenLines[$j];
616
                    if ($j === ($numLines - 1)) {
617
                        if ($tokenLines[$j] === '') {
618
                            break;
619
                        }
620
                    } else {
621
                        $newToken['content'] .= $this->eolChar;
622
                    }
623
624
                    $newToken['code']          = T_DOUBLE_QUOTED_STRING;
625
                    $newToken['type']          = 'T_DOUBLE_QUOTED_STRING';
626
                    $finalTokens[$newStackPtr] = $newToken;
627
                    $newStackPtr++;
628
                }
629
630
                // Continue, as we're done with this token.
631
                continue;
632
            }//end if
633
634
            /*
635
                If this is a heredoc, PHP will tokenize the whole
636
                thing which causes problems when heredocs don't
637
                contain real PHP code, which is almost never.
638
                We want to leave the start and end heredoc tokens
639
                alone though.
640
            */
641
642
            if ($tokenIsArray === true && $token[0] === T_START_HEREDOC) {
643
                // Add the start heredoc token to the final array.
644
                $finalTokens[$newStackPtr] = self::standardiseToken($token);
645
646
                // Check if this is actually a nowdoc and use a different token
647
                // to help the sniffs.
648
                $nowdoc = false;
649
                if ($token[1][3] === "'") {
650
                    $finalTokens[$newStackPtr]['code'] = T_START_NOWDOC;
651
                    $finalTokens[$newStackPtr]['type'] = 'T_START_NOWDOC';
652
                    $nowdoc = true;
653
                }
654
655
                $tokenContent = '';
656
                for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
657
                    $subTokenIsArray = is_array($tokens[$i]);
658
                    if ($subTokenIsArray === true
659
                        && $tokens[$i][0] === T_END_HEREDOC
660
                    ) {
661
                        // We found the other end of the heredoc.
662
                        break;
663
                    }
664
665
                    if ($subTokenIsArray === true) {
666
                        $tokenContent .= $tokens[$i][1];
667
                    } else {
668
                        $tokenContent .= $tokens[$i];
669
                    }
670
                }
671
672
                if ($i === $numTokens) {
673
                    // We got to the end of the file and never
674
                    // found the closing token, so this probably wasn't
675
                    // a heredoc.
676
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
677
                        $type = $finalTokens[$newStackPtr]['type'];
678
                        echo "\t\t* failed to find the end of the here/nowdoc".PHP_EOL;
679
                        echo "\t\t* token $stackPtr changed from $type to T_STRING".PHP_EOL;
680
                    }
681
682
                    $finalTokens[$newStackPtr]['code'] = T_STRING;
683
                    $finalTokens[$newStackPtr]['type'] = 'T_STRING';
684
                    $newStackPtr++;
685
                    continue;
686
                }
687
688
                $stackPtr = $i;
689
                $newStackPtr++;
690
691
                // Convert each line within the heredoc to a
692
                // new token, so it conforms with other multiple line tokens.
693
                $tokenLines = explode($this->eolChar, $tokenContent);
694
                $numLines   = count($tokenLines);
695
                $newToken   = [];
696
697
                for ($j = 0; $j < $numLines; $j++) {
698
                    $newToken['content'] = $tokenLines[$j];
699
                    if ($j === ($numLines - 1)) {
700
                        if ($tokenLines[$j] === '') {
701
                            break;
702
                        }
703
                    } else {
704
                        $newToken['content'] .= $this->eolChar;
705
                    }
706
707
                    if ($nowdoc === true) {
708
                        $newToken['code'] = T_NOWDOC;
709
                        $newToken['type'] = 'T_NOWDOC';
710
                    } else {
711
                        $newToken['code'] = T_HEREDOC;
712
                        $newToken['type'] = 'T_HEREDOC';
713
                    }
714
715
                    $finalTokens[$newStackPtr] = $newToken;
716
                    $newStackPtr++;
717
                }//end for
718
719
                // Add the end heredoc token to the final array.
720
                $finalTokens[$newStackPtr] = self::standardiseToken($tokens[$stackPtr]);
721
722
                if ($nowdoc === true) {
723
                    $finalTokens[$newStackPtr]['code'] = T_END_NOWDOC;
724
                    $finalTokens[$newStackPtr]['type'] = 'T_END_NOWDOC';
725
                }
726
727
                $newStackPtr++;
728
729
                // Continue, as we're done with this token.
730
                continue;
731
            }//end if
732
733
            /*
734
                Before PHP 7.0, the "yield from" was tokenized as
735
                T_YIELD, T_WHITESPACE and T_STRING. So look for
736
                and change this token in earlier versions.
737
            */
738
739
            if (PHP_VERSION_ID < 70000
740
                && PHP_VERSION_ID >= 50500
741
                && $tokenIsArray === true
742
                && $token[0] === T_YIELD
743
                && isset($tokens[($stackPtr + 1)]) === true
744
                && isset($tokens[($stackPtr + 2)]) === true
745
                && $tokens[($stackPtr + 1)][0] === T_WHITESPACE
746
                && $tokens[($stackPtr + 2)][0] === T_STRING
747
                && strtolower($tokens[($stackPtr + 2)][1]) === 'from'
748
            ) {
749
                $newToken            = [];
750
                $newToken['code']    = T_YIELD_FROM;
751
                $newToken['type']    = 'T_YIELD_FROM';
752
                $newToken['content'] = $token[1].$tokens[($stackPtr + 1)][1].$tokens[($stackPtr + 2)][1];
753
                $finalTokens[$newStackPtr] = $newToken;
754
755
                $newStackPtr++;
756
                $stackPtr += 2;
757
                continue;
758
            }
759
760
            /*
761
                Before PHP 5.5, the yield keyword was tokenized as
762
                T_STRING. So look for and change this token in
763
                earlier versions.
764
                Checks also if it is just "yield" or "yield from".
765
            */
766
767
            if (PHP_VERSION_ID < 50500
768
                && $tokenIsArray === true
769
                && $token[0] === T_STRING
770
                && strtolower($token[1]) === 'yield'
771
            ) {
772
                if (isset($tokens[($stackPtr + 1)]) === true
773
                    && isset($tokens[($stackPtr + 2)]) === true
774
                    && $tokens[($stackPtr + 1)][0] === T_WHITESPACE
775
                    && $tokens[($stackPtr + 2)][0] === T_STRING
776
                    && strtolower($tokens[($stackPtr + 2)][1]) === 'from'
777
                ) {
778
                    $newToken            = [];
779
                    $newToken['code']    = T_YIELD_FROM;
780
                    $newToken['type']    = 'T_YIELD_FROM';
781
                    $newToken['content'] = $token[1].$tokens[($stackPtr + 1)][1].$tokens[($stackPtr + 2)][1];
782
                    $finalTokens[$newStackPtr] = $newToken;
783
784
                    $newStackPtr++;
785
                    $stackPtr += 2;
786
                    continue;
787
                }
788
789
                $newToken            = [];
790
                $newToken['code']    = T_YIELD;
791
                $newToken['type']    = 'T_YIELD';
792
                $newToken['content'] = $token[1];
793
                $finalTokens[$newStackPtr] = $newToken;
794
795
                $newStackPtr++;
796
                continue;
797
            }//end if
798
799
            /*
800
                Before PHP 5.6, the ... operator was tokenized as three
801
                T_STRING_CONCAT tokens in a row. So look for and combine
802
                these tokens in earlier versions.
803
            */
804
805
            if ($tokenIsArray === false
806
                && $token[0] === '.'
807
                && isset($tokens[($stackPtr + 1)]) === true
808
                && isset($tokens[($stackPtr + 2)]) === true
809
                && $tokens[($stackPtr + 1)] === '.'
810
                && $tokens[($stackPtr + 2)] === '.'
811
            ) {
812
                $newToken            = [];
813
                $newToken['code']    = T_ELLIPSIS;
814
                $newToken['type']    = 'T_ELLIPSIS';
815
                $newToken['content'] = '...';
816
                $finalTokens[$newStackPtr] = $newToken;
817
818
                $newStackPtr++;
819
                $stackPtr += 2;
820
                continue;
821
            }
822
823
            /*
824
                Before PHP 5.6, the ** operator was tokenized as two
825
                T_MULTIPLY tokens in a row. So look for and combine
826
                these tokens in earlier versions.
827
            */
828
829
            if ($tokenIsArray === false
830
                && $token[0] === '*'
831
                && isset($tokens[($stackPtr + 1)]) === true
832
                && $tokens[($stackPtr + 1)] === '*'
833
            ) {
834
                $newToken            = [];
835
                $newToken['code']    = T_POW;
836
                $newToken['type']    = 'T_POW';
837
                $newToken['content'] = '**';
838
                $finalTokens[$newStackPtr] = $newToken;
839
840
                $newStackPtr++;
841
                $stackPtr++;
842
                continue;
843
            }
844
845
            /*
846
                Before PHP 5.6, the **= operator was tokenized as
847
                T_MULTIPLY followed by T_MUL_EQUAL. So look for and combine
848
                these tokens in earlier versions.
849
            */
850
851
            if ($tokenIsArray === false
852
                && $token[0] === '*'
853
                && isset($tokens[($stackPtr + 1)]) === true
854
                && is_array($tokens[($stackPtr + 1)]) === true
855
                && $tokens[($stackPtr + 1)][1] === '*='
856
            ) {
857
                $newToken            = [];
858
                $newToken['code']    = T_POW_EQUAL;
859
                $newToken['type']    = 'T_POW_EQUAL';
860
                $newToken['content'] = '**=';
861
                $finalTokens[$newStackPtr] = $newToken;
862
863
                $newStackPtr++;
864
                $stackPtr++;
865
                continue;
866
            }
867
868
            /*
869
                Before PHP 7, the ??= operator was tokenized as
870
                T_INLINE_THEN, T_INLINE_THEN, T_EQUAL.
871
                Between PHP 7.0 and 7.2, the ??= operator was tokenized as
872
                T_COALESCE, T_EQUAL.
873
                So look for and combine these tokens in earlier versions.
874
            */
875
876
            if (($tokenIsArray === false
877
                && $token[0] === '?'
878
                && isset($tokens[($stackPtr + 1)]) === true
879
                && $tokens[($stackPtr + 1)][0] === '?'
880
                && isset($tokens[($stackPtr + 2)]) === true
881
                && $tokens[($stackPtr + 2)][0] === '=')
882
                || ($tokenIsArray === true
883
                && $token[0] === T_COALESCE
884
                && isset($tokens[($stackPtr + 1)]) === true
885
                && $tokens[($stackPtr + 1)][0] === '=')
886
            ) {
887
                $newToken            = [];
888
                $newToken['code']    = T_COALESCE_EQUAL;
889
                $newToken['type']    = 'T_COALESCE_EQUAL';
890
                $newToken['content'] = '??=';
891
                $finalTokens[$newStackPtr] = $newToken;
892
893
                $newStackPtr++;
894
                $stackPtr++;
895
896
                if ($tokenIsArray === false) {
897
                    // Pre PHP 7.
898
                    $stackPtr++;
899
                }
900
901
                continue;
902
            }
903
904
            /*
905
                Before PHP 7, the ?? operator was tokenized as
906
                T_INLINE_THEN followed by T_INLINE_THEN.
907
                So look for and combine these tokens in earlier versions.
908
            */
909
910
            if ($tokenIsArray === false
911
                && $token[0] === '?'
912
                && isset($tokens[($stackPtr + 1)]) === true
913
                && $tokens[($stackPtr + 1)][0] === '?'
914
            ) {
915
                $newToken            = [];
916
                $newToken['code']    = T_COALESCE;
917
                $newToken['type']    = 'T_COALESCE';
918
                $newToken['content'] = '??';
919
                $finalTokens[$newStackPtr] = $newToken;
920
921
                $newStackPtr++;
922
                $stackPtr++;
923
                continue;
924
            }
925
926
            /*
927
                Convert ? to T_NULLABLE OR T_INLINE_THEN
928
            */
929
930
            if ($tokenIsArray === false && $token[0] === '?') {
931
                $newToken            = [];
932
                $newToken['content'] = '?';
933
934
                $prevNonEmpty = null;
935
                for ($i = ($stackPtr - 1); $i >= 0; $i--) {
936
                    if (is_array($tokens[$i]) === true) {
937
                        $tokenType = $tokens[$i][0];
938
                    } else {
939
                        $tokenType = $tokens[$i];
940
                    }
941
942
                    if ($prevNonEmpty === null
943
                        && isset(Util\Tokens::$emptyTokens[$tokenType]) === false
944
                    ) {
945
                        // Found the previous non-empty token.
946
                        if ($tokenType === ':' || $tokenType === ',') {
947
                            $newToken['code'] = T_NULLABLE;
948
                            $newToken['type'] = 'T_NULLABLE';
949
                            break;
950
                        }
951
952
                        $prevNonEmpty = $tokenType;
953
                    }
954
955
                    if ($tokenType === T_FUNCTION) {
956
                        $newToken['code'] = T_NULLABLE;
957
                        $newToken['type'] = 'T_NULLABLE';
958
                        break;
959
                    } else if (in_array($tokenType, [T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, '=', '{', ';']) === true) {
960
                        $newToken['code'] = T_INLINE_THEN;
961
                        $newToken['type'] = 'T_INLINE_THEN';
962
963
                        $insideInlineIf[] = $stackPtr;
964
                        break;
965
                    }
966
                }//end for
967
968
                $finalTokens[$newStackPtr] = $newToken;
969
                $newStackPtr++;
970
                continue;
971
            }//end if
972
973
            /*
974
                Tokens after a double colon may be look like scope openers,
975
                such as when writing code like Foo::NAMESPACE, but they are
976
                only ever variables or strings.
977
            */
978
979
            if ($stackPtr > 1
980
                && (is_array($tokens[($stackPtr - 1)]) === true
981
                && $tokens[($stackPtr - 1)][0] === T_PAAMAYIM_NEKUDOTAYIM)
982
                && $tokenIsArray === true
983
                && $token[0] !== T_STRING
984
                && $token[0] !== T_VARIABLE
985
                && $token[0] !== T_DOLLAR
986
                && isset(Util\Tokens::$emptyTokens[$token[0]]) === false
987
            ) {
988
                $newToken            = [];
989
                $newToken['code']    = T_STRING;
990
                $newToken['type']    = 'T_STRING';
991
                $newToken['content'] = $token[1];
992
                $finalTokens[$newStackPtr] = $newToken;
993
994
                $newStackPtr++;
995
                continue;
996
            }
997
998
            /*
999
                The string-like token after a function keyword should always be
1000
                tokenized as T_STRING even if it appears to be a different token,
1001
                such as when writing code like: function default(): foo
1002
                so go forward and change the token type before it is processed.
1003
            */
1004
1005
            if ($tokenIsArray === true && $token[0] === T_FUNCTION
1006
                && $finalTokens[$lastNotEmptyToken]['code'] !== T_USE
1007
            ) {
1008
                for ($x = ($stackPtr + 1); $x < $numTokens; $x++) {
1009
                    if (is_array($tokens[$x]) === false
1010
                        || isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === false
1011
                    ) {
1012
                        // Non-empty content.
1013
                        break;
1014
                    }
1015
                }
1016
1017
                if ($x < $numTokens && is_array($tokens[$x]) === true) {
1018
                    $tokens[$x][0] = T_STRING;
1019
                }
1020
            }
1021
1022
            /*
1023
                Before PHP 7, the <=> operator was tokenized as
1024
                T_IS_SMALLER_OR_EQUAL followed by T_GREATER_THAN.
1025
                So look for and combine these tokens in earlier versions.
1026
            */
1027
1028
            if ($tokenIsArray === true
1029
                && $token[0] === T_IS_SMALLER_OR_EQUAL
1030
                && isset($tokens[($stackPtr + 1)]) === true
1031
                && $tokens[($stackPtr + 1)][0] === '>'
1032
            ) {
1033
                $newToken            = [];
1034
                $newToken['code']    = T_SPACESHIP;
1035
                $newToken['type']    = 'T_SPACESHIP';
1036
                $newToken['content'] = '<=>';
1037
                $finalTokens[$newStackPtr] = $newToken;
1038
1039
                $newStackPtr++;
1040
                $stackPtr++;
1041
                continue;
1042
            }
1043
1044
            /*
1045
                PHP doesn't assign a token to goto labels, so we have to.
1046
                These are just string tokens with a single colon after them. Double
1047
                colons are already tokenized and so don't interfere with this check.
1048
                But we do have to account for CASE statements, that look just like
1049
                goto labels.
1050
            */
1051
1052
            if ($tokenIsArray === true
1053
                && $token[0] === T_STRING
1054
                && isset($tokens[($stackPtr + 1)]) === true
1055
                && $tokens[($stackPtr + 1)] === ':'
1056
                && $tokens[($stackPtr - 1)][0] !== T_PAAMAYIM_NEKUDOTAYIM
1057
            ) {
1058
                $stopTokens = [
1059
                    T_CASE               => true,
1060
                    T_SEMICOLON          => true,
1061
                    T_OPEN_CURLY_BRACKET => true,
1062
                    T_INLINE_THEN        => true,
1063
                ];
1064
1065
                for ($x = ($newStackPtr - 1); $x > 0; $x--) {
1066
                    if (isset($stopTokens[$finalTokens[$x]['code']]) === true) {
1067
                        break;
1068
                    }
1069
                }
1070
1071
                if ($finalTokens[$x]['code'] !== T_CASE
1072
                    && $finalTokens[$x]['code'] !== T_INLINE_THEN
1073
                ) {
1074
                    $finalTokens[$newStackPtr] = [
1075
                        'content' => $token[1].':',
1076
                        'code'    => T_GOTO_LABEL,
1077
                        'type'    => 'T_GOTO_LABEL',
1078
                    ];
1079
1080
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1081
                        echo "\t\t* token $stackPtr changed from T_STRING to T_GOTO_LABEL".PHP_EOL;
1082
                        echo "\t\t* skipping T_COLON token ".($stackPtr + 1).PHP_EOL;
1083
                    }
1084
1085
                    $newStackPtr++;
1086
                    $stackPtr++;
1087
                    continue;
1088
                }
1089
            }//end if
1090
1091
            /*
1092
                HHVM 3.5 tokenizes "else[\s]+if" as a T_ELSEIF token while PHP
1093
                proper only tokenizes "elseif" as a T_ELSEIF token. So split
1094
                up the HHVM token to make it looks like proper PHP.
1095
            */
1096
1097
            if ($tokenIsArray === true
1098
                && $token[0] === T_ELSEIF
1099
                && strtolower($token[1]) !== 'elseif'
1100
            ) {
1101
                $finalTokens[$newStackPtr] = [
1102
                    'content' => substr($token[1], 0, 4),
1103
                    'code'    => T_ELSE,
1104
                    'type'    => 'T_ELSE',
1105
                ];
1106
1107
                $newStackPtr++;
1108
                $finalTokens[$newStackPtr] = [
1109
                    'content' => substr($token[1], 4, -2),
1110
                    'code'    => T_WHITESPACE,
1111
                    'type'    => 'T_WHITESPACE',
1112
                ];
1113
1114
                $newStackPtr++;
1115
                $finalTokens[$newStackPtr] = [
1116
                    'content' => substr($token[1], -2),
1117
                    'code'    => T_IF,
1118
                    'type'    => 'T_IF',
1119
                ];
1120
1121
                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1122
                    echo "\t\t* token $stackPtr changed from T_ELSEIF to T_ELSE/T_WHITESPACE/T_IF".PHP_EOL;
1123
                }
1124
1125
                $newStackPtr++;
1126
                continue;
1127
            }//end if
1128
1129
            /*
1130
                HHVM 3.5 and 3.6 tokenizes a hashbang line such as #!/usr/bin/php
1131
                as T_HASHBANG while PHP proper uses T_INLINE_HTML.
1132
            */
1133
1134
            if ($tokenIsArray === true && token_name($token[0]) === 'T_HASHBANG') {
1135
                $finalTokens[$newStackPtr] = [
1136
                    'content' => $token[1],
1137
                    'code'    => T_INLINE_HTML,
1138
                    'type'    => 'T_INLINE_HTML',
1139
                ];
1140
1141
                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1142
                    echo "\t\t* token $stackPtr changed from T_HASHBANG to T_INLINE_HTML".PHP_EOL;
1143
                }
1144
1145
                $newStackPtr++;
1146
                continue;
1147
            }//end if
1148
1149
            /*
1150
                If this token has newlines in its content, split each line up
1151
                and create a new token for each line. We do this so it's easier
1152
                to ascertain where errors occur on a line.
1153
                Note that $token[1] is the token's content.
1154
            */
1155
1156
            if ($tokenIsArray === true && strpos($token[1], $this->eolChar) !== false) {
1157
                $tokenLines = explode($this->eolChar, $token[1]);
1158
                $numLines   = count($tokenLines);
1159
                $newToken   = [
1160
                    'type'    => token_name($token[0]),
1161
                    'code'    => $token[0],
1162
                    'content' => '',
1163
                ];
1164
1165
                for ($i = 0; $i < $numLines; $i++) {
1166
                    $newToken['content'] = $tokenLines[$i];
1167
                    if ($i === ($numLines - 1)) {
1168
                        if ($tokenLines[$i] === '') {
1169
                            break;
1170
                        }
1171
                    } else {
1172
                        $newToken['content'] .= $this->eolChar;
1173
                    }
1174
1175
                    $finalTokens[$newStackPtr] = $newToken;
1176
                    $newStackPtr++;
1177
                }
1178
            } else {
1179
                if ($tokenIsArray === true && $token[0] === T_STRING) {
1180
                    // Some T_STRING tokens should remain that way
1181
                    // due to their context.
1182
                    $context = [
1183
                        T_OBJECT_OPERATOR      => true,
1184
                        T_FUNCTION             => true,
1185
                        T_CLASS                => true,
1186
                        T_EXTENDS              => true,
1187
                        T_IMPLEMENTS           => true,
1188
                        T_NEW                  => true,
1189
                        T_CONST                => true,
1190
                        T_NS_SEPARATOR         => true,
1191
                        T_USE                  => true,
1192
                        T_NAMESPACE            => true,
1193
                        T_PAAMAYIM_NEKUDOTAYIM => true,
1194
                    ];
1195
                    if (isset($context[$finalTokens[$lastNotEmptyToken]['code']]) === true) {
1196
                        // Special case for syntax like: return new self
1197
                        // where self should not be a string.
1198
                        if ($finalTokens[$lastNotEmptyToken]['code'] === T_NEW
1199
                            && strtolower($token[1]) === 'self'
1200
                        ) {
1201
                            $finalTokens[$newStackPtr] = [
1202
                                'content' => $token[1],
1203
                                'code'    => T_SELF,
1204
                                'type'    => 'T_SELF',
1205
                            ];
1206
                        } else {
1207
                            $finalTokens[$newStackPtr] = [
1208
                                'content' => $token[1],
1209
                                'code'    => T_STRING,
1210
                                'type'    => 'T_STRING',
1211
                            ];
1212
                        }
1213
1214
                        $newStackPtr++;
1215
                        continue;
1216
                    }//end if
1217
                }//end if
1218
1219
                $newToken = null;
1220
                if ($tokenIsArray === false) {
1221
                    if (isset(self::$resolveTokenCache[$token[0]]) === true) {
1222
                        $newToken = self::$resolveTokenCache[$token[0]];
1223
                    }
1224
                } else {
1225
                    $cacheKey = null;
1226
                    if ($token[0] === T_STRING) {
1227
                        $cacheKey = strtolower($token[1]);
1228
                    } else if ($token[0] !== T_CURLY_OPEN) {
1229
                        $cacheKey = $token[0];
1230
                    }
1231
1232
                    if ($cacheKey !== null && isset(self::$resolveTokenCache[$cacheKey]) === true) {
1233
                        $newToken            = self::$resolveTokenCache[$cacheKey];
1234
                        $newToken['content'] = $token[1];
1235
                    }
1236
                }
1237
1238
                if ($newToken === null) {
1239
                    $newToken = self::standardiseToken($token);
1240
                }
1241
1242
                // Convert colons that are actually the ELSE component of an
1243
                // inline IF statement.
1244
                if (empty($insideInlineIf) === false && $newToken['code'] === T_COLON) {
1245
                    // Make sure this isn't the return type separator of a closure.
1246
                    $isReturnType = false;
1247
                    for ($i = ($stackPtr - 1); $i > 0; $i--) {
1248
                        if (is_array($tokens[$i]) === false
1249
                            || ($tokens[$i][0] !== T_DOC_COMMENT
1250
                            && $tokens[$i][0] !== T_COMMENT
1251
                            && $tokens[$i][0] !== T_WHITESPACE)
1252
                        ) {
1253
                            break;
1254
                        }
1255
                    }
1256
1257
                    if ($tokens[$i] === ')') {
1258
                        for ($i--; $i > 0; $i--) {
1259
                            if ($tokens[$i] === '(') {
1260
                                break;
1261
                            }
1262
                        }
1263
1264
                        // We've found the open parenthesis, so if the previous
1265
                        // non-empty token is FUNCTION or USE, this is a closure.
1266
                        for ($i--; $i > 0; $i--) {
1267
                            if (is_array($tokens[$i]) === false
1268
                                || ($tokens[$i][0] !== T_DOC_COMMENT
1269
                                && $tokens[$i][0] !== T_COMMENT
1270
                                && $tokens[$i][0] !== T_WHITESPACE)
1271
                            ) {
1272
                                break;
1273
                            }
1274
                        }
1275
1276
                        if ($tokens[$i][0] === T_FUNCTION || $tokens[$i][0] === T_USE) {
1277
                            $isReturnType = true;
1278
                        }
1279
                    }//end if
1280
1281
                    if ($isReturnType === false) {
1282
                        array_pop($insideInlineIf);
1283
                        $newToken['code'] = T_INLINE_ELSE;
1284
                        $newToken['type'] = 'T_INLINE_ELSE';
1285
                    }
1286
                }//end if
1287
1288
                // This is a special condition for T_ARRAY tokens used for
1289
                // type hinting function arguments as being arrays. We want to keep
1290
                // the parenthesis map clean, so let's tag these tokens as
1291
                // T_ARRAY_HINT.
1292
                if ($newToken['code'] === T_ARRAY) {
1293
                    for ($i = $stackPtr; $i < $numTokens; $i++) {
1294
                        if ($tokens[$i] === '(') {
1295
                            break;
1296
                        } else if ($tokens[$i][0] === T_VARIABLE) {
1297
                            $newToken['code'] = T_ARRAY_HINT;
1298
                            $newToken['type'] = 'T_ARRAY_HINT';
1299
                            break;
1300
                        }
1301
                    }
1302
                }
1303
1304
                // This is a special case when checking PHP 5.5+ code in PHP < 5.5
1305
                // where "finally" should be T_FINALLY instead of T_STRING.
1306
                if ($newToken['code'] === T_STRING
1307
                    && strtolower($newToken['content']) === 'finally'
1308
                ) {
1309
                    $newToken['code'] = T_FINALLY;
1310
                    $newToken['type'] = 'T_FINALLY';
1311
                }
1312
1313
                // This is a special case for the PHP 5.5 classname::class syntax
1314
                // where "class" should be T_STRING instead of T_CLASS.
1315
                if (($newToken['code'] === T_CLASS
1316
                    || $newToken['code'] === T_FUNCTION)
1317
                    && $finalTokens[($newStackPtr - 1)]['code'] === T_DOUBLE_COLON
1318
                ) {
1319
                    $newToken['code'] = T_STRING;
1320
                    $newToken['type'] = 'T_STRING';
1321
                }
1322
1323
                // This is a special case for PHP 5.6 use function and use const
1324
                // where "function" and "const" should be T_STRING instead of T_FUNCTION
1325
                // and T_CONST.
1326
                if (($newToken['code'] === T_FUNCTION
1327
                    || $newToken['code'] === T_CONST)
1328
                    && $finalTokens[$lastNotEmptyToken]['code'] === T_USE
1329
                ) {
1330
                    $newToken['code'] = T_STRING;
1331
                    $newToken['type'] = 'T_STRING';
1332
                }
1333
1334
                // This is a special case for use groups in PHP 7+ where leaving
1335
                // the curly braces as their normal tokens would confuse
1336
                // the scope map and sniffs.
1337
                if ($newToken['code'] === T_OPEN_CURLY_BRACKET
1338
                    && $finalTokens[$lastNotEmptyToken]['code'] === T_NS_SEPARATOR
1339
                ) {
1340
                    $newToken['code'] = T_OPEN_USE_GROUP;
1341
                    $newToken['type'] = 'T_OPEN_USE_GROUP';
1342
                    $insideUseGroup   = true;
1343
                }
1344
1345
                if ($insideUseGroup === true && $newToken['code'] === T_CLOSE_CURLY_BRACKET) {
1346
                    $newToken['code'] = T_CLOSE_USE_GROUP;
1347
                    $newToken['type'] = 'T_CLOSE_USE_GROUP';
1348
                    $insideUseGroup   = false;
1349
                }
1350
1351
                $finalTokens[$newStackPtr] = $newToken;
1352
                $newStackPtr++;
1353
            }//end if
1354
        }//end for
1355
1356
        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1357
            echo "\t*** END PHP TOKENIZING ***".PHP_EOL;
1358
        }
1359
1360
        return $finalTokens;
1361
1362
    }//end tokenize()
1363
1364
1365
    /**
1366
     * Performs additional processing after main tokenizing.
1367
     *
1368
     * This additional processing checks for CASE statements that are using curly
1369
     * braces for scope openers and closers. It also turns some T_FUNCTION tokens
1370
     * into T_CLOSURE when they are not standard function definitions. It also
1371
     * detects short array syntax and converts those square brackets into new tokens.
1372
     * It also corrects some usage of the static and class keywords. It also
1373
     * assigns tokens to function return types.
1374
     *
1375
     * @return void
1376
     */
1377
    protected function processAdditional()
1378
    {
1379
        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1380
            echo "\t*** START ADDITIONAL PHP PROCESSING ***".PHP_EOL;
1381
        }
1382
1383
        $numTokens = count($this->tokens);
1384
        for ($i = ($numTokens - 1); $i >= 0; $i--) {
1385
            // Check for any unset scope conditions due to alternate IF/ENDIF syntax.
1386
            if (isset($this->tokens[$i]['scope_opener']) === true
1387
                && isset($this->tokens[$i]['scope_condition']) === false
1388
            ) {
1389
                $this->tokens[$i]['scope_condition'] = $this->tokens[$this->tokens[$i]['scope_opener']]['scope_condition'];
1390
            }
1391
1392
            if ($this->tokens[$i]['code'] === T_FUNCTION) {
1393
                /*
1394
                    Detect functions that are actually closures and
1395
                    assign them a different token.
1396
                */
1397
1398
                if (isset($this->tokens[$i]['scope_opener']) === true) {
1399
                    for ($x = ($i + 1); $x < $numTokens; $x++) {
1400
                        if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false
1401
                            && $this->tokens[$x]['code'] !== T_BITWISE_AND
1402
                        ) {
1403
                            break;
1404
                        }
1405
                    }
1406
1407
                    if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS) {
1408
                        $this->tokens[$i]['code'] = T_CLOSURE;
1409
                        $this->tokens[$i]['type'] = 'T_CLOSURE';
1410
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1411
                            $line = $this->tokens[$i]['line'];
1412
                            echo "\t* token $i on line $line changed from T_FUNCTION to T_CLOSURE".PHP_EOL;
1413
                        }
1414
1415
                        for ($x = ($this->tokens[$i]['scope_opener'] + 1); $x < $this->tokens[$i]['scope_closer']; $x++) {
1416
                            if (isset($this->tokens[$x]['conditions'][$i]) === false) {
1417
                                continue;
1418
                            }
1419
1420
                            $this->tokens[$x]['conditions'][$i] = T_CLOSURE;
1421
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1422
                                $type = $this->tokens[$x]['type'];
1423
                                echo "\t\t* cleaned $x ($type) *".PHP_EOL;
1424
                            }
1425
                        }
1426
                    }
1427
1428
                    $tokenAfterReturnTypeHint = $this->tokens[$i]['scope_opener'];
1429
                } else if (isset($this->tokens[$i]['parenthesis_closer']) === true) {
1430
                    $tokenAfterReturnTypeHint = null;
1431
                    for ($x = ($this->tokens[$i]['parenthesis_closer'] + 1); $x < $numTokens; $x++) {
1432
                        if ($this->tokens[$x]['code'] === T_SEMICOLON) {
1433
                            $tokenAfterReturnTypeHint = $x;
1434
                            break;
1435
                        }
1436
                    }
1437
1438
                    if ($tokenAfterReturnTypeHint === null) {
1439
                        // Probably a syntax error.
1440
                        continue;
1441
                    }
1442
                } else {
1443
                    // Probably a syntax error.
1444
                    continue;
1445
                }//end if
1446
1447
                /*
1448
                    Detect function return values and assign them
1449
                    a special token, because PHP doesn't.
1450
                */
1451
1452
                for ($x = ($tokenAfterReturnTypeHint - 1); $x > $i; $x--) {
1453
                    if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1454
                        if (in_array($this->tokens[$x]['code'], [T_STRING, T_ARRAY, T_ARRAY_HINT, T_CALLABLE, T_SELF, T_PARENT], true) === true) {
1455
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1456
                                $line = $this->tokens[$x]['line'];
1457
                                $type = $this->tokens[$x]['type'];
1458
                                echo "\t* token $x on line $line changed from $type to T_RETURN_TYPE".PHP_EOL;
1459
                            }
1460
1461
                            $this->tokens[$x]['code'] = T_RETURN_TYPE;
1462
                            $this->tokens[$x]['type'] = 'T_RETURN_TYPE';
1463
1464
                            if (array_key_exists('parenthesis_opener', $this->tokens[$x]) === true) {
1465
                                unset($this->tokens[$x]['parenthesis_opener']);
1466
                            }
1467
1468
                            if (array_key_exists('parenthesis_closer', $this->tokens[$x]) === true) {
1469
                                unset($this->tokens[$x]['parenthesis_closer']);
1470
                            }
1471
1472
                            if (array_key_exists('parenthesis_owner', $this->tokens[$x]) === true) {
1473
                                unset($this->tokens[$x]['parenthesis_owner']);
1474
                            }
1475
                        }//end if
1476
1477
                        break;
1478
                    }//end if
1479
                }//end for
1480
1481
                continue;
1482
            } else if ($this->tokens[$i]['code'] === T_CLASS && isset($this->tokens[$i]['scope_opener']) === true) {
1483
                /*
1484
                    Detect anonymous classes and assign them a different token.
1485
                */
1486
1487
                for ($x = ($i + 1); $x < $numTokens; $x++) {
1488
                    if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1489
                        break;
1490
                    }
1491
                }
1492
1493
                if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS
1494
                    || $this->tokens[$x]['code'] === T_OPEN_CURLY_BRACKET
1495
                    || $this->tokens[$x]['code'] === T_EXTENDS
1496
                    || $this->tokens[$x]['code'] === T_IMPLEMENTS
1497
                ) {
1498
                    $this->tokens[$i]['code'] = T_ANON_CLASS;
1499
                    $this->tokens[$i]['type'] = 'T_ANON_CLASS';
1500
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1501
                        $line = $this->tokens[$i]['line'];
1502
                        echo "\t* token $i on line $line changed from T_CLASS to T_ANON_CLASS".PHP_EOL;
1503
                    }
1504
1505
                    for ($x = ($this->tokens[$i]['scope_opener'] + 1); $x < $this->tokens[$i]['scope_closer']; $x++) {
1506
                        if (isset($this->tokens[$x]['conditions'][$i]) === false) {
1507
                            continue;
1508
                        }
1509
1510
                        $this->tokens[$x]['conditions'][$i] = T_ANON_CLASS;
1511
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1512
                            $type = $this->tokens[$x]['type'];
1513
                            echo "\t\t* cleaned $x ($type) *".PHP_EOL;
1514
                        }
1515
                    }
1516
                }
1517
1518
                continue;
1519
            } else if ($this->tokens[$i]['code'] === T_OPEN_SQUARE_BRACKET) {
1520
                if (isset($this->tokens[$i]['bracket_closer']) === false) {
1521
                    continue;
1522
                }
1523
1524
                // Unless there is a variable or a bracket before this token,
1525
                // it is the start of an array being defined using the short syntax.
1526
                $isShortArray = false;
1527
                $allowed      = [
1528
                    T_CLOSE_SQUARE_BRACKET     => T_CLOSE_SQUARE_BRACKET,
1529
                    T_CLOSE_CURLY_BRACKET      => T_CLOSE_CURLY_BRACKET,
1530
                    T_CLOSE_PARENTHESIS        => T_CLOSE_PARENTHESIS,
1531
                    T_VARIABLE                 => T_VARIABLE,
1532
                    T_OBJECT_OPERATOR          => T_OBJECT_OPERATOR,
1533
                    T_STRING                   => T_STRING,
1534
                    T_CONSTANT_ENCAPSED_STRING => T_CONSTANT_ENCAPSED_STRING,
1535
                ];
1536
1537
                for ($x = ($i - 1); $x > 0; $x--) {
1538
                    // If we hit a scope opener, the statement has ended
1539
                    // without finding anything, so it's probably an array
1540
                    // using PHP 7.1 short list syntax.
1541
                    if (isset($this->tokens[$x]['scope_opener']) === true) {
1542
                        $isShortArray = true;
1543
                        break;
1544
                    }
1545
1546
                    if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1547
                        if (isset($allowed[$this->tokens[$x]['code']]) === false) {
1548
                            $isShortArray = true;
1549
                        }
1550
1551
                        break;
1552
                    }
1553
                }
1554
1555
                if ($isShortArray === true) {
1556
                    $this->tokens[$i]['code'] = T_OPEN_SHORT_ARRAY;
1557
                    $this->tokens[$i]['type'] = 'T_OPEN_SHORT_ARRAY';
1558
1559
                    $closer = $this->tokens[$i]['bracket_closer'];
1560
                    $this->tokens[$closer]['code'] = T_CLOSE_SHORT_ARRAY;
1561
                    $this->tokens[$closer]['type'] = 'T_CLOSE_SHORT_ARRAY';
1562
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1563
                        $line = $this->tokens[$i]['line'];
1564
                        echo "\t* token $i on line $line changed from T_OPEN_SQUARE_BRACKET to T_OPEN_SHORT_ARRAY".PHP_EOL;
1565
                        $line = $this->tokens[$closer]['line'];
1566
                        echo "\t* token $closer on line $line changed from T_CLOSE_SQUARE_BRACKET to T_CLOSE_SHORT_ARRAY".PHP_EOL;
1567
                    }
1568
                }
1569
1570
                continue;
1571
            } else if ($this->tokens[$i]['code'] === T_STATIC) {
1572
                for ($x = ($i - 1); $x > 0; $x--) {
1573
                    if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1574
                        break;
1575
                    }
1576
                }
1577
1578
                if ($this->tokens[$x]['code'] === T_INSTANCEOF) {
1579
                    $this->tokens[$i]['code'] = T_STRING;
1580
                    $this->tokens[$i]['type'] = 'T_STRING';
1581
1582
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1583
                        $line = $this->tokens[$i]['line'];
1584
                        echo "\t* token $i on line $line changed from T_STATIC to T_STRING".PHP_EOL;
1585
                    }
1586
                }
1587
1588
                continue;
1589
            } else if ($this->tokens[$i]['code'] === T_ECHO && $this->tokens[$i]['content'] === '<?=') {
1590
                // HHVM tokenizes <?= as T_ECHO but it should be T_OPEN_TAG_WITH_ECHO.
1591
                $this->tokens[$i]['code'] = T_OPEN_TAG_WITH_ECHO;
1592
                $this->tokens[$i]['type'] = 'T_OPEN_TAG_WITH_ECHO';
1593
1594
                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1595
                    $line = $this->tokens[$i]['line'];
1596
                    echo "\t* token $i on line $line changed from T_ECHO to T_OPEN_TAG_WITH_ECHO".PHP_EOL;
1597
                }
1598
            } else if ($this->tokens[$i]['code'] === T_TRUE
1599
                || $this->tokens[$i]['code'] === T_FALSE
1600
                || $this->tokens[$i]['code'] === T_NULL
1601
            ) {
1602
                for ($x = ($i + 1); $i < $numTokens; $x++) {
1603
                    if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1604
                        // Non-whitespace content.
1605
                        break;
1606
                    }
1607
                }
1608
1609
                $context = [
1610
                    T_OBJECT_OPERATOR      => true,
1611
                    T_NS_SEPARATOR         => true,
1612
                    T_PAAMAYIM_NEKUDOTAYIM => true,
1613
                ];
1614
                if (isset($context[$this->tokens[$x]['code']]) === true) {
1615
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1616
                        $line = $this->tokens[$i]['line'];
1617
                        $type = $this->tokens[$i]['type'];
1618
                        echo "\t* token $i on line $line changed from $type to T_STRING".PHP_EOL;
1619
                    }
1620
1621
                    $this->tokens[$i]['code'] = T_STRING;
1622
                    $this->tokens[$i]['type'] = 'T_STRING';
1623
                }
1624
            } else if ($this->tokens[$i]['code'] === T_CONST) {
1625
                // Context sensitive keywords support.
1626
                for ($x = ($i + 1); $i < $numTokens; $x++) {
1627
                    if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1628
                        // Non-whitespace content.
1629
                        break;
1630
                    }
1631
                }
1632
1633
                if ($this->tokens[$x]['code'] !== T_STRING) {
1634
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1635
                        $line = $this->tokens[$x]['line'];
1636
                        $type = $this->tokens[$x]['type'];
1637
                        echo "\t* token $x on line $line changed from $type to T_STRING".PHP_EOL;
1638
                    }
1639
1640
                    $this->tokens[$x]['code'] = T_STRING;
1641
                    $this->tokens[$x]['type'] = 'T_STRING';
1642
                }
1643
            }//end if
1644
1645
            if (($this->tokens[$i]['code'] !== T_CASE
1646
                && $this->tokens[$i]['code'] !== T_DEFAULT)
1647
                || isset($this->tokens[$i]['scope_opener']) === false
1648
            ) {
1649
                // Only interested in CASE and DEFAULT statements from here on in.
1650
                continue;
1651
            }
1652
1653
            $scopeOpener = $this->tokens[$i]['scope_opener'];
1654
            $scopeCloser = $this->tokens[$i]['scope_closer'];
1655
1656
            // If the first char after the opener is a curly brace
1657
            // and that brace has been ignored, it is actually
1658
            // opening this case statement and the opener and closer are
1659
            // probably set incorrectly.
1660
            for ($x = ($scopeOpener + 1); $x < $numTokens; $x++) {
1661
                if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1662
                    // Non-whitespace content.
1663
                    break;
1664
                }
1665
            }
1666
1667
            if ($this->tokens[$x]['code'] === T_CASE || $this->tokens[$x]['code'] === T_DEFAULT) {
1668
                // Special case for multiple CASE statements that share the same
1669
                // closer. Because we are going backwards through the file, this next
1670
                // CASE statement is already fixed, so just use its closer and don't
1671
                // worry about fixing anything.
1672
                $newCloser = $this->tokens[$x]['scope_closer'];
1673
                $this->tokens[$i]['scope_closer'] = $newCloser;
1674
                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1675
                    $oldType = $this->tokens[$scopeCloser]['type'];
1676
                    $newType = $this->tokens[$newCloser]['type'];
1677
                    $line    = $this->tokens[$i]['line'];
1678
                    echo "\t* token $i (T_CASE) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL;
1679
                }
1680
1681
                continue;
1682
            }
1683
1684
            if ($this->tokens[$x]['code'] !== T_OPEN_CURLY_BRACKET
1685
                || isset($this->tokens[$x]['scope_condition']) === true
1686
            ) {
1687
                // Not a CASE/DEFAULT with a curly brace opener.
1688
                continue;
1689
            }
1690
1691
            // The closer for this CASE/DEFAULT should be the closing curly brace and
1692
            // not whatever it already is. The opener needs to be the opening curly
1693
            // brace so everything matches up.
1694
            $newCloser = $this->tokens[$x]['bracket_closer'];
1695
            foreach ([$i, $x, $newCloser] as $index) {
1696
                $this->tokens[$index]['scope_condition'] = $i;
1697
                $this->tokens[$index]['scope_opener']    = $x;
1698
                $this->tokens[$index]['scope_closer']    = $newCloser;
1699
            }
1700
1701
            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1702
                $line      = $this->tokens[$i]['line'];
1703
                $tokenType = $this->tokens[$i]['type'];
1704
1705
                $oldType = $this->tokens[$scopeOpener]['type'];
1706
                $newType = $this->tokens[$x]['type'];
1707
                echo "\t* token $i ($tokenType) on line $line opener changed from $scopeOpener ($oldType) to $x ($newType)".PHP_EOL;
1708
1709
                $oldType = $this->tokens[$scopeCloser]['type'];
1710
                $newType = $this->tokens[$newCloser]['type'];
1711
                echo "\t* token $i ($tokenType) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL;
1712
            }
1713
1714
            if ($this->tokens[$scopeOpener]['scope_condition'] === $i) {
1715
                unset($this->tokens[$scopeOpener]['scope_condition']);
1716
                unset($this->tokens[$scopeOpener]['scope_opener']);
1717
                unset($this->tokens[$scopeOpener]['scope_closer']);
1718
            }
1719
1720
            if ($this->tokens[$scopeCloser]['scope_condition'] === $i) {
1721
                unset($this->tokens[$scopeCloser]['scope_condition']);
1722
                unset($this->tokens[$scopeCloser]['scope_opener']);
1723
                unset($this->tokens[$scopeCloser]['scope_closer']);
1724
            } else {
1725
                // We were using a shared closer. All tokens that were
1726
                // sharing this closer with us, except for the scope condition
1727
                // and it's opener, need to now point to the new closer.
1728
                $condition = $this->tokens[$scopeCloser]['scope_condition'];
1729
                $start     = ($this->tokens[$condition]['scope_opener'] + 1);
1730
                for ($y = $start; $y < $scopeCloser; $y++) {
1731
                    if (isset($this->tokens[$y]['scope_closer']) === true
1732
                        && $this->tokens[$y]['scope_closer'] === $scopeCloser
1733
                    ) {
1734
                        $this->tokens[$y]['scope_closer'] = $newCloser;
1735
1736
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1737
                            $line      = $this->tokens[$y]['line'];
1738
                            $tokenType = $this->tokens[$y]['type'];
1739
                            $oldType   = $this->tokens[$scopeCloser]['type'];
1740
                            $newType   = $this->tokens[$newCloser]['type'];
1741
                            echo "\t\t* token $y ($tokenType) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL;
1742
                        }
1743
                    }
1744
                }
1745
            }//end if
1746
1747
            unset($this->tokens[$x]['bracket_opener']);
1748
            unset($this->tokens[$x]['bracket_closer']);
1749
            unset($this->tokens[$newCloser]['bracket_opener']);
1750
            unset($this->tokens[$newCloser]['bracket_closer']);
1751
            $this->tokens[$scopeCloser]['conditions'][] = $i;
1752
1753
            // Now fix up all the tokens that think they are
1754
            // inside the CASE/DEFAULT statement when they are really outside.
1755
            for ($x = $newCloser; $x < $scopeCloser; $x++) {
1756
                foreach ($this->tokens[$x]['conditions'] as $num => $oldCond) {
1757
                    if ($oldCond === $this->tokens[$i]['code']) {
1758
                        $oldConditions = $this->tokens[$x]['conditions'];
1759
                        unset($this->tokens[$x]['conditions'][$num]);
1760
1761
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1762
                            $type     = $this->tokens[$x]['type'];
1763
                            $oldConds = '';
1764
                            foreach ($oldConditions as $condition) {
1765
                                $oldConds .= Util\Tokens::tokenName($condition).',';
1766
                            }
1767
1768
                            $oldConds = rtrim($oldConds, ',');
1769
1770
                            $newConds = '';
1771
                            foreach ($this->tokens[$x]['conditions'] as $condition) {
1772
                                $newConds .= Util\Tokens::tokenName($condition).',';
1773
                            }
1774
1775
                            $newConds = rtrim($newConds, ',');
1776
1777
                            echo "\t\t* cleaned $x ($type) *".PHP_EOL;
1778
                            echo "\t\t\t=> conditions changed from $oldConds to $newConds".PHP_EOL;
1779
                        }
1780
1781
                        break;
1782
                    }//end if
1783
                }//end foreach
1784
            }//end for
1785
        }//end for
1786
1787
        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1788
            echo "\t*** END ADDITIONAL PHP PROCESSING ***".PHP_EOL;
1789
        }
1790
1791
    }//end processAdditional()
1792
1793
1794
    /**
1795
     * Takes a token produced from <code>token_get_all()</code> and produces a
1796
     * more uniform token.
1797
     *
1798
     * @param string|array $token The token to convert.
1799
     *
1800
     * @return array The new token.
1801
     */
1802
    public static function standardiseToken($token)
1803
    {
1804
        if (isset($token[1]) === false) {
1805
            if (isset(self::$resolveTokenCache[$token[0]]) === true) {
1806
                return self::$resolveTokenCache[$token[0]];
1807
            }
1808
        } else {
1809
            $cacheKey = null;
1810
            if ($token[0] === T_STRING) {
1811
                $cacheKey = strtolower($token[1]);
1812
            } else if ($token[0] !== T_CURLY_OPEN) {
1813
                $cacheKey = $token[0];
1814
            }
1815
1816
            if ($cacheKey !== null && isset(self::$resolveTokenCache[$cacheKey]) === true) {
1817
                $newToken            = self::$resolveTokenCache[$cacheKey];
1818
                $newToken['content'] = $token[1];
1819
                return $newToken;
1820
            }
1821
        }
1822
1823
        if (isset($token[1]) === false) {
1824
            return self::resolveSimpleToken($token[0]);
1825
        }
1826
1827
        if ($token[0] === T_STRING) {
1828
            switch ($cacheKey) {
1829
            case 'false':
1830
                $newToken['type'] = 'T_FALSE';
1831
                break;
1832
            case 'true':
1833
                $newToken['type'] = 'T_TRUE';
1834
                break;
1835
            case 'null':
1836
                $newToken['type'] = 'T_NULL';
1837
                break;
1838
            case 'self':
1839
                $newToken['type'] = 'T_SELF';
1840
                break;
1841
            case 'parent':
1842
                $newToken['type'] = 'T_PARENT';
1843
                break;
1844
            default:
1845
                $newToken['type'] = 'T_STRING';
1846
                break;
1847
            }
1848
1849
            $newToken['code'] = constant($newToken['type']);
1850
1851
            self::$resolveTokenCache[$cacheKey] = $newToken;
1852
        } else if ($token[0] === T_CURLY_OPEN) {
1853
            $newToken = [
1854
                'code' => T_OPEN_CURLY_BRACKET,
1855
                'type' => 'T_OPEN_CURLY_BRACKET',
1856
            ];
1857
        } else {
1858
            $newToken = [
1859
                'code' => $token[0],
1860
                'type' => token_name($token[0]),
1861
            ];
1862
1863
            self::$resolveTokenCache[$token[0]] = $newToken;
1864
        }//end if
1865
1866
        $newToken['content'] = $token[1];
1867
        return $newToken;
1868
1869
    }//end standardiseToken()
1870
1871
1872
    /**
1873
     * Converts simple tokens into a format that conforms to complex tokens
1874
     * produced by token_get_all().
1875
     *
1876
     * Simple tokens are tokens that are not in array form when produced from
1877
     * token_get_all().
1878
     *
1879
     * @param string $token The simple token to convert.
1880
     *
1881
     * @return array The new token in array format.
1882
     */
1883
    public static function resolveSimpleToken($token)
1884
    {
1885
        $newToken = [];
1886
1887
        switch ($token) {
1888
        case '{':
1889
            $newToken['type'] = 'T_OPEN_CURLY_BRACKET';
1890
            break;
1891
        case '}':
1892
            $newToken['type'] = 'T_CLOSE_CURLY_BRACKET';
1893
            break;
1894
        case '[':
1895
            $newToken['type'] = 'T_OPEN_SQUARE_BRACKET';
1896
            break;
1897
        case ']':
1898
            $newToken['type'] = 'T_CLOSE_SQUARE_BRACKET';
1899
            break;
1900
        case '(':
1901
            $newToken['type'] = 'T_OPEN_PARENTHESIS';
1902
            break;
1903
        case ')':
1904
            $newToken['type'] = 'T_CLOSE_PARENTHESIS';
1905
            break;
1906
        case ':':
1907
            $newToken['type'] = 'T_COLON';
1908
            break;
1909
        case '.':
1910
            $newToken['type'] = 'T_STRING_CONCAT';
1911
            break;
1912
        case ';':
1913
            $newToken['type'] = 'T_SEMICOLON';
1914
            break;
1915
        case '=':
1916
            $newToken['type'] = 'T_EQUAL';
1917
            break;
1918
        case '*':
1919
            $newToken['type'] = 'T_MULTIPLY';
1920
            break;
1921
        case '/':
1922
            $newToken['type'] = 'T_DIVIDE';
1923
            break;
1924
        case '+':
1925
            $newToken['type'] = 'T_PLUS';
1926
            break;
1927
        case '-':
1928
            $newToken['type'] = 'T_MINUS';
1929
            break;
1930
        case '%':
1931
            $newToken['type'] = 'T_MODULUS';
1932
            break;
1933
        case '^':
1934
            $newToken['type'] = 'T_BITWISE_XOR';
1935
            break;
1936
        case '&':
1937
            $newToken['type'] = 'T_BITWISE_AND';
1938
            break;
1939
        case '|':
1940
            $newToken['type'] = 'T_BITWISE_OR';
1941
            break;
1942
        case '<':
1943
            $newToken['type'] = 'T_LESS_THAN';
1944
            break;
1945
        case '>':
1946
            $newToken['type'] = 'T_GREATER_THAN';
1947
            break;
1948
        case '!':
1949
            $newToken['type'] = 'T_BOOLEAN_NOT';
1950
            break;
1951
        case ',':
1952
            $newToken['type'] = 'T_COMMA';
1953
            break;
1954
        case '@':
1955
            $newToken['type'] = 'T_ASPERAND';
1956
            break;
1957
        case '$':
1958
            $newToken['type'] = 'T_DOLLAR';
1959
            break;
1960
        case '`':
1961
            $newToken['type'] = 'T_BACKTICK';
1962
            break;
1963
        default:
1964
            $newToken['type'] = 'T_NONE';
1965
            break;
1966
        }//end switch
1967
1968
        $newToken['code']    = constant($newToken['type']);
1969
        $newToken['content'] = $token;
1970
1971
        self::$resolveTokenCache[$token] = $newToken;
1972
        return $newToken;
1973
1974
    }//end resolveSimpleToken()
1975
1976
1977
}//end class
1978