GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — develop ( ebb7bf...0a5ff4 )
by Chris
02:41 queued 11s
created

PHP_CodeSniffer_Tokenizers_PHP::tokenizeString()   F

Complexity

Conditions 138
Paths > 20000

Size

Total Lines 705
Code Lines 388

Duplication

Lines 129
Ratio 18.3 %

Importance

Changes 0
Metric Value
cc 138
eloc 388
nc 35465286
nop 2
dl 129
loc 705
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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