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 ( 699b70...879176 )
by Chris
13:23
created

PHP_CodeSniffer_File::_createLevelMap()   F

Complexity

Conditions 39
Paths 1960

Size

Total Lines 232
Code Lines 145

Duplication

Lines 106
Ratio 45.69 %

Importance

Changes 0
Metric Value
cc 39
eloc 145
nc 1960
nop 3
dl 106
loc 232
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
 * A PHP_CodeSniffer_File object represents a PHP source file and the tokens
4
 * associated with it.
5
 *
6
 * PHP version 5
7
 *
8
 * @category  PHP
9
 * @package   PHP_CodeSniffer
10
 * @author    Greg Sherwood <[email protected]>
11
 * @author    Marc McIntyre <[email protected]>
12
 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
13
 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
14
 * @link      http://pear.php.net/package/PHP_CodeSniffer
15
 */
16
17
/**
18
 * A PHP_CodeSniffer_File object represents a PHP source file and the tokens
19
 * associated with it.
20
 *
21
 * It provides a means for traversing the token stack, along with
22
 * other token related operations. If a PHP_CodeSniffer_Sniff finds and error or
23
 *  warning within a PHP_CodeSniffer_File, you can raise an error using the
24
 *  addError() or addWarning() methods.
25
 *
26
 * <b>Token Information</b>
27
 *
28
 * Each token within the stack contains information about itself:
29
 *
30
 * <code>
31
 *   array(
32
 *    'code'       => 301,       // the token type code (see token_get_all())
33
 *    'content'    => 'if',      // the token content
34
 *    'type'       => 'T_IF',    // the token name
35
 *    'line'       => 56,        // the line number when the token is located
36
 *    'column'     => 12,        // the column in the line where this token
37
 *                               // starts (starts from 1)
38
 *    'level'      => 2          // the depth a token is within the scopes open
39
 *    'conditions' => array(     // a list of scope condition token
40
 *                               // positions => codes that
41
 *                     2 => 50,  // opened the scopes that this token exists
42
 *                     9 => 353, // in (see conditional tokens section below)
43
 *                    ),
44
 *   );
45
 * </code>
46
 *
47
 * <b>Conditional Tokens</b>
48
 *
49
 * In addition to the standard token fields, conditions contain information to
50
 * determine where their scope begins and ends:
51
 *
52
 * <code>
53
 *   array(
54
 *    'scope_condition' => 38, // the token position of the condition
55
 *    'scope_opener'    => 41, // the token position that started the scope
56
 *    'scope_closer'    => 70, // the token position that ended the scope
57
 *   );
58
 * </code>
59
 *
60
 * The condition, the scope opener and the scope closer each contain this
61
 * information.
62
 *
63
 * <b>Parenthesis Tokens</b>
64
 *
65
 * Each parenthesis token (T_OPEN_PARENTHESIS and T_CLOSE_PARENTHESIS) has a
66
 * reference to their opening and closing parenthesis, one being itself, the
67
 * other being its opposite.
68
 *
69
 * <code>
70
 *   array(
71
 *    'parenthesis_opener' => 34,
72
 *    'parenthesis_closer' => 40,
73
 *   );
74
 * </code>
75
 *
76
 * Some tokens can "own" a set of parenthesis. For example a T_FUNCTION token
77
 * has parenthesis around its argument list. These tokens also have the
78
 * parenthesis_opener and and parenthesis_closer indices. Not all parenthesis
79
 * have owners, for example parenthesis used for arithmetic operations and
80
 * function calls. The parenthesis tokens that have an owner have the following
81
 * auxiliary array indices.
82
 *
83
 * <code>
84
 *   array(
85
 *    'parenthesis_opener' => 34,
86
 *    'parenthesis_closer' => 40,
87
 *    'parenthesis_owner'  => 33,
88
 *   );
89
 * </code>
90
 *
91
 * Each token within a set of parenthesis also has an array index
92
 * 'nested_parenthesis' which is an array of the
93
 * left parenthesis => right parenthesis token positions.
94
 *
95
 * <code>
96
 *   'nested_parenthesis' => array(
97
 *                             12 => 15
98
 *                             11 => 14
99
 *                            );
100
 * </code>
101
 *
102
 * <b>Extended Tokens</b>
103
 *
104
 * PHP_CodeSniffer extends and augments some of the tokens created by
105
 * <i>token_get_all()</i>. A full list of these tokens can be seen in the
106
 * <i>Tokens.php</i> file.
107
 *
108
 * @category  PHP
109
 * @package   PHP_CodeSniffer
110
 * @author    Greg Sherwood <[email protected]>
111
 * @author    Marc McIntyre <[email protected]>
112
 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
113
 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
114
 * @version   Release: @package_version@
115
 * @link      http://pear.php.net/package/PHP_CodeSniffer
116
 */
117
class PHP_CodeSniffer_File
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
118
{
119
120
    /**
121
     * The absolute path to the file associated with this object.
122
     *
123
     * @var string
124
     */
125
    private $_file = '';
126
127
    /**
128
     * The EOL character this file uses.
129
     *
130
     * @var string
131
     */
132
    public $eolChar = '';
133
134
    /**
135
     * The PHP_CodeSniffer object controlling this run.
136
     *
137
     * @var PHP_CodeSniffer
138
     */
139
    public $phpcs = null;
140
141
    /**
142
     * The Fixer object to control fixing errors.
143
     *
144
     * @var PHP_CodeSniffer_Fixer
145
     */
146
    public $fixer = null;
147
148
    /**
149
     * The tokenizer being used for this file.
150
     *
151
     * @var object
152
     */
153
    public $tokenizer = null;
154
155
    /**
156
     * The tokenizer being used for this file.
157
     *
158
     * @var string
159
     */
160
    public $tokenizerType = 'PHP';
161
162
    /**
163
     * The number of tokens in this file.
164
     *
165
     * Stored here to save calling count() everywhere.
166
     *
167
     * @var int
168
     */
169
    public $numTokens = 0;
170
171
    /**
172
     * The tokens stack map.
173
     *
174
     * Note that the tokens in this array differ in format to the tokens
175
     * produced by token_get_all(). Tokens are initially produced with
176
     * token_get_all(), then augmented so that it's easier to process them.
177
     *
178
     * @var array()
179
     * @see Tokens.php
180
     */
181
    private $_tokens = array();
182
183
    /**
184
     * The errors raised from PHP_CodeSniffer_Sniffs.
185
     *
186
     * @var array()
187
     * @see getErrors()
188
     */
189
    private $_errors = array();
190
191
    /**
192
     * The warnings raised from PHP_CodeSniffer_Sniffs.
193
     *
194
     * @var array()
195
     * @see getWarnings()
196
     */
197
    private $_warnings = array();
198
199
    /**
200
     * The metrics recorded from PHP_CodeSniffer_Sniffs.
201
     *
202
     * @var array()
203
     * @see getMetrics()
204
     */
205
    private $_metrics = array();
206
207
    /**
208
     * Record the errors and warnings raised.
209
     *
210
     * @var bool
211
     */
212
    private $_recordErrors = true;
213
214
    /**
215
     * An array of lines that are being ignored.
216
     *
217
     * @var array()
218
     */
219
    private static $_ignoredLines = array();
220
221
    /**
222
     * An array of sniffs that are being ignored.
223
     *
224
     * @var array()
225
     */
226
    private $_ignoredListeners = array();
227
228
    /**
229
     * An array of message codes that are being ignored.
230
     *
231
     * @var array()
232
     */
233
    private $_ignoredCodes = array();
234
235
    /**
236
     * The total number of errors raised.
237
     *
238
     * @var int
239
     */
240
    private $_errorCount = 0;
241
242
    /**
243
     * The total number of warnings raised.
244
     *
245
     * @var int
246
     */
247
    private $_warningCount = 0;
248
249
    /**
250
     * The total number of errors/warnings that can be fixed.
251
     *
252
     * @var int
253
     */
254
    private $_fixableCount = 0;
255
256
    /**
257
     * An array of sniffs listening to this file's processing.
258
     *
259
     * @var array(PHP_CodeSniffer_Sniff)
260
     */
261
    private $_listeners = array();
262
263
    /**
264
     * The class name of the sniff currently processing the file.
265
     *
266
     * @var string
267
     */
268
    private $_activeListener = '';
269
270
    /**
271
     * An array of sniffs being processed and how long they took.
272
     *
273
     * @var array()
274
     */
275
    private $_listenerTimes = array();
276
277
    /**
278
     * An array of rules from the ruleset.xml file.
279
     *
280
     * This value gets set by PHP_CodeSniffer when the object is created.
281
     * It may be empty, indicating that the ruleset does not override
282
     * any of the default sniff settings.
283
     *
284
     * @var array
285
     */
286
    protected $ruleset = array();
287
288
289
    /**
290
     * Constructs a PHP_CodeSniffer_File.
291
     *
292
     * @param string          $file      The absolute path to the file to process.
293
     * @param array(string)   $listeners The initial listeners listening to processing of this file.
0 ignored issues
show
Documentation introduced by
The doc-type array(string) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
294
     *                                   to processing of this file.
295
     * @param array           $ruleset   An array of rules from the ruleset.xml file.
296
     *                                   ruleset.xml file.
297
     * @param PHP_CodeSniffer $phpcs     The PHP_CodeSniffer object controlling this run.
298
     *                                   this run.
299
     *
300
     * @throws PHP_CodeSniffer_Exception If the register() method does
301
     *                                   not return an array.
302
     */
303
    public function __construct(
304
        $file,
305
        array $listeners,
306
        array $ruleset,
307
        PHP_CodeSniffer $phpcs
308
    ) {
309
        $this->_file      = trim($file);
310
        $this->_listeners = $listeners;
311
        $this->ruleset    = $ruleset;
312
        $this->phpcs      = $phpcs;
313
        $this->fixer      = new PHP_CodeSniffer_Fixer();
314
315
        if (PHP_CODESNIFFER_INTERACTIVE === false) {
316
            $cliValues = $phpcs->cli->getCommandLineValues();
317
            if (isset($cliValues['showSources']) === true
318
                && $cliValues['showSources'] !== true
319
            ) {
320
                $recordErrors = false;
321
                foreach ($cliValues['reports'] as $report => $output) {
322
                    $reportClass = $phpcs->reporting->factory($report);
323
                    if (property_exists($reportClass, 'recordErrors') === false
324
                        || $reportClass->recordErrors === true
0 ignored issues
show
Bug introduced by
Accessing recordErrors on the interface PHP_CodeSniffer_Report suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
325
                    ) {
326
                        $recordErrors = true;
327
                        break;
328
                    }
329
                }
330
331
                $this->_recordErrors = $recordErrors;
332
            }
333
        }
334
335
    }//end __construct()
336
337
338
    /**
339
     * Sets the name of the currently active sniff.
340
     *
341
     * @param string $activeListener The class name of the current sniff.
342
     *
343
     * @return void
344
     */
345
    public function setActiveListener($activeListener)
346
    {
347
        $this->_activeListener = $activeListener;
348
349
    }//end setActiveListener()
350
351
352
    /**
353
     * Adds a listener to the token stack that listens to the specific tokens.
354
     *
355
     * When PHP_CodeSniffer encounters on the the tokens specified in $tokens,
356
     * it invokes the process method of the sniff.
357
     *
358
     * @param PHP_CodeSniffer_Sniff $listener The listener to add to the
359
     *                                        listener stack.
360
     * @param array(int)            $tokens   The token types the listener wishes to
0 ignored issues
show
Documentation introduced by
The doc-type array(int) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
361
     *                                        listen to.
362
     *
363
     * @return void
364
     */
365
    public function addTokenListener(PHP_CodeSniffer_Sniff $listener, array $tokens)
366
    {
367
        $class = get_class($listener);
368
        foreach ($tokens as $token) {
369
            if (isset($this->_listeners[$token]) === false) {
370
                $this->_listeners[$token] = array();
371
            }
372
373
            if (isset($this->_listeners[$token][$class]) === false) {
374
                $this->_listeners[$token][$class] = $listener;
375
            }
376
        }
377
378
    }//end addTokenListener()
379
380
381
    /**
382
     * Removes a listener from listening from the specified tokens.
383
     *
384
     * @param PHP_CodeSniffer_Sniff $listener The listener to remove from the
385
     *                                        listener stack.
386
     * @param array(int)            $tokens   The token types the listener wishes to
0 ignored issues
show
Documentation introduced by
The doc-type array(int) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
387
     *                                        stop listen to.
388
     *
389
     * @return void
390
     */
391
    public function removeTokenListener(
392
        PHP_CodeSniffer_Sniff $listener,
393
        array $tokens
394
    ) {
395
        $class = get_class($listener);
396
        foreach ($tokens as $token) {
397
            if (isset($this->_listeners[$token]) === false) {
398
                continue;
399
            }
400
401
            unset($this->_listeners[$token][$class]);
402
        }
403
404
    }//end removeTokenListener()
405
406
407
    /**
408
     * Rebuilds the list of listeners to ensure their state is cleared.
409
     *
410
     * @return void
411
     */
412
    public function refreshTokenListeners()
413
    {
414
        $this->phpcs->populateTokenListeners();
415
        $this->_listeners = $this->phpcs->getTokenSniffs();
416
417
    }//end refreshTokenListeners()
418
419
420
    /**
421
     * Returns the token stack for this file.
422
     *
423
     * @return array
424
     */
425
    public function getTokens()
426
    {
427
        return $this->_tokens;
428
429
    }//end getTokens()
430
431
432
    /**
433
     * Starts the stack traversal and tells listeners when tokens are found.
434
     *
435
     * @param string $contents The contents to parse. If NULL, the content
436
     *                         is taken from the file system.
437
     *
438
     * @return void
439
     */
440
    public function start($contents=null)
441
    {
442
        $this->_errors       = array();
443
        $this->_warnings     = array();
444
        $this->_errorCount   = 0;
445
        $this->_warningCount = 0;
446
        $this->_fixableCount = 0;
447
448
        // Reset the ignored lines because lines numbers may have changed
449
        // if we are fixing this file.
450
        self::$_ignoredLines = array();
451
452
        try {
453
            $this->eolChar = self::detectLineEndings($this->_file, $contents);
454
        } catch (PHP_CodeSniffer_Exception $e) {
455
            $this->addWarning($e->getMessage(), null, 'Internal.DetectLineEndings');
456
            return;
457
        }
458
459
        // If this is standard input, see if a filename was passed in as well.
460
        // This is done by including: phpcs_input_file: [file path]
461
        // as the first line of content.
462
        if ($this->_file === 'STDIN') {
463
            $cliValues = $this->phpcs->cli->getCommandLineValues();
464
            if ($cliValues['stdinPath'] !== '') {
465
                $this->_file = $cliValues['stdinPath'];
466
            } else if ($contents !== null && substr($contents, 0, 17) === 'phpcs_input_file:') {
467
                $eolPos      = strpos($contents, $this->eolChar);
468
                $filename    = trim(substr($contents, 17, ($eolPos - 17)));
469
                $contents    = substr($contents, ($eolPos + strlen($this->eolChar)));
470
                $this->_file = $filename;
471
            }
472
        }
473
474
        $this->_parse($contents);
475
        $this->fixer->startFile($this);
476
477
        if (PHP_CODESNIFFER_VERBOSITY > 2) {
478
            echo "\t*** START TOKEN PROCESSING ***".PHP_EOL;
479
        }
480
481
        $foundCode        = false;
482
        $listeners        = $this->phpcs->getSniffs();
483
        $listenerIgnoreTo = array();
484
        $inTests          = defined('PHP_CODESNIFFER_IN_TESTS');
485
486
        // Foreach of the listeners that have registered to listen for this
487
        // token, get them to process it.
488
        foreach ($this->_tokens as $stackPtr => $token) {
489
            // Check for ignored lines.
490
            if ($token['code'] === T_COMMENT
491
                || $token['code'] === T_DOC_COMMENT_TAG
492
                || ($inTests === true && $token['code'] === T_INLINE_HTML)
493
            ) {
494
                if (strpos($token['content'], '@codingStandards') !== false) {
495
                    if (strpos($token['content'], '@codingStandardsIgnoreFile') !== false) {
496
                        // Ignoring the whole file, just a little late.
497
                        $this->_errors       = array();
498
                        $this->_warnings     = array();
499
                        $this->_errorCount   = 0;
500
                        $this->_warningCount = 0;
501
                        $this->_fixableCount = 0;
502
                        return;
503
                    } else if (strpos($token['content'], '@codingStandardsChangeSetting') !== false) {
504
                        $start   = strpos($token['content'], '@codingStandardsChangeSetting');
505
                        $comment = substr($token['content'], ($start + 30));
506
                        $parts   = explode(' ', $comment);
507
                        if (count($parts) >= 3
508
                            && isset($this->phpcs->sniffCodes[$parts[0]]) === true
509
                        ) {
510
                            $listenerCode  = array_shift($parts);
511
                            $propertyCode  = array_shift($parts);
512
                            $propertyValue = rtrim(implode(' ', $parts), " */\r\n");
513
                            $listenerClass = $this->phpcs->sniffCodes[$listenerCode];
514
                            $this->phpcs->setSniffProperty($listenerClass, $propertyCode, $propertyValue);
515
                        }
516
                    }//end if
517
                }//end if
518
            }//end if
519
520
            if (PHP_CODESNIFFER_VERBOSITY > 2) {
521
                $type    = $token['type'];
522
                $content = PHP_CodeSniffer::prepareForOutput($token['content']);
523
                echo "\t\tProcess token $stackPtr: $type => $content".PHP_EOL;
524
            }
525
526
            if ($token['code'] !== T_INLINE_HTML) {
527
                $foundCode = true;
528
            }
529
530
            if (isset($this->_listeners[$token['code']]) === false) {
531
                continue;
532
            }
533
534
            foreach ($this->_listeners[$token['code']] as $listenerData) {
535
                if (isset($this->_ignoredListeners[$listenerData['class']]) === true
536
                    || (isset($listenerIgnoreTo[$listenerData['class']]) === true
537
                    && $listenerIgnoreTo[$listenerData['class']] > $stackPtr)
538
                ) {
539
                    // This sniff is ignoring past this token, or the whole file.
540
                    continue;
541
                }
542
543
                // Make sure this sniff supports the tokenizer
544
                // we are currently using.
545
                $class = $listenerData['class'];
546
547
                if (isset($listenerData['tokenizers'][$this->tokenizerType]) === false) {
548
                    continue;
549
                }
550
551
                // If the file path matches one of our ignore patterns, skip it.
552
                // While there is support for a type of each pattern
553
                // (absolute or relative) we don't actually support it here.
554
                foreach ($listenerData['ignore'] as $pattern) {
555
                    // We assume a / directory separator, as do the exclude rules
556
                    // most developers write, so we need a special case for any system
557
                    // that is different.
558
                    if (DIRECTORY_SEPARATOR === '\\') {
559
                        $pattern = str_replace('/', '\\\\', $pattern);
560
                    }
561
562
                    $pattern = '`'.$pattern.'`i';
563
                    if (preg_match($pattern, $this->_file) === 1) {
564
                        $this->_ignoredListeners[$class] = true;
565
                        continue(2);
566
                    }
567
                }
568
569
                $this->_activeListener = $class;
570
571
                if (PHP_CODESNIFFER_VERBOSITY > 2) {
572
                    $startTime = microtime(true);
573
                    echo "\t\t\tProcessing ".$this->_activeListener.'... ';
574
                }
575
576
                $ignoreTo = $listeners[$class]->process($this, $stackPtr);
577
                if ($ignoreTo !== null) {
578
                    $listenerIgnoreTo[$this->_activeListener] = $ignoreTo;
579
                }
580
581
                if (PHP_CODESNIFFER_VERBOSITY > 2) {
582
                    $timeTaken = (microtime(true) - $startTime);
0 ignored issues
show
Bug introduced by
The variable $startTime does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
583
                    if (isset($this->_listenerTimes[$this->_activeListener]) === false) {
584
                        $this->_listenerTimes[$this->_activeListener] = 0;
585
                    }
586
587
                    $this->_listenerTimes[$this->_activeListener] += $timeTaken;
588
589
                    $timeTaken = round(($timeTaken), 4);
590
                    echo "DONE in $timeTaken seconds".PHP_EOL;
591
                }
592
593
                $this->_activeListener = '';
594
            }//end foreach
595
        }//end foreach
596
597
        if ($this->_recordErrors === false) {
598
            $this->_errors   = array();
599
            $this->_warnings = array();
600
        }
601
602
        // If short open tags are off but the file being checked uses
603
        // short open tags, the whole content will be inline HTML
604
        // and nothing will be checked. So try and handle this case.
605
        if ($foundCode === false && $this->tokenizerType === 'PHP') {
606
            $shortTags = (bool) ini_get('short_open_tag');
607
            if ($shortTags === false) {
608
                $error = 'No PHP code was found in this file and short open tags are not allowed by this install of PHP. This file may be using short open tags but PHP does not allow them.';
609
                $this->addWarning($error, null, 'Internal.NoCodeFound');
610
            }
611
        }
612
613
        if (PHP_CODESNIFFER_VERBOSITY > 2) {
614
            echo "\t*** END TOKEN PROCESSING ***".PHP_EOL;
615
            echo "\t*** START SNIFF PROCESSING REPORT ***".PHP_EOL;
616
617
            asort($this->_listenerTimes, SORT_NUMERIC);
618
            $this->_listenerTimes = array_reverse($this->_listenerTimes, true);
619
            foreach ($this->_listenerTimes as $listener => $timeTaken) {
620
                echo "\t$listener: ".round(($timeTaken), 4).' secs'.PHP_EOL;
621
            }
622
623
            echo "\t*** END SNIFF PROCESSING REPORT ***".PHP_EOL;
624
        }
625
626
    }//end start()
627
628
629
    /**
630
     * Remove vars stored in this file that are no longer required.
631
     *
632
     * @return void
633
     */
634
    public function cleanUp()
635
    {
636
        $this->_tokens    = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $_tokens.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
637
        $this->_listeners = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $_listeners.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
638
639
    }//end cleanUp()
640
641
642
    /**
643
     * Tokenizes the file and prepares it for the test run.
644
     *
645
     * @param string $contents The contents to parse. If NULL, the content
646
     *                         is taken from the file system.
647
     *
648
     * @return void
649
     */
650
    private function _parse($contents=null)
651
    {
652
        if ($contents === null && empty($this->_tokens) === false) {
653
            // File has already been parsed.
654
            return;
655
        }
656
657
        $stdin     = false;
658
        $cliValues = $this->phpcs->cli->getCommandLineValues();
659
        if (empty($cliValues['files']) === true) {
660
            $stdin = true;
661
        }
662
663
        // Determine the tokenizer from the file extension.
664
        $fileParts = explode('.', $this->_file);
665
        $extension = array_pop($fileParts);
666
        if (isset($this->phpcs->allowedFileExtensions[$extension]) === true) {
667
            $tokenizerClass      = 'PHP_CodeSniffer_Tokenizers_'.$this->phpcs->allowedFileExtensions[$extension];
668
            $this->tokenizerType = $this->phpcs->allowedFileExtensions[$extension];
669
        } else if (isset($this->phpcs->defaultFileExtensions[$extension]) === true) {
670
            $tokenizerClass      = 'PHP_CodeSniffer_Tokenizers_'.$this->phpcs->defaultFileExtensions[$extension];
671
            $this->tokenizerType = $this->phpcs->defaultFileExtensions[$extension];
672
        } else {
673
            // Revert to default.
674
            $tokenizerClass = 'PHP_CodeSniffer_Tokenizers_'.$this->tokenizerType;
675
        }
676
677
        $tokenizer       = new $tokenizerClass();
678
        $this->tokenizer = $tokenizer;
679
680
        if ($contents === null) {
681
            $contents = file_get_contents($this->_file);
682
        }
683
684
        try {
685
            $tabWidth = null;
686
            $encoding = null;
687
            if (defined('PHP_CODESNIFFER_IN_TESTS') === true) {
688
                $cliValues = $this->phpcs->cli->getCommandLineValues();
689
                if (isset($cliValues['tabWidth']) === true) {
690
                    $tabWidth = $cliValues['tabWidth'];
691
                }
692
693
                if (isset($cliValues['encoding']) === true) {
694
                    $encoding = $cliValues['encoding'];
695
                }
696
            }
697
698
            $this->_tokens = self::tokenizeString($contents, $tokenizer, $this->eolChar, $tabWidth, $encoding);
699
        } catch (PHP_CodeSniffer_Exception $e) {
700
            $this->addWarning($e->getMessage(), null, 'Internal.Tokenizer.Exception');
701
            if (PHP_CODESNIFFER_VERBOSITY > 0 || (PHP_CODESNIFFER_CBF === true && $stdin === false)) {
702
                echo "[$this->tokenizerType => tokenizer error]... ";
703
                if (PHP_CODESNIFFER_VERBOSITY > 1) {
704
                    echo PHP_EOL;
705
                }
706
            }
707
708
            return;
709
        }//end try
710
711
        $this->numTokens = count($this->_tokens);
712
713
        // Check for mixed line endings as these can cause tokenizer errors and we
714
        // should let the user know that the results they get may be incorrect.
715
        // This is done by removing all backslashes, removing the newline char we
716
        // detected, then converting newlines chars into text. If any backslashes
717
        // are left at the end, we have additional newline chars in use.
718
        $contents = str_replace('\\', '', $contents);
719
        $contents = str_replace($this->eolChar, '', $contents);
720
        $contents = str_replace("\n", '\n', $contents);
721
        $contents = str_replace("\r", '\r', $contents);
722
        if (strpos($contents, '\\') !== false) {
723
            $error = 'File has mixed line endings; this may cause incorrect results';
724
            $this->addWarning($error, 0, 'Internal.LineEndings.Mixed');
725
        }
726
727
        if (PHP_CODESNIFFER_VERBOSITY > 0 || (PHP_CODESNIFFER_CBF === true && $stdin === false)) {
728
            if ($this->numTokens === 0) {
729
                $numLines = 0;
730
            } else {
731
                $numLines = $this->_tokens[($this->numTokens - 1)]['line'];
732
            }
733
734
            echo "[$this->tokenizerType => $this->numTokens tokens in $numLines lines]... ";
735
            if (PHP_CODESNIFFER_VERBOSITY > 1) {
736
                echo PHP_EOL;
737
            }
738
        }
739
740
    }//end _parse()
741
742
743
    /**
744
     * Opens a file and detects the EOL character being used.
745
     *
746
     * @param string $file     The full path to the file.
747
     * @param string $contents The contents to parse. If NULL, the content
748
     *                         is taken from the file system.
749
     *
750
     * @return string
751
     * @throws PHP_CodeSniffer_Exception If $file could not be opened.
752
     */
753
    public static function detectLineEndings($file, $contents=null)
754
    {
755
        if ($contents === null) {
756
            // Determine the newline character being used in this file.
757
            // Will be either \r, \r\n or \n.
758
            if (is_readable($file) === false) {
759
                $error = 'Error opening file; file no longer exists or you do not have access to read the file';
760
                throw new PHP_CodeSniffer_Exception($error);
761
            } else {
762
                $handle = fopen($file, 'r');
763
                if ($handle === false) {
764
                    $error = 'Error opening file; could not auto-detect line endings';
765
                    throw new PHP_CodeSniffer_Exception($error);
766
                }
767
            }
768
769
            $firstLine = fgets($handle);
770
            fclose($handle);
771
772
            $eolChar = substr($firstLine, -1);
773
            if ($eolChar === "\n") {
774
                $secondLastChar = substr($firstLine, -2, 1);
775
                if ($secondLastChar === "\r") {
776
                    $eolChar = "\r\n";
777
                }
778
            } else if ($eolChar !== "\r") {
779
                // Must not be an EOL char at the end of the line.
780
                // Probably a one-line file, so assume \n as it really
781
                // doesn't matter considering there are no newlines.
782
                $eolChar = "\n";
783
            }
784
        } else {
785
            if (preg_match("/\r\n?|\n/", $contents, $matches) !== 1) {
786
                // Assuming there are no newlines.
787
                $eolChar = "\n";
788
            } else {
789
                $eolChar = $matches[0];
790
            }
791
        }//end if
792
793
        return $eolChar;
794
795
    }//end detectLineEndings()
796
797
798
    /**
799
     * Records an error against a specific token in the file.
800
     *
801
     * @param string  $error    The error message.
802
     * @param int     $stackPtr The stack position where the error occurred.
803
     * @param string  $code     A violation code unique to the sniff message.
804
     * @param array   $data     Replacements for the error message.
805
     * @param int     $severity The severity level for this error. A value of 0
806
     *                          will be converted into the default severity level.
807
     * @param boolean $fixable  Can the error be fixed by the sniff?
808
     *
809
     * @return boolean
810
     */
811 View Code Duplication
    public function addError(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
812
        $error,
813
        $stackPtr,
814
        $code='',
815
        $data=array(),
816
        $severity=0,
817
        $fixable=false
818
    ) {
819
        if ($stackPtr === null) {
820
            $line   = 1;
821
            $column = 1;
822
        } else {
823
            $line   = $this->_tokens[$stackPtr]['line'];
824
            $column = $this->_tokens[$stackPtr]['column'];
825
        }
826
827
        return $this->_addError($error, $line, $column, $code, $data, $severity, $fixable);
828
829
    }//end addError()
830
831
832
    /**
833
     * Records a warning against a specific token in the file.
834
     *
835
     * @param string  $warning  The error message.
836
     * @param int     $stackPtr The stack position where the error occurred.
837
     * @param string  $code     A violation code unique to the sniff message.
838
     * @param array   $data     Replacements for the warning message.
839
     * @param int     $severity The severity level for this warning. A value of 0
840
     *                          will be converted into the default severity level.
841
     * @param boolean $fixable  Can the warning be fixed by the sniff?
842
     *
843
     * @return boolean
844
     */
845 View Code Duplication
    public function addWarning(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
846
        $warning,
847
        $stackPtr,
848
        $code='',
849
        $data=array(),
850
        $severity=0,
851
        $fixable=false
852
    ) {
853
        if ($stackPtr === null) {
854
            $line   = 1;
855
            $column = 1;
856
        } else {
857
            $line   = $this->_tokens[$stackPtr]['line'];
858
            $column = $this->_tokens[$stackPtr]['column'];
859
        }
860
861
        return $this->_addWarning($warning, $line, $column, $code, $data, $severity, $fixable);
862
863
    }//end addWarning()
864
865
866
    /**
867
     * Records an error against a specific line in the file.
868
     *
869
     * @param string $error    The error message.
870
     * @param int    $line     The line on which the error occurred.
871
     * @param string $code     A violation code unique to the sniff message.
872
     * @param array  $data     Replacements for the error message.
873
     * @param int    $severity The severity level for this error. A value of 0
874
     *                         will be converted into the default severity level.
875
     *
876
     * @return boolean
877
     */
878
    public function addErrorOnLine(
879
        $error,
880
        $line,
881
        $code='',
882
        $data=array(),
883
        $severity=0
884
    ) {
885
        return $this->_addError($error, $line, 1, $code, $data, $severity, false);
886
887
    }//end addErrorOnLine()
888
889
890
    /**
891
     * Records a warning against a specific token in the file.
892
     *
893
     * @param string $warning  The error message.
894
     * @param int    $line     The line on which the warning occurred.
895
     * @param string $code     A violation code unique to the sniff message.
896
     * @param array  $data     Replacements for the warning message.
897
     * @param int    $severity The severity level for this warning. A value of 0
898
     *                         will be converted into the default severity level.
899
     *
900
     * @return boolean
901
     */
902
    public function addWarningOnLine(
903
        $warning,
904
        $line,
905
        $code='',
906
        $data=array(),
907
        $severity=0
908
    ) {
909
        return $this->_addWarning($warning, $line, 1, $code, $data, $severity, false);
910
911
    }//end addWarningOnLine()
912
913
914
    /**
915
     * Records a fixable error against a specific token in the file.
916
     *
917
     * Returns true if the error was recorded and should be fixed.
918
     *
919
     * @param string $error    The error message.
920
     * @param int    $stackPtr The stack position where the error occurred.
921
     * @param string $code     A violation code unique to the sniff message.
922
     * @param array  $data     Replacements for the error message.
923
     * @param int    $severity The severity level for this error. A value of 0
924
     *                         will be converted into the default severity level.
925
     *
926
     * @return boolean
927
     */
928 View Code Duplication
    public function addFixableError(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
929
        $error,
930
        $stackPtr,
931
        $code='',
932
        $data=array(),
933
        $severity=0
934
    ) {
935
        $recorded = $this->addError($error, $stackPtr, $code, $data, $severity, true);
936
        if ($recorded === true && $this->fixer->enabled === true) {
937
            return true;
938
        }
939
940
        return false;
941
942
    }//end addFixableError()
943
944
945
    /**
946
     * Records a fixable warning against a specific token in the file.
947
     *
948
     * Returns true if the warning was recorded and should be fixed.
949
     *
950
     * @param string $warning  The error message.
951
     * @param int    $stackPtr The stack position where the error occurred.
952
     * @param string $code     A violation code unique to the sniff message.
953
     * @param array  $data     Replacements for the warning message.
954
     * @param int    $severity The severity level for this warning. A value of 0
955
     *                         will be converted into the default severity level.
956
     *
957
     * @return boolean
958
     */
959 View Code Duplication
    public function addFixableWarning(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
960
        $warning,
961
        $stackPtr,
962
        $code='',
963
        $data=array(),
964
        $severity=0
965
    ) {
966
        $recorded = $this->addWarning($warning, $stackPtr, $code, $data, $severity, true);
967
        if ($recorded === true && $this->fixer->enabled === true) {
968
            return true;
969
        }
970
971
        return false;
972
973
    }//end addFixableWarning()
974
975
976
    /**
977
     * Adds an error to the error stack.
978
     *
979
     * @param string  $error    The error message.
980
     * @param int     $line     The line on which the error occurred.
981
     * @param int     $column   The column at which the error occurred.
982
     * @param string  $code     A violation code unique to the sniff message.
983
     * @param array   $data     Replacements for the error message.
984
     * @param int     $severity The severity level for this error. A value of 0
985
     *                          will be converted into the default severity level.
986
     * @param boolean $fixable  Can the error be fixed by the sniff?
987
     *
988
     * @return boolean
989
     */
990 View Code Duplication
    private function _addError($error, $line, $column, $code, $data, $severity, $fixable)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
991
    {
992
        if (isset(self::$_ignoredLines[$line]) === true) {
993
            return false;
994
        }
995
996
        // Work out which sniff generated the error.
997
        if (substr($code, 0, 9) === 'Internal.') {
998
            // Any internal message.
999
            $sniffCode = $code;
1000
        } else {
1001
            $parts = explode('_', str_replace('\\', '_', $this->_activeListener));
1002
            if (isset($parts[3]) === true) {
1003
                $sniff = $parts[0].'.'.$parts[2].'.'.$parts[3];
1004
1005
                // Remove "Sniff" from the end.
1006
                $sniff = substr($sniff, 0, -5);
1007
            } else {
1008
                $sniff = 'unknownSniff';
1009
            }
1010
1011
            $sniffCode = $sniff;
1012
            if ($code !== '') {
1013
                $sniffCode .= '.'.$code;
1014
            }
1015
        }//end if
1016
1017
        // If we know this sniff code is being ignored for this file, return early.
1018
        if (isset($this->_ignoredCodes[$sniffCode]) === true) {
1019
            return false;
1020
        }
1021
1022
        // Make sure this message type has not been set to "warning".
1023
        if (isset($this->ruleset[$sniffCode]['type']) === true
1024
            && $this->ruleset[$sniffCode]['type'] === 'warning'
1025
        ) {
1026
            // Pass this off to the warning handler.
1027
            return $this->_addWarning($error, $line, $column, $code, $data, $severity, $fixable);
1028
        } else if ($this->phpcs->cli->errorSeverity === 0) {
1029
            // Don't bother doing any processing as errors are just going to
1030
            // be hidden in the reports anyway.
1031
            return false;
1032
        }
1033
1034
        // Make sure we are interested in this severity level.
1035
        if (isset($this->ruleset[$sniffCode]['severity']) === true) {
1036
            $severity = $this->ruleset[$sniffCode]['severity'];
1037
        } else if ($severity === 0) {
1038
            $severity = PHPCS_DEFAULT_ERROR_SEV;
1039
        }
1040
1041
        if ($this->phpcs->cli->errorSeverity > $severity) {
1042
            return false;
1043
        }
1044
1045
        // Make sure we are not ignoring this file.
1046
        $patterns = $this->phpcs->getIgnorePatterns($sniffCode);
1047
        foreach ($patterns as $pattern => $type) {
1048
            // While there is support for a type of each pattern
1049
            // (absolute or relative) we don't actually support it here.
1050
            $replacements = array(
1051
                             '\\,' => ',',
1052
                             '*'   => '.*',
1053
                            );
1054
1055
            // We assume a / directory separator, as do the exclude rules
1056
            // most developers write, so we need a special case for any system
1057
            // that is different.
1058
            if (DIRECTORY_SEPARATOR === '\\') {
1059
                $replacements['/'] = '\\\\';
1060
            }
1061
1062
            $pattern = '`'.strtr($pattern, $replacements).'`i';
1063
            if (preg_match($pattern, $this->_file) === 1) {
1064
                $this->_ignoredCodes[$sniffCode] = true;
1065
                return false;
1066
            }
1067
        }//end foreach
1068
1069
        $this->_errorCount++;
1070
        if ($fixable === true) {
1071
            $this->_fixableCount++;
1072
        }
1073
1074
        if ($this->_recordErrors === false) {
1075
            if (isset($this->_errors[$line]) === false) {
1076
                $this->_errors[$line] = 0;
1077
            }
1078
1079
            $this->_errors[$line]++;
1080
            return true;
1081
        }
1082
1083
        // Work out the error message.
1084
        if (isset($this->ruleset[$sniffCode]['message']) === true) {
1085
            $error = $this->ruleset[$sniffCode]['message'];
1086
        }
1087
1088
        if (empty($data) === true) {
1089
            $message = $error;
1090
        } else {
1091
            $message = vsprintf($error, $data);
1092
        }
1093
1094
        if (isset($this->_errors[$line]) === false) {
1095
            $this->_errors[$line] = array();
1096
        }
1097
1098
        if (isset($this->_errors[$line][$column]) === false) {
1099
            $this->_errors[$line][$column] = array();
1100
        }
1101
1102
        $this->_errors[$line][$column][] = array(
1103
                                            'message'  => $message,
1104
                                            'source'   => $sniffCode,
1105
                                            'severity' => $severity,
1106
                                            'fixable'  => $fixable,
1107
                                           );
1108
1109
        if (PHP_CODESNIFFER_VERBOSITY > 1
1110
            && $this->fixer->enabled === true
1111
            && $fixable === true
1112
        ) {
1113
            @ob_end_clean();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1114
            echo "\tE: [Line $line] $message ($sniffCode)".PHP_EOL;
1115
            ob_start();
1116
        }
1117
1118
        return true;
1119
1120
    }//end _addError()
1121
1122
1123
    /**
1124
     * Adds an warning to the warning stack.
1125
     *
1126
     * @param string  $warning  The error message.
1127
     * @param int     $line     The line on which the warning occurred.
1128
     * @param int     $column   The column at which the warning occurred.
1129
     * @param string  $code     A violation code unique to the sniff message.
1130
     * @param array   $data     Replacements for the warning message.
1131
     * @param int     $severity The severity level for this warning. A value of 0
1132
     *                          will be converted into the default severity level.
1133
     * @param boolean $fixable  Can the warning be fixed by the sniff?
1134
     *
1135
     * @return boolean
1136
     */
1137 View Code Duplication
    private function _addWarning($warning, $line, $column, $code, $data, $severity, $fixable)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1138
    {
1139
        if (isset(self::$_ignoredLines[$line]) === true) {
1140
            return false;
1141
        }
1142
1143
        // Work out which sniff generated the warning.
1144
        if (substr($code, 0, 9) === 'Internal.') {
1145
            // Any internal message.
1146
            $sniffCode = $code;
1147
        } else {
1148
            $parts = explode('_', str_replace('\\', '_', $this->_activeListener));
1149
            if (isset($parts[3]) === true) {
1150
                $sniff = $parts[0].'.'.$parts[2].'.'.$parts[3];
1151
1152
                // Remove "Sniff" from the end.
1153
                $sniff = substr($sniff, 0, -5);
1154
            } else {
1155
                $sniff = 'unknownSniff';
1156
            }
1157
1158
            $sniffCode = $sniff;
1159
            if ($code !== '') {
1160
                $sniffCode .= '.'.$code;
1161
            }
1162
        }//end if
1163
1164
        // If we know this sniff code is being ignored for this file, return early.
1165
        if (isset($this->_ignoredCodes[$sniffCode]) === true) {
1166
            return false;
1167
        }
1168
1169
        // Make sure this message type has not been set to "error".
1170
        if (isset($this->ruleset[$sniffCode]['type']) === true
1171
            && $this->ruleset[$sniffCode]['type'] === 'error'
1172
        ) {
1173
            // Pass this off to the error handler.
1174
            return $this->_addError($warning, $line, $column, $code, $data, $severity, $fixable);
1175
        } else if ($this->phpcs->cli->warningSeverity === 0) {
1176
            // Don't bother doing any processing as warnings are just going to
1177
            // be hidden in the reports anyway.
1178
            return false;
1179
        }
1180
1181
        // Make sure we are interested in this severity level.
1182
        if (isset($this->ruleset[$sniffCode]['severity']) === true) {
1183
            $severity = $this->ruleset[$sniffCode]['severity'];
1184
        } else if ($severity === 0) {
1185
            $severity = PHPCS_DEFAULT_WARN_SEV;
1186
        }
1187
1188
        if ($this->phpcs->cli->warningSeverity > $severity) {
1189
            return false;
1190
        }
1191
1192
        // Make sure we are not ignoring this file.
1193
        $patterns = $this->phpcs->getIgnorePatterns($sniffCode);
1194
        foreach ($patterns as $pattern => $type) {
1195
            // While there is support for a type of each pattern
1196
            // (absolute or relative) we don't actually support it here.
1197
            $replacements = array(
1198
                             '\\,' => ',',
1199
                             '*'   => '.*',
1200
                            );
1201
1202
            // We assume a / directory separator, as do the exclude rules
1203
            // most developers write, so we need a special case for any system
1204
            // that is different.
1205
            if (DIRECTORY_SEPARATOR === '\\') {
1206
                $replacements['/'] = '\\\\';
1207
            }
1208
1209
            $pattern = '`'.strtr($pattern, $replacements).'`i';
1210
            if (preg_match($pattern, $this->_file) === 1) {
1211
                $this->_ignoredCodes[$sniffCode] = true;
1212
                return false;
1213
            }
1214
        }//end foreach
1215
1216
        $this->_warningCount++;
1217
        if ($fixable === true) {
1218
            $this->_fixableCount++;
1219
        }
1220
1221
        if ($this->_recordErrors === false) {
1222
            if (isset($this->_warnings[$line]) === false) {
1223
                $this->_warnings[$line] = 0;
1224
            }
1225
1226
            $this->_warnings[$line]++;
1227
            return true;
1228
        }
1229
1230
        // Work out the warning message.
1231
        if (isset($this->ruleset[$sniffCode]['message']) === true) {
1232
            $warning = $this->ruleset[$sniffCode]['message'];
1233
        }
1234
1235
        if (empty($data) === true) {
1236
            $message = $warning;
1237
        } else {
1238
            $message = vsprintf($warning, $data);
1239
        }
1240
1241
        if (isset($this->_warnings[$line]) === false) {
1242
            $this->_warnings[$line] = array();
1243
        }
1244
1245
        if (isset($this->_warnings[$line][$column]) === false) {
1246
            $this->_warnings[$line][$column] = array();
1247
        }
1248
1249
        $this->_warnings[$line][$column][] = array(
1250
                                              'message'  => $message,
1251
                                              'source'   => $sniffCode,
1252
                                              'severity' => $severity,
1253
                                              'fixable'  => $fixable,
1254
                                             );
1255
1256
        if (PHP_CODESNIFFER_VERBOSITY > 1
1257
            && $this->fixer->enabled === true
1258
            && $fixable === true
1259
        ) {
1260
            @ob_end_clean();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1261
            echo "\tW: $message ($sniffCode)".PHP_EOL;
1262
            ob_start();
1263
        }
1264
1265
        return true;
1266
1267
    }//end _addWarning()
1268
1269
1270
    /**
1271
     * Adds an warning to the warning stack.
1272
     *
1273
     * @param int    $stackPtr The stack position where the metric was recorded.
1274
     * @param string $metric   The name of the metric being recorded.
1275
     * @param string $value    The value of the metric being recorded.
1276
     *
1277
     * @return boolean
1278
     */
1279
    public function recordMetric($stackPtr, $metric, $value)
1280
    {
1281
        if (isset($this->_metrics[$metric]) === false) {
1282
            $this->_metrics[$metric] = array(
1283
                                        'values' => array(
1284
                                                     $value => array($stackPtr),
1285
                                                    ),
1286
                                       );
1287
        } else {
1288
            if (isset($this->_metrics[$metric]['values'][$value]) === false) {
1289
                $this->_metrics[$metric]['values'][$value] = array($stackPtr);
1290
            } else {
1291
                $this->_metrics[$metric]['values'][$value][] = $stackPtr;
1292
            }
1293
        }
1294
1295
        return true;
1296
1297
    }//end recordMetric()
1298
1299
1300
    /**
1301
     * Returns the number of errors raised.
1302
     *
1303
     * @return int
1304
     */
1305
    public function getErrorCount()
1306
    {
1307
        return $this->_errorCount;
1308
1309
    }//end getErrorCount()
1310
1311
1312
    /**
1313
     * Returns the number of warnings raised.
1314
     *
1315
     * @return int
1316
     */
1317
    public function getWarningCount()
1318
    {
1319
        return $this->_warningCount;
1320
1321
    }//end getWarningCount()
1322
1323
1324
    /**
1325
     * Returns the number of successes recorded.
1326
     *
1327
     * @return int
1328
     */
1329
    public function getSuccessCount()
1330
    {
1331
        return $this->_successCount;
0 ignored issues
show
Bug introduced by
The property _successCount does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
1332
1333
    }//end getSuccessCount()
1334
1335
1336
    /**
1337
     * Returns the number of fixable errors/warnings raised.
1338
     *
1339
     * @return int
1340
     */
1341
    public function getFixableCount()
1342
    {
1343
        return $this->_fixableCount;
1344
1345
    }//end getFixableCount()
1346
1347
1348
    /**
1349
     * Returns the list of ignored lines.
1350
     *
1351
     * @return array
1352
     */
1353
    public function getIgnoredLines()
1354
    {
1355
        return self::$_ignoredLines;
1356
1357
    }//end getIgnoredLines()
1358
1359
1360
    /**
1361
     * Returns the errors raised from processing this file.
1362
     *
1363
     * @return array
1364
     */
1365
    public function getErrors()
1366
    {
1367
        return $this->_errors;
1368
1369
    }//end getErrors()
1370
1371
1372
    /**
1373
     * Returns the warnings raised from processing this file.
1374
     *
1375
     * @return array
1376
     */
1377
    public function getWarnings()
1378
    {
1379
        return $this->_warnings;
1380
1381
    }//end getWarnings()
1382
1383
1384
    /**
1385
     * Returns the metrics found while processing this file.
1386
     *
1387
     * @return array
1388
     */
1389
    public function getMetrics()
1390
    {
1391
        return $this->_metrics;
1392
1393
    }//end getMetrics()
1394
1395
1396
    /**
1397
     * Returns the absolute filename of this file.
1398
     *
1399
     * @return string
1400
     */
1401
    public function getFilename()
1402
    {
1403
        return $this->_file;
1404
1405
    }//end getFilename()
1406
1407
1408
    /**
1409
     * Creates an array of tokens when given some PHP code.
1410
     *
1411
     * Starts by using token_get_all() but does a lot of extra processing
1412
     * to insert information about the context of the token.
1413
     *
1414
     * @param string $string    The string to tokenize.
1415
     * @param object $tokenizer A tokenizer class to use to tokenize the string.
1416
     * @param string $eolChar   The EOL character to use for splitting strings.
1417
     * @param int    $tabWidth  The number of spaces each tab respresents.
1418
     * @param string $encoding  The charset of the sniffed file.
1419
     *
1420
     * @throws PHP_CodeSniffer_Exception If the file cannot be processed.
1421
     * @return array
1422
     */
1423
    public static function tokenizeString($string, $tokenizer, $eolChar='\n', $tabWidth=null, $encoding=null)
1424
    {
1425
        // Minified files often have a very large number of characters per line
1426
        // and cause issues when tokenizing.
1427
        if (property_exists($tokenizer, 'skipMinified') === true
1428
            && $tokenizer->skipMinified === true
1429
        ) {
1430
            $numChars = strlen($string);
1431
            $numLines = (substr_count($string, $eolChar) + 1);
1432
            $average  = ($numChars / $numLines);
1433
            if ($average > 100) {
1434
                throw new PHP_CodeSniffer_Exception('File appears to be minified and cannot be processed');
1435
            }
1436
        }
1437
1438
        $tokens = $tokenizer->tokenizeString($string, $eolChar);
1439
1440
        if ($tabWidth === null) {
1441
            $tabWidth = PHP_CODESNIFFER_TAB_WIDTH;
1442
        }
1443
1444
        if ($encoding === null) {
1445
            $encoding = PHP_CODESNIFFER_ENCODING;
1446
        }
1447
1448
        self::_createPositionMap($tokens, $tokenizer, $eolChar, $encoding, $tabWidth);
1449
        self::_createTokenMap($tokens, $tokenizer, $eolChar);
1450
        self::_createParenthesisNestingMap($tokens, $tokenizer, $eolChar);
1451
        self::_createScopeMap($tokens, $tokenizer, $eolChar);
1452
1453
        self::_createLevelMap($tokens, $tokenizer, $eolChar);
1454
1455
        // Allow the tokenizer to do additional processing if required.
1456
        $tokenizer->processAdditional($tokens, $eolChar);
1457
1458
        return $tokens;
1459
1460
    }//end tokenizeString()
1461
1462
1463
    /**
1464
     * Sets token position information.
1465
     *
1466
     * Can also convert tabs into spaces. Each tab can represent between
1467
     * 1 and $width spaces, so this cannot be a straight string replace.
1468
     *
1469
     * @param array  $tokens    The array of tokens to process.
1470
     * @param object $tokenizer The tokenizer being used to process this file.
1471
     * @param string $eolChar   The EOL character to use for splitting strings.
1472
     * @param string $encoding  The charset of the sniffed file.
1473
     * @param int    $tabWidth  The number of spaces that each tab represents.
1474
     *                          Set to 0 to disable tab replacement.
1475
     *
1476
     * @return void
1477
     */
1478
    private static function _createPositionMap(&$tokens, $tokenizer, $eolChar, $encoding, $tabWidth)
1479
    {
1480
        $currColumn    = 1;
1481
        $lineNumber    = 1;
1482
        $eolLen        = (strlen($eolChar) * -1);
1483
        $tokenizerType = get_class($tokenizer);
1484
        $ignoring      = false;
1485
        $inTests       = defined('PHP_CODESNIFFER_IN_TESTS');
1486
1487
        $checkEncoding = false;
1488
        if ($encoding !== 'iso-8859-1' && function_exists('iconv_strlen') === true) {
1489
            $checkEncoding = true;
1490
        }
1491
1492
        $tokensWithTabs = array(
1493
                           T_WHITESPACE               => true,
1494
                           T_COMMENT                  => true,
1495
                           T_DOC_COMMENT              => true,
1496
                           T_DOC_COMMENT_WHITESPACE   => true,
1497
                           T_DOC_COMMENT_STRING       => true,
1498
                           T_CONSTANT_ENCAPSED_STRING => true,
1499
                           T_DOUBLE_QUOTED_STRING     => true,
1500
                           T_HEREDOC                  => true,
1501
                           T_NOWDOC                   => true,
1502
                           T_INLINE_HTML              => true,
1503
                          );
1504
1505
        $numTokens = count($tokens);
1506
        for ($i = 0; $i < $numTokens; $i++) {
1507
            $tokens[$i]['line']   = $lineNumber;
1508
            $tokens[$i]['column'] = $currColumn;
1509
1510
            if ($tokenizerType === 'PHP_CodeSniffer_Tokenizers_PHP'
1511
                && isset(PHP_CodeSniffer_Tokens::$knownLengths[$tokens[$i]['code']]) === true
1512
            ) {
1513
                // There are no tabs in the tokens we know the length of.
1514
                $length      = PHP_CodeSniffer_Tokens::$knownLengths[$tokens[$i]['code']];
1515
                $currColumn += $length;
1516
            } else if ($tabWidth === 0
1517
                || isset($tokensWithTabs[$tokens[$i]['code']]) === false
1518
                || strpos($tokens[$i]['content'], "\t") === false
1519
            ) {
1520
                // There are no tabs in this content, or we aren't replacing them.
1521
                if ($checkEncoding === true) {
1522
                    // Not using the default encoding, so take a bit more care.
1523
                    $length = @iconv_strlen($tokens[$i]['content'], $encoding);
1524
                    if ($length === false) {
1525
                        // String contained invalid characters, so revert to default.
1526
                        $length = strlen($tokens[$i]['content']);
1527
                    }
1528
                } else {
1529
                    $length = strlen($tokens[$i]['content']);
1530
                }
1531
1532
                $currColumn += $length;
1533
            } else {
1534
                if (str_replace("\t", '', $tokens[$i]['content']) === '') {
1535
                    // String only contains tabs, so we can shortcut the process.
1536
                    $numTabs = strlen($tokens[$i]['content']);
1537
1538
                    $newContent   = '';
0 ignored issues
show
Unused Code introduced by
$newContent is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1539
                    $firstTabSize = ($tabWidth - ($currColumn % $tabWidth) + 1);
1540
                    $length       = ($firstTabSize + ($tabWidth * ($numTabs - 1)));
1541
                    $currColumn  += $length;
1542
                    $newContent   = str_repeat(' ', $length);
1543
                } else {
1544
                    // We need to determine the length of each tab.
1545
                    $tabs = explode("\t", $tokens[$i]['content']);
1546
1547
                    $numTabs    = (count($tabs) - 1);
1548
                    $tabNum     = 0;
1549
                    $newContent = '';
1550
                    $length     = 0;
1551
1552
                    foreach ($tabs as $content) {
1553
                        if ($content !== '') {
1554
                            $newContent .= $content;
1555
                            if ($checkEncoding === true) {
1556
                                // Not using the default encoding, so take a bit more care.
1557
                                $contentLength = @iconv_strlen($content, $encoding);
1558
                                if ($contentLength === false) {
1559
                                    // String contained invalid characters, so revert to default.
1560
                                    $contentLength = strlen($content);
1561
                                }
1562
                            } else {
1563
                                $contentLength = strlen($content);
1564
                            }
1565
1566
                            $currColumn += $contentLength;
1567
                            $length     += $contentLength;
1568
                        }
1569
1570
                        // The last piece of content does not have a tab after it.
1571
                        if ($tabNum === $numTabs) {
1572
                            break;
1573
                        }
1574
1575
                        // Process the tab that comes after the content.
1576
                        $lastCurrColumn = $currColumn;
1577
                        $tabNum++;
1578
1579
                        // Move the pointer to the next tab stop.
1580
                        if (($currColumn % $tabWidth) === 0) {
1581
                            // This is the first tab, and we are already at a
1582
                            // tab stop, so this tab counts as a single space.
1583
                            $currColumn++;
1584
                        } else {
1585
                            $currColumn++;
1586
                            while (($currColumn % $tabWidth) !== 0) {
1587
                                $currColumn++;
1588
                            }
1589
1590
                            $currColumn++;
1591
                        }
1592
1593
                        $length     += ($currColumn - $lastCurrColumn);
1594
                        $newContent .= str_repeat(' ', ($currColumn - $lastCurrColumn));
1595
                    }//end foreach
1596
                }//end if
1597
1598
                $tokens[$i]['orig_content'] = $tokens[$i]['content'];
1599
                $tokens[$i]['content']      = $newContent;
1600
            }//end if
1601
1602
            $tokens[$i]['length'] = $length;
1603
1604
            if (isset(PHP_CodeSniffer_Tokens::$knownLengths[$tokens[$i]['code']]) === false
1605
                && strpos($tokens[$i]['content'], $eolChar) !== false
1606
            ) {
1607
                $lineNumber++;
1608
                $currColumn = 1;
1609
1610
                // Newline chars are not counted in the token length.
1611
                $tokens[$i]['length'] += $eolLen;
1612
            }
1613
1614
            if ($tokens[$i]['code'] === T_COMMENT
1615
                || $tokens[$i]['code'] === T_DOC_COMMENT_TAG
1616
                || ($inTests === true && $tokens[$i]['code'] === T_INLINE_HTML)
1617
            ) {
1618
                if (strpos($tokens[$i]['content'], '@codingStandards') !== false) {
1619
                    if ($ignoring === false
1620
                        && strpos($tokens[$i]['content'], '@codingStandardsIgnoreStart') !== false
1621
                    ) {
1622
                        $ignoring = true;
1623
                    } else if ($ignoring === true
1624
                        && strpos($tokens[$i]['content'], '@codingStandardsIgnoreEnd') !== false
1625
                    ) {
1626
                        $ignoring = false;
1627
                        // Ignore this comment too.
1628
                        self::$_ignoredLines[$tokens[$i]['line']] = true;
1629
                    } else if ($ignoring === false
1630
                        && strpos($tokens[$i]['content'], '@codingStandardsIgnoreLine') !== false
1631
                    ) {
1632
                        self::$_ignoredLines[($tokens[$i]['line'] + 1)] = true;
1633
                        // Ignore this comment too.
1634
                        self::$_ignoredLines[$tokens[$i]['line']] = true;
1635
                    }
1636
                }
1637
            }//end if
1638
1639
            if ($ignoring === true) {
1640
                self::$_ignoredLines[$tokens[$i]['line']] = true;
1641
            }
1642
        }//end for
1643
1644
    }//end _createPositionMap()
1645
1646
1647
    /**
1648
     * Creates a map of brackets positions.
1649
     *
1650
     * @param array  $tokens    The array of tokens to process.
1651
     * @param object $tokenizer The tokenizer being used to process this file.
1652
     * @param string $eolChar   The EOL character to use for splitting strings.
1653
     *
1654
     * @return void
1655
     */
1656
    private static function _createTokenMap(&$tokens, $tokenizer, $eolChar)
0 ignored issues
show
Unused Code introduced by
The parameter $tokenizer is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $eolChar is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1657
    {
1658
        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1659
            echo "\t*** START TOKEN MAP ***".PHP_EOL;
1660
        }
1661
1662
        $squareOpeners = array();
1663
        $curlyOpeners  = array();
1664
        $numTokens     = count($tokens);
1665
1666
        $openers   = array();
1667
        $openOwner = null;
1668
1669
        for ($i = 0; $i < $numTokens; $i++) {
1670
            /*
1671
                Parenthesis mapping.
1672
            */
1673
1674
            if (isset(PHP_CodeSniffer_Tokens::$parenthesisOpeners[$tokens[$i]['code']]) === true) {
1675
                $tokens[$i]['parenthesis_opener'] = null;
1676
                $tokens[$i]['parenthesis_closer'] = null;
1677
                $tokens[$i]['parenthesis_owner']  = $i;
1678
                $openOwner = $i;
1679
            } else if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
1680
                $openers[] = $i;
1681
                $tokens[$i]['parenthesis_opener'] = $i;
1682
                if ($openOwner !== null) {
1683
                    $tokens[$openOwner]['parenthesis_opener'] = $i;
1684
                    $tokens[$i]['parenthesis_owner']          = $openOwner;
1685
                    $openOwner = null;
1686
                }
1687
            } else if ($tokens[$i]['code'] === T_CLOSE_PARENTHESIS) {
1688
                // Did we set an owner for this set of parenthesis?
1689
                $numOpeners = count($openers);
1690
                if ($numOpeners !== 0) {
1691
                    $opener = array_pop($openers);
1692
                    if (isset($tokens[$opener]['parenthesis_owner']) === true) {
1693
                        $owner = $tokens[$opener]['parenthesis_owner'];
1694
1695
                        $tokens[$owner]['parenthesis_closer'] = $i;
1696
                        $tokens[$i]['parenthesis_owner']      = $owner;
1697
                    }
1698
1699
                    $tokens[$i]['parenthesis_opener']      = $opener;
1700
                    $tokens[$i]['parenthesis_closer']      = $i;
1701
                    $tokens[$opener]['parenthesis_closer'] = $i;
1702
                }
1703
            }//end if
1704
1705
            /*
1706
                Bracket mapping.
1707
            */
1708
1709
            switch ($tokens[$i]['code']) {
1710
            case T_OPEN_SQUARE_BRACKET:
1711
                $squareOpeners[] = $i;
1712
1713 View Code Duplication
                if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1714
                    echo str_repeat("\t", count($squareOpeners));
1715
                    echo str_repeat("\t", count($curlyOpeners));
1716
                    echo "=> Found square bracket opener at $i".PHP_EOL;
1717
                }
1718
                break;
1719
            case T_OPEN_CURLY_BRACKET:
1720
                if (isset($tokens[$i]['scope_closer']) === false) {
1721
                    $curlyOpeners[] = $i;
1722
1723 View Code Duplication
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1724
                        echo str_repeat("\t", count($squareOpeners));
1725
                        echo str_repeat("\t", count($curlyOpeners));
1726
                        echo "=> Found curly bracket opener at $i".PHP_EOL;
1727
                    }
1728
                }
1729
                break;
1730
            case T_CLOSE_SQUARE_BRACKET:
1731
                if (empty($squareOpeners) === false) {
1732
                    $opener = array_pop($squareOpeners);
1733
                    $tokens[$i]['bracket_opener']      = $opener;
1734
                    $tokens[$i]['bracket_closer']      = $i;
1735
                    $tokens[$opener]['bracket_opener'] = $opener;
1736
                    $tokens[$opener]['bracket_closer'] = $i;
1737
1738 View Code Duplication
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1739
                        echo str_repeat("\t", count($squareOpeners));
1740
                        echo str_repeat("\t", count($curlyOpeners));
1741
                        echo "\t=> Found square bracket closer at $i for $opener".PHP_EOL;
1742
                    }
1743
                }
1744
                break;
1745
            case T_CLOSE_CURLY_BRACKET:
1746
                if (empty($curlyOpeners) === false
1747
                    && isset($tokens[$i]['scope_opener']) === false
1748
                ) {
1749
                    $opener = array_pop($curlyOpeners);
1750
                    $tokens[$i]['bracket_opener']      = $opener;
1751
                    $tokens[$i]['bracket_closer']      = $i;
1752
                    $tokens[$opener]['bracket_opener'] = $opener;
1753
                    $tokens[$opener]['bracket_closer'] = $i;
1754
1755 View Code Duplication
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1756
                        echo str_repeat("\t", count($squareOpeners));
1757
                        echo str_repeat("\t", count($curlyOpeners));
1758
                        echo "\t=> Found curly bracket closer at $i for $opener".PHP_EOL;
1759
                    }
1760
                }
1761
                break;
1762
            default:
1763
                continue;
1764
            }//end switch
1765
        }//end for
1766
1767
        // Cleanup for any openers that we didn't find closers for.
1768
        // This typically means there was a syntax error breaking things.
1769
        foreach ($openers as $opener) {
1770
            unset($tokens[$opener]['parenthesis_opener']);
1771
            unset($tokens[$opener]['parenthesis_owner']);
1772
        }
1773
1774
        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1775
            echo "\t*** END TOKEN MAP ***".PHP_EOL;
1776
        }
1777
1778
    }//end _createTokenMap()
1779
1780
1781
    /**
1782
     * Creates a map for the parenthesis tokens that surround other tokens.
1783
     *
1784
     * @param array  $tokens    The array of tokens to process.
1785
     * @param object $tokenizer The tokenizer being used to process this file.
1786
     * @param string $eolChar   The EOL character to use for splitting strings.
1787
     *
1788
     * @return void
1789
     */
1790
    private static function _createParenthesisNestingMap(
1791
        &$tokens,
1792
        $tokenizer,
0 ignored issues
show
Unused Code introduced by
The parameter $tokenizer is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1793
        $eolChar
0 ignored issues
show
Unused Code introduced by
The parameter $eolChar is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1794
    ) {
1795
        $numTokens = count($tokens);
1796
        $map       = array();
1797
        for ($i = 0; $i < $numTokens; $i++) {
1798
            if (isset($tokens[$i]['parenthesis_opener']) === true
1799
                && $i === $tokens[$i]['parenthesis_opener']
1800
            ) {
1801
                if (empty($map) === false) {
1802
                    $tokens[$i]['nested_parenthesis'] = $map;
1803
                }
1804
1805
                if (isset($tokens[$i]['parenthesis_closer']) === true) {
1806
                    $map[$tokens[$i]['parenthesis_opener']]
1807
                        = $tokens[$i]['parenthesis_closer'];
1808
                }
1809
            } else if (isset($tokens[$i]['parenthesis_closer']) === true
1810
                && $i === $tokens[$i]['parenthesis_closer']
1811
            ) {
1812
                array_pop($map);
1813
                if (empty($map) === false) {
1814
                    $tokens[$i]['nested_parenthesis'] = $map;
1815
                }
1816
            } else {
1817
                if (empty($map) === false) {
1818
                    $tokens[$i]['nested_parenthesis'] = $map;
1819
                }
1820
            }//end if
1821
        }//end for
1822
1823
    }//end _createParenthesisNestingMap()
1824
1825
1826
    /**
1827
     * Creates a scope map of tokens that open scopes.
1828
     *
1829
     * @param array  $tokens    The array of tokens to process.
1830
     * @param object $tokenizer The tokenizer being used to process this file.
1831
     * @param string $eolChar   The EOL character to use for splitting strings.
1832
     *
1833
     * @return void
1834
     * @see    _recurseScopeMap()
1835
     */
1836
    private static function _createScopeMap(&$tokens, $tokenizer, $eolChar)
1837
    {
1838
        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1839
            echo "\t*** START SCOPE MAP ***".PHP_EOL;
1840
        }
1841
1842
        $numTokens = count($tokens);
1843
        for ($i = 0; $i < $numTokens; $i++) {
1844
            // Check to see if the current token starts a new scope.
1845
            if (isset($tokenizer->scopeOpeners[$tokens[$i]['code']]) === true) {
1846
                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1847
                    $type    = $tokens[$i]['type'];
1848
                    $content = PHP_CodeSniffer::prepareForOutput($tokens[$i]['content']);
1849
                    echo "\tStart scope map at $i:$type => $content".PHP_EOL;
1850
                }
1851
1852
                if (isset($tokens[$i]['scope_condition']) === true) {
1853
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1854
                        echo "\t* already processed, skipping *".PHP_EOL;
1855
                    }
1856
1857
                    continue;
1858
                }
1859
1860
                $i = self::_recurseScopeMap(
1861
                    $tokens,
1862
                    $numTokens,
1863
                    $tokenizer,
1864
                    $eolChar,
1865
                    $i
1866
                );
1867
            }//end if
1868
        }//end for
1869
1870
        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1871
            echo "\t*** END SCOPE MAP ***".PHP_EOL;
1872
        }
1873
1874
    }//end _createScopeMap()
1875
1876
1877
    /**
1878
     * Recurses though the scope openers to build a scope map.
1879
     *
1880
     * @param array  $tokens    The array of tokens to process.
1881
     * @param int    $numTokens The size of the tokens array.
1882
     * @param object $tokenizer The tokenizer being used to process this file.
1883
     * @param string $eolChar   The EOL character to use for splitting strings.
1884
     * @param int    $stackPtr  The position in the stack of the token that
1885
     *                          opened the scope (eg. an IF token or FOR token).
1886
     * @param int    $depth     How many scope levels down we are.
1887
     * @param int    $ignore    How many curly braces we are ignoring.
1888
     *
1889
     * @return int The position in the stack that closed the scope.
1890
     */
1891
    private static function _recurseScopeMap(
1892
        &$tokens,
1893
        $numTokens,
1894
        $tokenizer,
1895
        $eolChar,
1896
        $stackPtr,
1897
        $depth=1,
1898
        &$ignore=0
1899
    ) {
1900
        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1901
            echo str_repeat("\t", $depth);
1902
            echo "=> Begin scope map recursion at token $stackPtr with depth $depth".PHP_EOL;
1903
        }
1904
1905
        $opener    = null;
1906
        $currType  = $tokens[$stackPtr]['code'];
1907
        $startLine = $tokens[$stackPtr]['line'];
1908
1909
        // We will need this to restore the value if we end up
1910
        // returning a token ID that causes our calling function to go back
1911
        // over already ignored braces.
1912
        $originalIgnore = $ignore;
1913
1914
        // If the start token for this scope opener is the same as
1915
        // the scope token, we have already found our opener.
1916
        if (isset($tokenizer->scopeOpeners[$currType]['start'][$currType]) === true) {
1917
            $opener = $stackPtr;
1918
        }
1919
1920
        for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
1921
            $tokenType = $tokens[$i]['code'];
1922
1923
            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1924
                $type    = $tokens[$i]['type'];
1925
                $line    = $tokens[$i]['line'];
1926
                $content = PHP_CodeSniffer::prepareForOutput($tokens[$i]['content']);
1927
1928
                echo str_repeat("\t", $depth);
1929
                echo "Process token $i on line $line [";
1930
                if ($opener !== null) {
1931
                    echo "opener:$opener;";
1932
                }
1933
1934
                if ($ignore > 0) {
1935
                    echo "ignore=$ignore;";
1936
                }
1937
1938
                echo "]: $type => $content".PHP_EOL;
1939
            }//end if
1940
1941
            // Very special case for IF statements in PHP that can be defined without
1942
            // scope tokens. E.g., if (1) 1; 1 ? (1 ? 1 : 1) : 1;
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1943
            // If an IF statement below this one has an opener but no
1944
            // keyword, the opener will be incorrectly assigned to this IF statement.
1945
            if (($currType === T_IF || $currType === T_ELSE)
1946
                && $opener === null
1947
                && $tokens[$i]['code'] === T_SEMICOLON
1948
            ) {
1949
                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1950
                    $type = $tokens[$stackPtr]['type'];
1951
                    echo str_repeat("\t", $depth);
1952
                    echo "=> Found semicolon before scope opener for $stackPtr:$type, bailing".PHP_EOL;
1953
                }
1954
1955
                return $i;
1956
            }
1957
1958
            if ($opener === null
1959
                && $ignore === 0
1960
                && $tokenType === T_CLOSE_CURLY_BRACKET
1961
                && isset($tokenizer->scopeOpeners[$currType]['end'][$tokenType]) === true
1962
            ) {
1963
                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1964
                    $type = $tokens[$stackPtr]['type'];
1965
                    echo str_repeat("\t", $depth);
1966
                    echo "=> Found curly brace closer before scope opener for $stackPtr:$type, bailing".PHP_EOL;
1967
                }
1968
1969
                return ($i - 1);
1970
            }
1971
1972
            if ($opener !== null
1973
                && (isset($tokens[$i]['scope_opener']) === false
1974
                || $tokenizer->scopeOpeners[$tokens[$stackPtr]['code']]['shared'] === true)
1975
                && isset($tokenizer->scopeOpeners[$currType]['end'][$tokenType]) === true
1976
            ) {
1977
                if ($ignore > 0 && $tokenType === T_CLOSE_CURLY_BRACKET) {
1978
                    // The last opening bracket must have been for a string
1979
                    // offset or alike, so let's ignore it.
1980
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1981
                        echo str_repeat("\t", $depth);
1982
                        echo '* finished ignoring curly brace *'.PHP_EOL;
1983
                    }
1984
1985
                    $ignore--;
1986
                    continue;
1987
                } else if ($tokens[$opener]['code'] === T_OPEN_CURLY_BRACKET
1988
                    && $tokenType !== T_CLOSE_CURLY_BRACKET
1989
                ) {
1990
                    // The opener is a curly bracket so the closer must be a curly bracket as well.
1991
                    // We ignore this closer to handle cases such as T_ELSE or T_ELSEIF being considered
1992
                    // a closer of T_IF when it should not.
1993
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1994
                        $type = $tokens[$stackPtr]['type'];
1995
                        echo str_repeat("\t", $depth);
1996
                        echo "=> Ignoring non-curly scope closer for $stackPtr:$type".PHP_EOL;
1997
                    }
1998
                } else {
1999
                    $scopeCloser = $i;
2000
                    $todo        = array(
2001
                                    $stackPtr,
2002
                                    $opener,
2003
                                   );
2004
2005 View Code Duplication
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2006
                        $type       = $tokens[$stackPtr]['type'];
2007
                        $closerType = $tokens[$scopeCloser]['type'];
2008
                        echo str_repeat("\t", $depth);
2009
                        echo "=> Found scope closer ($scopeCloser:$closerType) for $stackPtr:$type".PHP_EOL;
2010
                    }
2011
2012
                    $validCloser = true;
2013
                    if (($tokens[$stackPtr]['code'] === T_IF || $tokens[$stackPtr]['code'] === T_ELSEIF)
2014
                        && ($tokenType === T_ELSE || $tokenType === T_ELSEIF)
2015
                    ) {
2016
                        // To be a closer, this token must have an opener.
2017
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
2018
                            echo str_repeat("\t", $depth);
2019
                            echo "* closer needs to be tested *".PHP_EOL;
2020
                        }
2021
2022
                        $i = self::_recurseScopeMap(
2023
                            $tokens,
2024
                            $numTokens,
2025
                            $tokenizer,
2026
                            $eolChar,
2027
                            $i,
2028
                            ($depth + 1),
2029
                            $ignore
2030
                        );
2031
2032
                        if (isset($tokens[$scopeCloser]['scope_opener']) === false) {
2033
                            $validCloser = false;
2034
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
2035
                                echo str_repeat("\t", $depth);
2036
                                echo "* closer is not valid (no opener found) *".PHP_EOL;
2037
                            }
2038
                        } else if ($tokens[$tokens[$scopeCloser]['scope_opener']]['code'] !== $tokens[$opener]['code']) {
2039
                            $validCloser = false;
2040 View Code Duplication
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2041
                                echo str_repeat("\t", $depth);
2042
                                $type       = $tokens[$tokens[$scopeCloser]['scope_opener']]['type'];
2043
                                $openerType = $tokens[$opener]['type'];
2044
                                echo "* closer is not valid (mismatched opener type; $type != $openerType) *".PHP_EOL;
2045
                            }
2046
                        } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
2047
                            echo str_repeat("\t", $depth);
2048
                            echo "* closer was valid *".PHP_EOL;
2049
                        }
2050
                    } else {
2051
                        // The closer was not processed, so we need to
2052
                        // complete that token as well.
2053
                        $todo[] = $scopeCloser;
2054
                    }//end if
2055
2056
                    if ($validCloser === true) {
2057
                        foreach ($todo as $token) {
2058
                            $tokens[$token]['scope_condition'] = $stackPtr;
2059
                            $tokens[$token]['scope_opener']    = $opener;
2060
                            $tokens[$token]['scope_closer']    = $scopeCloser;
2061
                        }
2062
2063
                        if ($tokenizer->scopeOpeners[$tokens[$stackPtr]['code']]['shared'] === true) {
2064
                            // As we are going back to where we started originally, restore
2065
                            // the ignore value back to its original value.
2066
                            $ignore = $originalIgnore;
2067
                            return $opener;
2068
                        } else if ($scopeCloser === $i
2069
                            && isset($tokenizer->scopeOpeners[$tokenType]) === true
2070
                        ) {
2071
                            // Unset scope_condition here or else the token will appear to have
2072
                            // already been processed, and it will be skipped. Normally we want that,
2073
                            // but in this case, the token is both a closer and an opener, so
2074
                            // it needs to act like an opener. This is also why we return the
2075
                            // token before this one; so the closer has a chance to be processed
2076
                            // a second time, but as an opener.
2077
                            unset($tokens[$scopeCloser]['scope_condition']);
2078
                            return ($i - 1);
2079
                        } else {
2080
                            return $i;
2081
                        }
2082
                    } else {
2083
                        continue;
2084
                    }//end if
2085
                }//end if
2086
            }//end if
2087
2088
            // Is this an opening condition ?
2089
            if (isset($tokenizer->scopeOpeners[$tokenType]) === true) {
2090
                if ($opener === null) {
2091
                    if ($tokenType === T_USE) {
2092
                        // PHP use keywords are special because they can be
2093
                        // used as blocks but also inline in function definitions.
2094
                        // So if we find them nested inside another opener, just skip them.
2095
                        continue;
2096
                    }
2097
2098
                    if ($tokenType === T_FUNCTION
2099
                        && $tokens[$stackPtr]['code'] !== T_FUNCTION
2100
                    ) {
2101
                        // Probably a closure, so process it manually.
2102
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
2103
                            $type = $tokens[$stackPtr]['type'];
2104
                            echo str_repeat("\t", $depth);
2105
                            echo "=> Found function before scope opener for $stackPtr:$type, processing manually".PHP_EOL;
2106
                        }
2107
2108 View Code Duplication
                        if (isset($tokens[$i]['scope_closer']) === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2109
                            // We've already processed this closure.
2110
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
2111
                                echo str_repeat("\t", $depth);
2112
                                echo '* already processed, skipping *'.PHP_EOL;
2113
                            }
2114
2115
                            $i = $tokens[$i]['scope_closer'];
2116
                            continue;
2117
                        }
2118
2119
                        $i = self::_recurseScopeMap(
2120
                            $tokens,
2121
                            $numTokens,
2122
                            $tokenizer,
2123
                            $eolChar,
2124
                            $i,
2125
                            ($depth + 1),
2126
                            $ignore
2127
                        );
2128
2129
                        continue;
2130
                    }//end if
2131
2132
                    // Found another opening condition but still haven't
2133
                    // found our opener, so we are never going to find one.
2134
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
2135
                        $type = $tokens[$stackPtr]['type'];
2136
                        echo str_repeat("\t", $depth);
2137
                        echo "=> Found new opening condition before scope opener for $stackPtr:$type, ";
2138
                    }
2139
2140
                    if (($tokens[$stackPtr]['code'] === T_IF
2141
                        || $tokens[$stackPtr]['code'] === T_ELSEIF
2142
                        || $tokens[$stackPtr]['code'] === T_ELSE)
2143
                        && ($tokens[$i]['code'] === T_ELSE
2144
                        || $tokens[$i]['code'] === T_ELSEIF)
2145
                    ) {
2146
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
2147
                            echo "continuing".PHP_EOL;
2148
                        }
2149
2150
                        return ($i - 1);
2151
                    } else {
2152
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
2153
                            echo "backtracking".PHP_EOL;
2154
                        }
2155
2156
                        return $stackPtr;
2157
                    }
2158
                }//end if
2159
2160
                if (PHP_CODESNIFFER_VERBOSITY > 1) {
2161
                    echo str_repeat("\t", $depth);
2162
                    echo '* token is an opening condition *'.PHP_EOL;
2163
                }
2164
2165
                $isShared = ($tokenizer->scopeOpeners[$tokenType]['shared'] === true);
2166
2167
                if (isset($tokens[$i]['scope_condition']) === true) {
2168
                    // We've been here before.
2169
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
2170
                        echo str_repeat("\t", $depth);
2171
                        echo '* already processed, skipping *'.PHP_EOL;
2172
                    }
2173
2174
                    if ($isShared === false
2175
                        && isset($tokens[$i]['scope_closer']) === true
2176
                    ) {
2177
                        $i = $tokens[$i]['scope_closer'];
2178
                    }
2179
2180
                    continue;
2181
                } else if ($currType === $tokenType
2182
                    && $isShared === false
2183
                    && $opener === null
2184
                ) {
2185
                    // We haven't yet found our opener, but we have found another
2186
                    // scope opener which is the same type as us, and we don't
2187
                    // share openers, so we will never find one.
2188
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
2189
                        echo str_repeat("\t", $depth);
2190
                        echo '* it was another token\'s opener, bailing *'.PHP_EOL;
2191
                    }
2192
2193
                    return $stackPtr;
2194
                } else {
2195
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
2196
                        echo str_repeat("\t", $depth);
2197
                        echo '* searching for opener *'.PHP_EOL;
2198
                    }
2199
2200 View Code Duplication
                    if (isset($tokenizer->scopeOpeners[$tokenType]['end'][T_CLOSE_CURLY_BRACKET]) === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2201
                        $oldIgnore = $ignore;
2202
                        $ignore    = 0;
2203
                    }
2204
2205
                    // PHP has a max nesting level for functions. Stop before we hit that limit
2206
                    // because too many loops means we've run into trouble anyway.
2207 View Code Duplication
                    if ($depth > 50) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2208
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
2209
                            echo str_repeat("\t", $depth);
2210
                            echo '* reached maximum nesting level; aborting *'.PHP_EOL;
2211
                        }
2212
2213
                        throw new PHP_CodeSniffer_Exception('Maximum nesting level reached; file could not be processed');
2214
                    }
2215
2216
                    $oldDepth = $depth;
2217
                    if ($isShared === true
2218
                        && isset($tokenizer->scopeOpeners[$tokenType]['with'][$currType]) === true
2219
                    ) {
2220
                        // Don't allow the depth to increment because this is
2221
                        // possibly not a true nesting if we are sharing our closer.
2222
                        // This can happen, for example, when a SWITCH has a large
2223
                        // number of CASE statements with the same shared BREAK.
2224
                        $depth--;
2225
                    }
2226
2227
                    $i = self::_recurseScopeMap(
2228
                        $tokens,
2229
                        $numTokens,
2230
                        $tokenizer,
2231
                        $eolChar,
2232
                        $i,
2233
                        ($depth + 1),
2234
                        $ignore
2235
                    );
2236
2237
                    $depth = $oldDepth;
2238
2239 View Code Duplication
                    if (isset($tokenizer->scopeOpeners[$tokenType]['end'][T_CLOSE_CURLY_BRACKET]) === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2240
                        $ignore = $oldIgnore;
0 ignored issues
show
Bug introduced by
The variable $oldIgnore does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2241
                    }
2242
                }//end if
2243
            }//end if
2244
2245
            if (isset($tokenizer->scopeOpeners[$currType]['start'][$tokenType]) === true
2246
                && $opener === null
2247
            ) {
2248
                if ($tokenType === T_OPEN_CURLY_BRACKET) {
2249
                    if (isset($tokens[$stackPtr]['parenthesis_closer']) === true
2250
                        && $i < $tokens[$stackPtr]['parenthesis_closer']
2251
                    ) {
2252
                        // We found a curly brace inside the condition of the
2253
                        // current scope opener, so it must be a string offset.
2254
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
2255
                            echo str_repeat("\t", $depth);
2256
                            echo '* ignoring curly brace *'.PHP_EOL;
2257
                        }
2258
2259
                        $ignore++;
2260
                    } else {
2261
                        // Make sure this is actually an opener and not a
2262
                        // string offset (e.g., $var{0}).
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2263
                        for ($x = ($i - 1); $x > 0; $x--) {
2264
                            if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === true) {
2265
                                continue;
2266
                            } else {
2267
                                // If the first non-whitespace/comment token is a
2268
                                // variable or object operator then this is an opener
2269
                                // for a string offset and not a scope.
2270 View Code Duplication
                                if ($tokens[$x]['code'] === T_VARIABLE
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2271
                                    || $tokens[$x]['code'] === T_OBJECT_OPERATOR
2272
                                ) {
2273
                                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
2274
                                        echo str_repeat("\t", $depth);
2275
                                        echo '* ignoring curly brace *'.PHP_EOL;
2276
                                    }
2277
2278
                                    $ignore++;
2279
                                }//end if
2280
2281
                                break;
2282
                            }//end if
2283
                        }//end for
2284
                    }//end if
2285
                }//end if
2286
2287
                if ($ignore === 0 || $tokenType !== T_OPEN_CURLY_BRACKET) {
2288
                    // We found the opening scope token for $currType.
2289
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
2290
                        $type = $tokens[$stackPtr]['type'];
2291
                        echo str_repeat("\t", $depth);
2292
                        echo "=> Found scope opener for $stackPtr:$type".PHP_EOL;
2293
                    }
2294
2295
                    $opener = $i;
2296
                }
2297
            } else if ($tokenType === T_OPEN_PARENTHESIS) {
2298
                if (isset($tokens[$i]['parenthesis_owner']) === true) {
2299
                    $owner = $tokens[$i]['parenthesis_owner'];
2300
                    if (isset(PHP_CodeSniffer_Tokens::$scopeOpeners[$tokens[$owner]['code']]) === true
2301
                        && isset($tokens[$i]['parenthesis_closer']) === true
2302
                    ) {
2303
                        // If we get into here, then we opened a parenthesis for
2304
                        // a scope (eg. an if or else if) so we need to update the
2305
                        // start of the line so that when we check to see
2306
                        // if the closing parenthesis is more than 3 lines away from
2307
                        // the statement, we check from the closing parenthesis.
2308
                        $startLine = $tokens[$tokens[$i]['parenthesis_closer']]['line'];
2309
                    }
2310
                }
2311
            } else if ($tokenType === T_OPEN_CURLY_BRACKET && $opener !== null) {
2312
                // We opened something that we don't have a scope opener for.
2313
                // Examples of this are curly brackets for string offsets etc.
2314
                // We want to ignore this so that we don't have an invalid scope
2315
                // map.
2316
                if (PHP_CODESNIFFER_VERBOSITY > 1) {
2317
                    echo str_repeat("\t", $depth);
2318
                    echo '* ignoring curly brace *'.PHP_EOL;
2319
                }
2320
2321
                $ignore++;
2322
            } else if ($tokenType === T_CLOSE_CURLY_BRACKET && $ignore > 0) {
2323
                // We found the end token for the opener we were ignoring.
2324
                if (PHP_CODESNIFFER_VERBOSITY > 1) {
2325
                    echo str_repeat("\t", $depth);
2326
                    echo '* finished ignoring curly brace *'.PHP_EOL;
2327
                }
2328
2329
                $ignore--;
2330
            } else if ($opener === null
2331
                && isset($tokenizer->scopeOpeners[$currType]) === true
2332
            ) {
2333
                // If we still haven't found the opener after 3 lines,
2334
                // we're not going to find it, unless we know it requires
2335
                // an opener (in which case we better keep looking) or the last
2336
                // token was empty (in which case we'll just confirm there is
2337
                // more code in this file and not just a big comment).
2338
                if ($tokens[$i]['line'] >= ($startLine + 3)
2339
                    && isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[($i - 1)]['code']]) === false
2340
                ) {
2341
                    if ($tokenizer->scopeOpeners[$currType]['strict'] === true) {
2342 View Code Duplication
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2343
                            $type  = $tokens[$stackPtr]['type'];
2344
                            $lines = ($tokens[$i]['line'] - $startLine);
2345
                            echo str_repeat("\t", $depth);
2346
                            echo "=> Still looking for $stackPtr:$type scope opener after $lines lines".PHP_EOL;
2347
                        }
2348
                    } else {
2349
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
2350
                            $type = $tokens[$stackPtr]['type'];
2351
                            echo str_repeat("\t", $depth);
2352
                            echo "=> Couldn't find scope opener for $stackPtr:$type, bailing".PHP_EOL;
2353
                        }
2354
2355
                        return $stackPtr;
2356
                    }
2357
                }
2358
            } else if ($opener !== null
2359
                && $tokenType !== T_BREAK
2360
                && isset($tokenizer->endScopeTokens[$tokenType]) === true
2361
            ) {
2362
                if (isset($tokens[$i]['scope_condition']) === false) {
2363
                    if ($ignore > 0) {
2364
                        // We found the end token for the opener we were ignoring.
2365
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
2366
                            echo str_repeat("\t", $depth);
2367
                            echo '* finished ignoring curly brace *'.PHP_EOL;
2368
                        }
2369
2370
                        $ignore--;
2371
                    } else {
2372
                        // We found a token that closes the scope but it doesn't
2373
                        // have a condition, so it belongs to another token and
2374
                        // our token doesn't have a closer, so pretend this is
2375
                        // the closer.
2376
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
2377
                            $type = $tokens[$stackPtr]['type'];
2378
                            echo str_repeat("\t", $depth);
2379
                            echo "=> Found (unexpected) scope closer for $stackPtr:$type".PHP_EOL;
2380
                        }
2381
2382 View Code Duplication
                        foreach (array($stackPtr, $opener) as $token) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2383
                            $tokens[$token]['scope_condition'] = $stackPtr;
2384
                            $tokens[$token]['scope_opener']    = $opener;
2385
                            $tokens[$token]['scope_closer']    = $i;
2386
                        }
2387
2388
                        return ($i - 1);
2389
                    }//end if
2390
                }//end if
2391
            }//end if
2392
        }//end for
2393
2394
        return $stackPtr;
2395
2396
    }//end _recurseScopeMap()
2397
2398
2399
    /**
2400
     * Constructs the level map.
2401
     *
2402
     * The level map adds a 'level' index to each token which indicates the
2403
     * depth that a token within a set of scope blocks. It also adds a
2404
     * 'condition' index which is an array of the scope conditions that opened
2405
     * each of the scopes - position 0 being the first scope opener.
2406
     *
2407
     * @param array  $tokens    The array of tokens to process.
2408
     * @param object $tokenizer The tokenizer being used to process this file.
2409
     * @param string $eolChar   The EOL character to use for splitting strings.
2410
     *
2411
     * @return void
2412
     */
2413
    private static function _createLevelMap(&$tokens, $tokenizer, $eolChar)
0 ignored issues
show
Unused Code introduced by
The parameter $eolChar is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2414
    {
2415
        if (PHP_CODESNIFFER_VERBOSITY > 1) {
2416
            echo "\t*** START LEVEL MAP ***".PHP_EOL;
2417
        }
2418
2419
        $numTokens  = count($tokens);
2420
        $level      = 0;
2421
        $conditions = array();
2422
        $lastOpener = null;
2423
        $openers    = array();
2424
2425
        for ($i = 0; $i < $numTokens; $i++) {
2426
            if (PHP_CODESNIFFER_VERBOSITY > 1) {
2427
                $type = $tokens[$i]['type'];
2428
                $line = $tokens[$i]['line'];
2429
                $len  = $tokens[$i]['length'];
2430
                $col  = $tokens[$i]['column'];
2431
2432
                $content = PHP_CodeSniffer::prepareForOutput($tokens[$i]['content']);
2433
2434
                echo str_repeat("\t", ($level + 1));
2435
                echo "Process token $i on line $line [col:$col;len:$len;lvl:$level;";
2436
                if (empty($conditions) !== true) {
2437
                    $condString = 'conds;';
2438
                    foreach ($conditions as $condition) {
2439
                        $condString .= token_name($condition).',';
2440
                    }
2441
2442
                    echo rtrim($condString, ',').';';
2443
                }
2444
2445
                echo "]: $type => $content".PHP_EOL;
2446
            }//end if
2447
2448
            $tokens[$i]['level']      = $level;
2449
            $tokens[$i]['conditions'] = $conditions;
2450
2451
            if (isset($tokens[$i]['scope_condition']) === true) {
2452
                // Check to see if this token opened the scope.
2453
                if ($tokens[$i]['scope_opener'] === $i) {
2454
                    $stackPtr = $tokens[$i]['scope_condition'];
2455 View Code Duplication
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2456
                        $type = $tokens[$stackPtr]['type'];
2457
                        echo str_repeat("\t", ($level + 1));
2458
                        echo "=> Found scope opener for $stackPtr:$type".PHP_EOL;
2459
                    }
2460
2461
                    $stackPtr = $tokens[$i]['scope_condition'];
2462
2463
                    // If we find a scope opener that has a shared closer,
2464
                    // then we need to go back over the condition map that we
2465
                    // just created and fix ourselves as we just added some
2466
                    // conditions where there was none. This happens for T_CASE
2467
                    // statements that are using the same break statement.
2468
                    if ($lastOpener !== null && $tokens[$lastOpener]['scope_closer'] === $tokens[$i]['scope_closer']) {
2469
                        // This opener shares its closer with the previous opener,
2470
                        // but we still need to check if the two openers share their
2471
                        // closer with each other directly (like CASE and DEFAULT)
2472
                        // or if they are just sharing because one doesn't have a
2473
                        // closer (like CASE with no BREAK using a SWITCHes closer).
2474
                        $thisType = $tokens[$tokens[$i]['scope_condition']]['code'];
2475
                        $opener   = $tokens[$lastOpener]['scope_condition'];
2476
2477
                        $isShared = isset($tokenizer->scopeOpeners[$thisType]['with'][$tokens[$opener]['code']]);
2478
2479
                        reset($tokenizer->scopeOpeners[$thisType]['end']);
2480
                        reset($tokenizer->scopeOpeners[$tokens[$opener]['code']]['end']);
2481
                        $sameEnd = (current($tokenizer->scopeOpeners[$thisType]['end']) === current($tokenizer->scopeOpeners[$tokens[$opener]['code']]['end']));
2482
2483
                        if ($isShared === true && $sameEnd === true) {
2484
                            $badToken = $opener;
2485 View Code Duplication
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2486
                                $type = $tokens[$badToken]['type'];
2487
                                echo str_repeat("\t", ($level + 1));
2488
                                echo "* shared closer, cleaning up $badToken:$type *".PHP_EOL;
2489
                            }
2490
2491 View Code Duplication
                            for ($x = $tokens[$i]['scope_condition']; $x <= $i; $x++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2492
                                $oldConditions = $tokens[$x]['conditions'];
2493
                                $oldLevel      = $tokens[$x]['level'];
2494
                                $tokens[$x]['level']--;
2495
                                unset($tokens[$x]['conditions'][$badToken]);
2496
                                if (PHP_CODESNIFFER_VERBOSITY > 1) {
2497
                                    $type     = $tokens[$x]['type'];
2498
                                    $oldConds = '';
2499
                                    foreach ($oldConditions as $condition) {
2500
                                        $oldConds .= token_name($condition).',';
2501
                                    }
2502
2503
                                    $oldConds = rtrim($oldConds, ',');
2504
2505
                                    $newConds = '';
2506
                                    foreach ($tokens[$x]['conditions'] as $condition) {
2507
                                        $newConds .= token_name($condition).',';
2508
                                    }
2509
2510
                                    $newConds = rtrim($newConds, ',');
2511
2512
                                    $newLevel = $tokens[$x]['level'];
2513
                                    echo str_repeat("\t", ($level + 1));
2514
                                    echo "* cleaned $x:$type *".PHP_EOL;
2515
                                    echo str_repeat("\t", ($level + 2));
2516
                                    echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
2517
                                    echo str_repeat("\t", ($level + 2));
2518
                                    echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
2519
                                }//end if
2520
                            }//end for
2521
2522
                            unset($conditions[$badToken]);
2523 View Code Duplication
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2524
                                $type = $tokens[$badToken]['type'];
2525
                                echo str_repeat("\t", ($level + 1));
2526
                                echo "* token $badToken:$type removed from conditions array *".PHP_EOL;
2527
                            }
2528
2529
                            unset($openers[$lastOpener]);
2530
2531
                            $level--;
2532 View Code Duplication
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2533
                                echo str_repeat("\t", ($level + 2));
2534
                                echo '* level decreased *'.PHP_EOL;
2535
                            }
2536
                        }//end if
2537
                    }//end if
2538
2539
                    $level++;
2540 View Code Duplication
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2541
                        echo str_repeat("\t", ($level + 1));
2542
                        echo '* level increased *'.PHP_EOL;
2543
                    }
2544
2545
                    $conditions[$stackPtr] = $tokens[$stackPtr]['code'];
2546 View Code Duplication
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2547
                        $type = $tokens[$stackPtr]['type'];
2548
                        echo str_repeat("\t", ($level + 1));
2549
                        echo "* token $stackPtr:$type added to conditions array *".PHP_EOL;
2550
                    }
2551
2552
                    $lastOpener = $tokens[$i]['scope_opener'];
2553
                    if ($lastOpener !== null) {
2554
                        $openers[$lastOpener] = $lastOpener;
2555
                    }
2556
                } else if ($lastOpener !== null && $tokens[$lastOpener]['scope_closer'] === $i) {
2557
                    foreach (array_reverse($openers) as $opener) {
2558
                        if ($tokens[$opener]['scope_closer'] === $i) {
2559
                            $oldOpener = array_pop($openers);
2560
                            if (empty($openers) === false) {
2561
                                $lastOpener           = array_pop($openers);
2562
                                $openers[$lastOpener] = $lastOpener;
2563
                            } else {
2564
                                $lastOpener = null;
2565
                            }
2566
2567 View Code Duplication
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2568
                                $type = $tokens[$oldOpener]['type'];
2569
                                echo str_repeat("\t", ($level + 1));
2570
                                echo "=> Found scope closer for $oldOpener:$type".PHP_EOL;
2571
                            }
2572
2573
                            $oldCondition = array_pop($conditions);
2574 View Code Duplication
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2575
                                echo str_repeat("\t", ($level + 1));
2576
                                echo '* token '.token_name($oldCondition).' removed from conditions array *'.PHP_EOL;
2577
                            }
2578
2579
                            // Make sure this closer actually belongs to us.
2580
                            // Either the condition also has to think this is the
2581
                            // closer, or it has to allow sharing with us.
2582
                            $condition = $tokens[$tokens[$i]['scope_condition']]['code'];
2583
                            if ($condition !== $oldCondition) {
2584
                                if (isset($tokenizer->scopeOpeners[$oldCondition]['with'][$condition]) === false) {
2585
                                    $badToken = $tokens[$oldOpener]['scope_condition'];
2586
2587 View Code Duplication
                                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2588
                                        $type = token_name($oldCondition);
2589
                                        echo str_repeat("\t", ($level + 1));
2590
                                        echo "* scope closer was bad, cleaning up $badToken:$type *".PHP_EOL;
2591
                                    }
2592
2593 View Code Duplication
                                    for ($x = ($oldOpener + 1); $x <= $i; $x++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2594
                                        $oldConditions = $tokens[$x]['conditions'];
2595
                                        $oldLevel      = $tokens[$x]['level'];
2596
                                        $tokens[$x]['level']--;
2597
                                        unset($tokens[$x]['conditions'][$badToken]);
2598
                                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
2599
                                            $type     = $tokens[$x]['type'];
2600
                                            $oldConds = '';
2601
                                            foreach ($oldConditions as $condition) {
2602
                                                $oldConds .= token_name($condition).',';
2603
                                            }
2604
2605
                                            $oldConds = rtrim($oldConds, ',');
2606
2607
                                            $newConds = '';
2608
                                            foreach ($tokens[$x]['conditions'] as $condition) {
2609
                                                $newConds .= token_name($condition).',';
2610
                                            }
2611
2612
                                            $newConds = rtrim($newConds, ',');
2613
2614
                                            $newLevel = $tokens[$x]['level'];
2615
                                            echo str_repeat("\t", ($level + 1));
2616
                                            echo "* cleaned $x:$type *".PHP_EOL;
2617
                                            echo str_repeat("\t", ($level + 2));
2618
                                            echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
2619
                                            echo str_repeat("\t", ($level + 2));
2620
                                            echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
2621
                                        }//end if
2622
                                    }//end for
2623
                                }//end if
2624
                            }//end if
2625
2626
                            $level--;
2627 View Code Duplication
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2628
                                echo str_repeat("\t", ($level + 2));
2629
                                echo '* level decreased *'.PHP_EOL;
2630
                            }
2631
2632
                            $tokens[$i]['level']      = $level;
2633
                            $tokens[$i]['conditions'] = $conditions;
2634
                        }//end if
2635
                    }//end foreach
2636
                }//end if
2637
            }//end if
2638
        }//end for
2639
2640
        if (PHP_CODESNIFFER_VERBOSITY > 1) {
2641
            echo "\t*** END LEVEL MAP ***".PHP_EOL;
2642
        }
2643
2644
    }//end _createLevelMap()
2645
2646
2647
    /**
2648
     * Returns the declaration names for T_CLASS, T_INTERFACE and T_FUNCTION tokens.
2649
     *
2650
     * @param int $stackPtr The position of the declaration token which
2651
     *                      declared the class, interface or function.
2652
     *
2653
     * @return string|null The name of the class, interface or function.
2654
     *                     or NULL if the function is a closure.
2655
     * @throws PHP_CodeSniffer_Exception If the specified token is not of type
2656
     *                                   T_FUNCTION, T_CLASS or T_INTERFACE.
2657
     */
2658
    public function getDeclarationName($stackPtr)
2659
    {
2660
        $tokenCode = $this->_tokens[$stackPtr]['code'];
2661
        if ($tokenCode !== T_FUNCTION
2662
            && $tokenCode !== T_CLASS
2663
            && $tokenCode !== T_INTERFACE
2664
            && $tokenCode !== T_TRAIT
2665
        ) {
2666
            throw new PHP_CodeSniffer_Exception('Token type "'.$this->_tokens[$stackPtr]['type'].'" is not T_FUNCTION, T_CLASS, T_INTERFACE or T_TRAIT');
2667
        }
2668
2669
        if ($tokenCode === T_FUNCTION
2670
            && $this->isAnonymousFunction($stackPtr) === true
2671
        ) {
2672
            return null;
2673
        }
2674
2675
        $content = null;
2676 View Code Duplication
        for ($i = $stackPtr; $i < $this->numTokens; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2677
            if ($this->_tokens[$i]['code'] === T_STRING) {
2678
                $content = $this->_tokens[$i]['content'];
2679
                break;
2680
            }
2681
        }
2682
2683
        return $content;
2684
2685
    }//end getDeclarationName()
2686
2687
2688
    /**
2689
     * Check if the token at the specified position is a anonymous function.
2690
     *
2691
     * @param int $stackPtr The position of the declaration token which
2692
     *                      declared the class, interface or function.
2693
     *
2694
     * @return boolean
2695
     * @throws PHP_CodeSniffer_Exception If the specified token is not of type
2696
     *                                   T_FUNCTION
2697
     */
2698
    public function isAnonymousFunction($stackPtr)
2699
    {
2700
        $tokenCode = $this->_tokens[$stackPtr]['code'];
2701
        if ($tokenCode !== T_FUNCTION) {
2702
            throw new PHP_CodeSniffer_Exception('Token type is not T_FUNCTION');
2703
        }
2704
2705
        if (isset($this->_tokens[$stackPtr]['parenthesis_opener']) === false) {
2706
            // Something is not right with this function.
2707
            return false;
2708
        }
2709
2710
        $name = false;
2711 View Code Duplication
        for ($i = ($stackPtr + 1); $i < $this->numTokens; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2712
            if ($this->_tokens[$i]['code'] === T_STRING) {
2713
                $name = $i;
2714
                break;
2715
            }
2716
        }
2717
2718
        if ($name === false) {
2719
            // No name found.
2720
            return true;
2721
        }
2722
2723
        $open = $this->_tokens[$stackPtr]['parenthesis_opener'];
2724
        if ($name > $open) {
2725
            return true;
2726
        }
2727
2728
        return false;
2729
2730
    }//end isAnonymousFunction()
2731
2732
2733
    /**
2734
     * Returns the method parameters for the specified T_FUNCTION token.
2735
     *
2736
     * Each parameter is in the following format:
2737
     *
2738
     * <code>
2739
     *   0 => array(
2740
     *         'name'              => '$var',  // The variable name.
2741
     *         'pass_by_reference' => false,   // Passed by reference.
2742
     *         'type_hint'         => string,  // Type hint for array or custom type
2743
     *        )
2744
     * </code>
2745
     *
2746
     * Parameters with default values have an additional array index of
2747
     * 'default' with the value of the default as a string.
2748
     *
2749
     * @param int $stackPtr The position in the stack of the T_FUNCTION token
2750
     *                      to acquire the parameters for.
2751
     *
2752
     * @return array
2753
     * @throws PHP_CodeSniffer_Exception If the specified $stackPtr is not of
2754
     *                                   type T_FUNCTION.
2755
     */
2756
    public function getMethodParameters($stackPtr)
2757
    {
2758
        if ($this->_tokens[$stackPtr]['code'] !== T_FUNCTION) {
2759
            throw new PHP_CodeSniffer_Exception('$stackPtr must be of type T_FUNCTION');
2760
        }
2761
2762
        $opener = $this->_tokens[$stackPtr]['parenthesis_opener'];
2763
        $closer = $this->_tokens[$stackPtr]['parenthesis_closer'];
2764
2765
        $vars            = array();
2766
        $currVar         = null;
2767
        $defaultStart    = null;
2768
        $paramCount      = 0;
2769
        $passByReference = false;
2770
        $variableLength  = false;
2771
        $typeHint        = '';
2772
2773
        for ($i = ($opener + 1); $i <= $closer; $i++) {
2774
            // Check to see if this token has a parenthesis or bracket opener. If it does
2775
            // it's likely to be an array which might have arguments in it. This
2776
            // could cause problems in our parsing below, so lets just skip to the
2777
            // end of it.
2778
            if (isset($this->_tokens[$i]['parenthesis_opener']) === true) {
2779
                // Don't do this if it's the close parenthesis for the method.
2780
                if ($i !== $this->_tokens[$i]['parenthesis_closer']) {
2781
                    $i = ($this->_tokens[$i]['parenthesis_closer'] + 1);
2782
                }
2783
            }
2784
2785
            if (isset($this->_tokens[$i]['bracket_opener']) === true) {
2786
                // Don't do this if it's the close parenthesis for the method.
2787
                if ($i !== $this->_tokens[$i]['bracket_closer']) {
2788
                    $i = ($this->_tokens[$i]['bracket_closer'] + 1);
2789
                }
2790
            }
2791
2792
            switch ($this->_tokens[$i]['code']) {
2793
            case T_BITWISE_AND:
2794
                $passByReference = true;
2795
                break;
2796
            case T_VARIABLE:
2797
                $currVar = $i;
2798
                break;
2799
            case T_ELLIPSIS:
2800
                $variableLength = true;
2801
                break;
2802
            case T_ARRAY_HINT:
2803
            case T_CALLABLE:
2804
                $typeHint = $this->_tokens[$i]['content'];
2805
                break;
2806
            case T_STRING:
2807
                // This is a string, so it may be a type hint, but it could
2808
                // also be a constant used as a default value.
2809
                $prevComma = false;
2810 View Code Duplication
                for ($t = $i; $t >= $opener; $t--) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2811
                    if ($this->_tokens[$t]['code'] === T_COMMA) {
2812
                        $prevComma = $t;
2813
                        break;
2814
                    }
2815
                }
2816
2817
                if ($prevComma !== false) {
2818
                    $nextEquals = false;
2819 View Code Duplication
                    for ($t = $prevComma; $t < $i; $t++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2820
                        if ($this->_tokens[$t]['code'] === T_EQUAL) {
2821
                            $nextEquals = $t;
2822
                            break;
2823
                        }
2824
                    }
2825
2826
                    if ($nextEquals !== false) {
2827
                        break;
2828
                    }
2829
                }
2830
2831
                if ($defaultStart === null) {
2832
                    $typeHint .= $this->_tokens[$i]['content'];
2833
                }
2834
                break;
2835
            case T_NS_SEPARATOR:
2836
                // Part of a type hint or default value.
2837
                if ($defaultStart === null) {
2838
                    $typeHint .= $this->_tokens[$i]['content'];
2839
                }
2840
                break;
2841
            case T_CLOSE_PARENTHESIS:
2842
            case T_COMMA:
2843
                // If it's null, then there must be no parameters for this
2844
                // method.
2845
                if ($currVar === null) {
2846
                    continue;
2847
                }
2848
2849
                $vars[$paramCount]         = array();
2850
                $vars[$paramCount]['name'] = $this->_tokens[$currVar]['content'];
2851
2852
                if ($defaultStart !== null) {
2853
                    $vars[$paramCount]['default']
2854
                        = $this->getTokensAsString(
2855
                            $defaultStart,
2856
                            ($i - $defaultStart)
2857
                        );
2858
                }
2859
2860
                $vars[$paramCount]['pass_by_reference'] = $passByReference;
2861
                $vars[$paramCount]['variable_length']   = $variableLength;
2862
                $vars[$paramCount]['type_hint']         = $typeHint;
2863
2864
                // Reset the vars, as we are about to process the next parameter.
2865
                $defaultStart    = null;
2866
                $passByReference = false;
2867
                $variableLength  = false;
2868
                $typeHint        = '';
2869
2870
                $paramCount++;
2871
                break;
2872
            case T_EQUAL:
2873
                $defaultStart = ($i + 1);
2874
                break;
2875
            }//end switch
2876
        }//end for
2877
2878
        return $vars;
2879
2880
    }//end getMethodParameters()
2881
2882
2883
    /**
2884
     * Returns the visibility and implementation properties of a method.
2885
     *
2886
     * The format of the array is:
2887
     * <code>
2888
     *   array(
2889
     *    'scope'           => 'public', // public private or protected
2890
     *    'scope_specified' => true,     // true is scope keyword was found.
2891
     *    'is_abstract'     => false,    // true if the abstract keyword was found.
2892
     *    'is_final'        => false,    // true if the final keyword was found.
2893
     *    'is_static'       => false,    // true if the static keyword was found.
2894
     *    'is_closure'      => false,    // true if no name is found.
2895
     *   );
2896
     * </code>
2897
     *
2898
     * @param int $stackPtr The position in the stack of the T_FUNCTION token to
2899
     *                      acquire the properties for.
2900
     *
2901
     * @return array
2902
     * @throws PHP_CodeSniffer_Exception If the specified position is not a
2903
     *                                   T_FUNCTION token.
2904
     */
2905
    public function getMethodProperties($stackPtr)
2906
    {
2907
        if ($this->_tokens[$stackPtr]['code'] !== T_FUNCTION) {
2908
            throw new PHP_CodeSniffer_Exception('$stackPtr must be of type T_FUNCTION');
2909
        }
2910
2911
        $valid = array(
2912
                  T_PUBLIC      => T_PUBLIC,
2913
                  T_PRIVATE     => T_PRIVATE,
2914
                  T_PROTECTED   => T_PROTECTED,
2915
                  T_STATIC      => T_STATIC,
2916
                  T_FINAL       => T_FINAL,
2917
                  T_ABSTRACT    => T_ABSTRACT,
2918
                  T_WHITESPACE  => T_WHITESPACE,
2919
                  T_COMMENT     => T_COMMENT,
2920
                  T_DOC_COMMENT => T_DOC_COMMENT,
2921
                 );
2922
2923
        $scope          = 'public';
2924
        $scopeSpecified = false;
2925
        $isAbstract     = false;
2926
        $isFinal        = false;
2927
        $isStatic       = false;
2928
        $isClosure      = $this->isAnonymousFunction($stackPtr);
2929
2930
        for ($i = ($stackPtr - 1); $i > 0; $i--) {
2931
            if (isset($valid[$this->_tokens[$i]['code']]) === false) {
2932
                break;
2933
            }
2934
2935
            switch ($this->_tokens[$i]['code']) {
2936
            case T_PUBLIC:
2937
                $scope          = 'public';
2938
                $scopeSpecified = true;
2939
                break;
2940
            case T_PRIVATE:
2941
                $scope          = 'private';
2942
                $scopeSpecified = true;
2943
                break;
2944
            case T_PROTECTED:
2945
                $scope          = 'protected';
2946
                $scopeSpecified = true;
2947
                break;
2948
            case T_ABSTRACT:
2949
                $isAbstract = true;
2950
                break;
2951
            case T_FINAL:
2952
                $isFinal = true;
2953
                break;
2954
            case T_STATIC:
2955
                $isStatic = true;
2956
                break;
2957
            }//end switch
2958
        }//end for
2959
2960
        return array(
2961
                'scope'           => $scope,
2962
                'scope_specified' => $scopeSpecified,
2963
                'is_abstract'     => $isAbstract,
2964
                'is_final'        => $isFinal,
2965
                'is_static'       => $isStatic,
2966
                'is_closure'      => $isClosure,
2967
               );
2968
2969
    }//end getMethodProperties()
2970
2971
2972
    /**
2973
     * Returns the visibility and implementation properties of the class member
2974
     * variable found at the specified position in the stack.
2975
     *
2976
     * The format of the array is:
2977
     *
2978
     * <code>
2979
     *   array(
2980
     *    'scope'       => 'public', // public private or protected
2981
     *    'is_static'   => false,    // true if the static keyword was found.
2982
     *   );
2983
     * </code>
2984
     *
2985
     * @param int $stackPtr The position in the stack of the T_VARIABLE token to
2986
     *                      acquire the properties for.
2987
     *
2988
     * @return array
2989
     * @throws PHP_CodeSniffer_Exception If the specified position is not a
2990
     *                                   T_VARIABLE token, or if the position is not
2991
     *                                   a class member variable.
2992
     */
2993
    public function getMemberProperties($stackPtr)
2994
    {
2995
        if ($this->_tokens[$stackPtr]['code'] !== T_VARIABLE) {
2996
            throw new PHP_CodeSniffer_Exception('$stackPtr must be of type T_VARIABLE');
2997
        }
2998
2999
        $conditions = array_keys($this->_tokens[$stackPtr]['conditions']);
3000
        $ptr        = array_pop($conditions);
3001
        if (isset($this->_tokens[$ptr]) === false
3002
            || ($this->_tokens[$ptr]['code'] !== T_CLASS
3003
            && $this->_tokens[$ptr]['code'] !== T_TRAIT)
3004
        ) {
3005
            if (isset($this->_tokens[$ptr]) === true
3006
                && $this->_tokens[$ptr]['code'] === T_INTERFACE
3007
            ) {
3008
                // T_VARIABLEs in interfaces can actually be method arguments
3009
                // but they wont be seen as being inside the method because there
3010
                // are no scope openers and closers for abstract methods. If it is in
3011
                // parentheses, we can be pretty sure it is a method argument.
3012
                if (isset($this->_tokens[$stackPtr]['nested_parenthesis']) === false
3013
                    || empty($this->_tokens[$stackPtr]['nested_parenthesis']) === true
3014
                ) {
3015
                    $error = 'Possible parse error: interfaces may not include member vars';
3016
                    $this->addWarning($error, $stackPtr, 'Internal.ParseError.InterfaceHasMemberVar');
3017
                    return array();
3018
                }
3019
            } else {
3020
                throw new PHP_CodeSniffer_Exception('$stackPtr is not a class member var');
3021
            }
3022
        }
3023
3024
        $valid = array(
3025
                  T_PUBLIC      => T_PUBLIC,
3026
                  T_PRIVATE     => T_PRIVATE,
3027
                  T_PROTECTED   => T_PROTECTED,
3028
                  T_STATIC      => T_STATIC,
3029
                  T_WHITESPACE  => T_WHITESPACE,
3030
                  T_COMMENT     => T_COMMENT,
3031
                  T_DOC_COMMENT => T_DOC_COMMENT,
3032
                  T_VARIABLE    => T_VARIABLE,
3033
                  T_COMMA       => T_COMMA,
3034
                 );
3035
3036
        $scope          = 'public';
3037
        $scopeSpecified = false;
3038
        $isStatic       = false;
3039
3040
        for ($i = ($stackPtr - 1); $i > 0; $i--) {
3041
            if (isset($valid[$this->_tokens[$i]['code']]) === false) {
3042
                break;
3043
            }
3044
3045
            switch ($this->_tokens[$i]['code']) {
3046
            case T_PUBLIC:
3047
                $scope          = 'public';
3048
                $scopeSpecified = true;
3049
                break;
3050
            case T_PRIVATE:
3051
                $scope          = 'private';
3052
                $scopeSpecified = true;
3053
                break;
3054
            case T_PROTECTED:
3055
                $scope          = 'protected';
3056
                $scopeSpecified = true;
3057
                break;
3058
            case T_STATIC:
3059
                $isStatic = true;
3060
                break;
3061
            }
3062
        }//end for
3063
3064
        return array(
3065
                'scope'           => $scope,
3066
                'scope_specified' => $scopeSpecified,
3067
                'is_static'       => $isStatic,
3068
               );
3069
3070
    }//end getMemberProperties()
3071
3072
3073
    /**
3074
     * Returns the visibility and implementation properties of a class.
3075
     *
3076
     * The format of the array is:
3077
     * <code>
3078
     *   array(
3079
     *    'is_abstract' => false, // true if the abstract keyword was found.
3080
     *    'is_final'    => false, // true if the final keyword was found.
3081
     *   );
3082
     * </code>
3083
     *
3084
     * @param int $stackPtr The position in the stack of the T_CLASS token to
3085
     *                      acquire the properties for.
3086
     *
3087
     * @return array
3088
     * @throws PHP_CodeSniffer_Exception If the specified position is not a
3089
     *                                   T_CLASS token.
3090
     */
3091
    public function getClassProperties($stackPtr)
3092
    {
3093
        if ($this->_tokens[$stackPtr]['code'] !== T_CLASS) {
3094
            throw new PHP_CodeSniffer_Exception('$stackPtr must be of type T_CLASS');
3095
        }
3096
3097
        $valid = array(
3098
                  T_FINAL       => T_FINAL,
3099
                  T_ABSTRACT    => T_ABSTRACT,
3100
                  T_WHITESPACE  => T_WHITESPACE,
3101
                  T_COMMENT     => T_COMMENT,
3102
                  T_DOC_COMMENT => T_DOC_COMMENT,
3103
                 );
3104
3105
        $isAbstract = false;
3106
        $isFinal    = false;
3107
3108
        for ($i = ($stackPtr - 1); $i > 0; $i--) {
3109
            if (isset($valid[$this->_tokens[$i]['code']]) === false) {
3110
                break;
3111
            }
3112
3113
            switch ($this->_tokens[$i]['code']) {
3114
            case T_ABSTRACT:
3115
                $isAbstract = true;
3116
                break;
3117
3118
            case T_FINAL:
3119
                $isFinal = true;
3120
                break;
3121
            }
3122
        }//end for
3123
3124
        return array(
3125
                'is_abstract' => $isAbstract,
3126
                'is_final'    => $isFinal,
3127
               );
3128
3129
    }//end getClassProperties()
3130
3131
3132
    /**
3133
     * Determine if the passed token is a reference operator.
3134
     *
3135
     * Returns true if the specified token position represents a reference.
3136
     * Returns false if the token represents a bitwise operator.
3137
     *
3138
     * @param int $stackPtr The position of the T_BITWISE_AND token.
3139
     *
3140
     * @return boolean
3141
     */
3142
    public function isReference($stackPtr)
3143
    {
3144
        if ($this->_tokens[$stackPtr]['code'] !== T_BITWISE_AND) {
3145
            return false;
3146
        }
3147
3148
        $tokenBefore = $this->findPrevious(
3149
            PHP_CodeSniffer_Tokens::$emptyTokens,
3150
            ($stackPtr - 1),
3151
            null,
3152
            true
3153
        );
3154
3155
        if ($this->_tokens[$tokenBefore]['code'] === T_FUNCTION) {
3156
            // Function returns a reference.
3157
            return true;
3158
        }
3159
3160
        if ($this->_tokens[$tokenBefore]['code'] === T_DOUBLE_ARROW) {
3161
            // Inside a foreach loop, this is a reference.
3162
            return true;
3163
        }
3164
3165
        if ($this->_tokens[$tokenBefore]['code'] === T_AS) {
3166
            // Inside a foreach loop, this is a reference.
3167
            return true;
3168
        }
3169
3170
        if ($this->_tokens[$tokenBefore]['code'] === T_OPEN_SHORT_ARRAY) {
3171
            // Inside an array declaration, this is a reference.
3172
            return true;
3173
        }
3174
3175
        if (isset(PHP_CodeSniffer_Tokens::$assignmentTokens[$this->_tokens[$tokenBefore]['code']]) === true) {
3176
            // This is directly after an assignment. It's a reference. Even if
3177
            // it is part of an operation, the other tests will handle it.
3178
            return true;
3179
        }
3180
3181
        if (isset($this->_tokens[$stackPtr]['nested_parenthesis']) === true) {
3182
            $brackets    = $this->_tokens[$stackPtr]['nested_parenthesis'];
3183
            $lastBracket = array_pop($brackets);
3184
            if (isset($this->_tokens[$lastBracket]['parenthesis_owner']) === true) {
3185
                $owner = $this->_tokens[$this->_tokens[$lastBracket]['parenthesis_owner']];
3186
                if ($owner['code'] === T_FUNCTION
3187
                    || $owner['code'] === T_CLOSURE
3188
                    || $owner['code'] === T_ARRAY
3189
                ) {
3190
                    // Inside a function or array declaration, this is a reference.
3191
                    return true;
3192
                }
3193
            } else {
3194
                $prev = false;
3195
                for ($t = ($this->_tokens[$lastBracket]['parenthesis_opener'] - 1); $t >= 0; $t--) {
3196
                    if ($this->_tokens[$t]['code'] !== T_WHITESPACE) {
3197
                        $prev = $t;
3198
                        break;
3199
                    }
3200
                }
3201
3202
                if ($prev !== false && $this->_tokens[$prev]['code'] === T_USE) {
3203
                    return true;
3204
                }
3205
            }//end if
3206
        }//end if
3207
3208
        $tokenAfter = $this->findNext(
3209
            PHP_CodeSniffer_Tokens::$emptyTokens,
3210
            ($stackPtr + 1),
3211
            null,
3212
            true
3213
        );
3214
3215
        if ($this->_tokens[$tokenAfter]['code'] === T_VARIABLE
3216
            && ($this->_tokens[$tokenBefore]['code'] === T_OPEN_PARENTHESIS
3217
            || $this->_tokens[$tokenBefore]['code'] === T_COMMA)
3218
        ) {
3219
            return true;
3220
        }
3221
3222
        return false;
3223
3224
    }//end isReference()
3225
3226
3227
    /**
3228
     * Returns the content of the tokens from the specified start position in
3229
     * the token stack for the specified length.
3230
     *
3231
     * @param int $start  The position to start from in the token stack.
3232
     * @param int $length The length of tokens to traverse from the start pos.
3233
     *
3234
     * @return string The token contents.
3235
     */
3236
    public function getTokensAsString($start, $length)
3237
    {
3238
        $str = '';
3239
        $end = ($start + $length);
3240
        if ($end > $this->numTokens) {
3241
            $end = $this->numTokens;
3242
        }
3243
3244
        for ($i = $start; $i < $end; $i++) {
3245
            $str .= $this->_tokens[$i]['content'];
3246
        }
3247
3248
        return $str;
3249
3250
    }//end getTokensAsString()
3251
3252
3253
    /**
3254
     * Returns the position of the previous specified token(s).
3255
     *
3256
     * If a value is specified, the previous token of the specified type(s)
3257
     * containing the specified value will be returned.
3258
     *
3259
     * Returns false if no token can be found.
3260
     *
3261
     * @param int|array $types   The type(s) of tokens to search for.
3262
     * @param int       $start   The position to start searching from in the
3263
     *                           token stack.
3264
     * @param int       $end     The end position to fail if no token is found.
3265
     *                           if not specified or null, end will default to
3266
     *                           the start of the token stack.
3267
     * @param bool      $exclude If true, find the previous token that are NOT of
3268
     *                           the types specified in $types.
3269
     * @param string    $value   The value that the token(s) must be equal to.
3270
     *                           If value is omitted, tokens with any value will
3271
     *                           be returned.
3272
     * @param bool      $local   If true, tokens outside the current statement
3273
     *                           will not be checked. IE. checking will stop
3274
     *                           at the previous semi-colon found.
3275
     *
3276
     * @return int|bool
3277
     * @see    findNext()
3278
     */
3279
    public function findPrevious(
3280
        $types,
3281
        $start,
3282
        $end=null,
3283
        $exclude=false,
3284
        $value=null,
3285
        $local=false
3286
    ) {
3287
        $types = (array) $types;
3288
3289
        if ($end === null) {
3290
            $end = 0;
3291
        }
3292
3293
        for ($i = $start; $i >= $end; $i--) {
3294
            $found = (bool) $exclude;
3295 View Code Duplication
            foreach ($types as $type) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3296
                if ($this->_tokens[$i]['code'] === $type) {
3297
                    $found = !$exclude;
3298
                    break;
3299
                }
3300
            }
3301
3302 View Code Duplication
            if ($found === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3303
                if ($value === null) {
3304
                    return $i;
3305
                } else if ($this->_tokens[$i]['content'] === $value) {
3306
                    return $i;
3307
                }
3308
            }
3309
3310 View Code Duplication
            if ($local === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3311
                if (isset($this->_tokens[$i]['scope_opener']) === true
3312
                    && $i === $this->_tokens[$i]['scope_closer']
3313
                ) {
3314
                    $i = $this->_tokens[$i]['scope_opener'];
3315
                } else if (isset($this->_tokens[$i]['bracket_opener']) === true
3316
                    && $i === $this->_tokens[$i]['bracket_closer']
3317
                ) {
3318
                    $i = $this->_tokens[$i]['bracket_opener'];
3319
                } else if (isset($this->_tokens[$i]['parenthesis_opener']) === true
3320
                    && $i === $this->_tokens[$i]['parenthesis_closer']
3321
                ) {
3322
                    $i = $this->_tokens[$i]['parenthesis_opener'];
3323
                } else if ($this->_tokens[$i]['code'] === T_SEMICOLON) {
3324
                    break;
3325
                }
3326
            }
3327
        }//end for
3328
3329
        return false;
3330
3331
    }//end findPrevious()
3332
3333
3334
    /**
3335
     * Returns the position of the next specified token(s).
3336
     *
3337
     * If a value is specified, the next token of the specified type(s)
3338
     * containing the specified value will be returned.
3339
     *
3340
     * Returns false if no token can be found.
3341
     *
3342
     * @param int|array $types   The type(s) of tokens to search for.
3343
     * @param int       $start   The position to start searching from in the
3344
     *                           token stack.
3345
     * @param int       $end     The end position to fail if no token is found.
3346
     *                           if not specified or null, end will default to
3347
     *                           the end of the token stack.
3348
     * @param bool      $exclude If true, find the next token that is NOT of
3349
     *                           a type specified in $types.
3350
     * @param string    $value   The value that the token(s) must be equal to.
3351
     *                           If value is omitted, tokens with any value will
3352
     *                           be returned.
3353
     * @param bool      $local   If true, tokens outside the current statement
3354
     *                           will not be checked. i.e., checking will stop
3355
     *                           at the next semi-colon found.
3356
     *
3357
     * @return int|bool
3358
     * @see    findPrevious()
3359
     */
3360
    public function findNext(
3361
        $types,
3362
        $start,
3363
        $end=null,
3364
        $exclude=false,
3365
        $value=null,
3366
        $local=false
3367
    ) {
3368
        $types = (array) $types;
3369
3370
        if ($end === null || $end > $this->numTokens) {
3371
            $end = $this->numTokens;
3372
        }
3373
3374
        for ($i = $start; $i < $end; $i++) {
3375
            $found = (bool) $exclude;
3376 View Code Duplication
            foreach ($types as $type) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3377
                if ($this->_tokens[$i]['code'] === $type) {
3378
                    $found = !$exclude;
3379
                    break;
3380
                }
3381
            }
3382
3383 View Code Duplication
            if ($found === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3384
                if ($value === null) {
3385
                    return $i;
3386
                } else if ($this->_tokens[$i]['content'] === $value) {
3387
                    return $i;
3388
                }
3389
            }
3390
3391
            if ($local === true && $this->_tokens[$i]['code'] === T_SEMICOLON) {
3392
                break;
3393
            }
3394
        }//end for
3395
3396
        return false;
3397
3398
    }//end findNext()
3399
3400
3401
    /**
3402
     * Returns the position of the first non-whitespace token in a statement.
3403
     *
3404
     * @param int       $start  The position to start searching from in the token stack.
3405
     * @param int|array $ignore Token types that should not be considered stop points.
3406
     *
3407
     * @return int
3408
     */
3409
    public function findStartOfStatement($start, $ignore=null)
3410
    {
3411
        $endTokens = PHP_CodeSniffer_Tokens::$blockOpeners;
3412
3413
        $endTokens[T_COLON]            = true;
3414
        $endTokens[T_COMMA]            = true;
3415
        $endTokens[T_DOUBLE_ARROW]     = true;
3416
        $endTokens[T_SEMICOLON]        = true;
3417
        $endTokens[T_OPEN_TAG]         = true;
3418
        $endTokens[T_CLOSE_TAG]        = true;
3419
        $endTokens[T_OPEN_SHORT_ARRAY] = true;
3420
3421 View Code Duplication
        if ($ignore !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3422
            $ignore = (array) $ignore;
3423
            foreach ($ignore as $code) {
3424
                if (isset($endTokens[$code]) === true) {
3425
                    unset($endTokens[$code]);
3426
                }
3427
            }
3428
        }
3429
3430
        $lastNotEmpty = $start;
3431
3432
        for ($i = $start; $i >= 0; $i--) {
3433
            if (isset($endTokens[$this->_tokens[$i]['code']]) === true) {
3434
                // Found the end of the previous statement.
3435
                return $lastNotEmpty;
3436
            }
3437
3438
            if (isset($this->_tokens[$i]['scope_opener']) === true
3439
                && $i === $this->_tokens[$i]['scope_closer']
3440
            ) {
3441
                // Found the end of the previous scope block.
3442
                return $lastNotEmpty;
3443
            }
3444
3445
            // Skip nested statements.
3446
            if (isset($this->_tokens[$i]['bracket_opener']) === true
3447
                && $i === $this->_tokens[$i]['bracket_closer']
3448
            ) {
3449
                $i = $this->_tokens[$i]['bracket_opener'];
3450
            } else if (isset($this->_tokens[$i]['parenthesis_opener']) === true
3451
                && $i === $this->_tokens[$i]['parenthesis_closer']
3452
            ) {
3453
                $i = $this->_tokens[$i]['parenthesis_opener'];
3454
            }
3455
3456 View Code Duplication
            if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$this->_tokens[$i]['code']]) === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3457
                $lastNotEmpty = $i;
3458
            }
3459
        }//end for
3460
3461
        return 0;
3462
3463
    }//end findStartOfStatement()
3464
3465
3466
    /**
3467
     * Returns the position of the last non-whitespace token in a statement.
3468
     *
3469
     * @param int       $start  The position to start searching from in the token stack.
3470
     * @param int|array $ignore Token types that should not be considered stop points.
3471
     *
3472
     * @return int
3473
     */
3474
    public function findEndOfStatement($start, $ignore=null)
3475
    {
3476
        $endTokens = array(
3477
                      T_COLON                => true,
3478
                      T_COMMA                => true,
3479
                      T_DOUBLE_ARROW         => true,
3480
                      T_SEMICOLON            => true,
3481
                      T_CLOSE_PARENTHESIS    => true,
3482
                      T_CLOSE_SQUARE_BRACKET => true,
3483
                      T_CLOSE_CURLY_BRACKET  => true,
3484
                      T_CLOSE_SHORT_ARRAY    => true,
3485
                      T_OPEN_TAG             => true,
3486
                      T_CLOSE_TAG            => true,
3487
                     );
3488
3489 View Code Duplication
        if ($ignore !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3490
            $ignore = (array) $ignore;
3491
            foreach ($ignore as $code) {
3492
                if (isset($endTokens[$code]) === true) {
3493
                    unset($endTokens[$code]);
3494
                }
3495
            }
3496
        }
3497
3498
        $lastNotEmpty = $start;
3499
3500
        for ($i = $start; $i < $this->numTokens; $i++) {
3501
            if ($i !== $start && isset($endTokens[$this->_tokens[$i]['code']]) === true) {
3502
                // Found the end of the statement.
3503
                if ($this->_tokens[$i]['code'] === T_CLOSE_PARENTHESIS
3504
                    || $this->_tokens[$i]['code'] === T_CLOSE_SQUARE_BRACKET
3505
                    || $this->_tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET
3506
                    || $this->_tokens[$i]['code'] === T_CLOSE_SHORT_ARRAY
3507
                    || $this->_tokens[$i]['code'] === T_OPEN_TAG
3508
                    || $this->_tokens[$i]['code'] === T_CLOSE_TAG
3509
                ) {
3510
                    return $lastNotEmpty;
3511
                }
3512
3513
                return $i;
3514
            }
3515
3516
            // Skip nested statements.
3517 View Code Duplication
            if (isset($this->_tokens[$i]['scope_closer']) === true
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3518
                && ($i === $this->_tokens[$i]['scope_opener']
3519
                || $i === $this->_tokens[$i]['scope_condition'])
3520
            ) {
3521
                $i = $this->_tokens[$i]['scope_closer'];
3522
            } else if (isset($this->_tokens[$i]['bracket_closer']) === true
3523
                && $i === $this->_tokens[$i]['bracket_opener']
3524
            ) {
3525
                $i = $this->_tokens[$i]['bracket_closer'];
3526
            } else if (isset($this->_tokens[$i]['parenthesis_closer']) === true
3527
                && $i === $this->_tokens[$i]['parenthesis_opener']
3528
            ) {
3529
                $i = $this->_tokens[$i]['parenthesis_closer'];
3530
            }
3531
3532 View Code Duplication
            if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$this->_tokens[$i]['code']]) === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3533
                $lastNotEmpty = $i;
3534
            }
3535
        }//end for
3536
3537
        return ($this->numTokens - 1);
3538
3539
    }//end findEndOfStatement()
3540
3541
3542
    /**
3543
     * Returns the position of the first token on a line, matching given type.
3544
     *
3545
     * Returns false if no token can be found.
3546
     *
3547
     * @param int|array $types   The type(s) of tokens to search for.
3548
     * @param int       $start   The position to start searching from in the
3549
     *                           token stack. The first token matching on
3550
     *                           this line before this token will be returned.
3551
     * @param bool      $exclude If true, find the token that is NOT of
3552
     *                           the types specified in $types.
3553
     * @param string    $value   The value that the token must be equal to.
3554
     *                           If value is omitted, tokens with any value will
3555
     *                           be returned.
3556
     *
3557
     * @return int | bool
3558
     */
3559
    public function findFirstOnLine($types, $start, $exclude=false, $value=null)
3560
    {
3561
        if (is_array($types) === false) {
3562
            $types = array($types);
3563
        }
3564
3565
        $foundToken = false;
3566
3567
        for ($i = $start; $i >= 0; $i--) {
3568
            if ($this->_tokens[$i]['line'] < $this->_tokens[$start]['line']) {
3569
                break;
3570
            }
3571
3572
            $found = $exclude;
3573
            foreach ($types as $type) {
3574
                if ($exclude === false) {
3575
                    if ($this->_tokens[$i]['code'] === $type) {
3576
                        $found = true;
3577
                        break;
3578
                    }
3579
                } else {
3580
                    if ($this->_tokens[$i]['code'] === $type) {
3581
                        $found = false;
3582
                        break;
3583
                    }
3584
                }
3585
            }
3586
3587
            if ($found === true) {
3588
                if ($value === null) {
3589
                    $foundToken = $i;
3590
                } else if ($this->_tokens[$i]['content'] === $value) {
3591
                    $foundToken = $i;
3592
                }
3593
            }
3594
        }//end for
3595
3596
        return $foundToken;
3597
3598
    }//end findFirstOnLine()
3599
3600
3601
    /**
3602
     * Determine if the passed token has a condition of one of the passed types.
3603
     *
3604
     * @param int       $stackPtr The position of the token we are checking.
3605
     * @param int|array $types    The type(s) of tokens to search for.
3606
     *
3607
     * @return boolean
3608
     */
3609
    public function hasCondition($stackPtr, $types)
3610
    {
3611
        // Check for the existence of the token.
3612
        if (isset($this->_tokens[$stackPtr]) === false) {
3613
            return false;
3614
        }
3615
3616
        // Make sure the token has conditions.
3617
        if (isset($this->_tokens[$stackPtr]['conditions']) === false) {
3618
            return false;
3619
        }
3620
3621
        $types      = (array) $types;
3622
        $conditions = $this->_tokens[$stackPtr]['conditions'];
3623
3624
        foreach ($types as $type) {
3625
            if (in_array($type, $conditions) === true) {
3626
                // We found a token with the required type.
3627
                return true;
3628
            }
3629
        }
3630
3631
        return false;
3632
3633
    }//end hasCondition()
3634
3635
3636
    /**
3637
     * Return the position of the condition for the passed token.
3638
     *
3639
     * Returns FALSE if the token does not have the condition.
3640
     *
3641
     * @param int $stackPtr The position of the token we are checking.
3642
     * @param int $type     The type of token to search for.
3643
     *
3644
     * @return int
3645
     */
3646
    public function getCondition($stackPtr, $type)
3647
    {
3648
        // Check for the existence of the token.
3649
        if (isset($this->_tokens[$stackPtr]) === false) {
3650
            return false;
3651
        }
3652
3653
        // Make sure the token has conditions.
3654
        if (isset($this->_tokens[$stackPtr]['conditions']) === false) {
3655
            return false;
3656
        }
3657
3658
        $conditions = $this->_tokens[$stackPtr]['conditions'];
3659
        foreach ($conditions as $token => $condition) {
3660
            if ($condition === $type) {
3661
                return $token;
3662
            }
3663
        }
3664
3665
        return false;
3666
3667
    }//end getCondition()
3668
3669
3670
    /**
3671
     * Returns the name of the class that the specified class extends.
3672
     *
3673
     * Returns FALSE on error or if there is no extended class name.
3674
     *
3675
     * @param int $stackPtr The stack position of the class.
3676
     *
3677
     * @return string
3678
     */
3679
    public function findExtendedClassName($stackPtr)
3680
    {
3681
        // Check for the existence of the token.
3682
        if (isset($this->_tokens[$stackPtr]) === false) {
3683
            return false;
3684
        }
3685
3686
        if ($this->_tokens[$stackPtr]['code'] !== T_CLASS) {
3687
            return false;
3688
        }
3689
3690
        if (isset($this->_tokens[$stackPtr]['scope_closer']) === false) {
3691
            return false;
3692
        }
3693
3694
        $classCloserIndex = $this->_tokens[$stackPtr]['scope_closer'];
3695
        $extendsIndex     = $this->findNext(T_EXTENDS, $stackPtr, $classCloserIndex);
3696
        if (false === $extendsIndex) {
3697
            return false;
3698
        }
3699
3700
        $find = array(
3701
                 T_NS_SEPARATOR,
3702
                 T_STRING,
3703
                 T_WHITESPACE,
3704
                );
3705
3706
        $end  = $this->findNext($find, ($extendsIndex + 1), $classCloserIndex, true);
3707
        $name = $this->getTokensAsString(($extendsIndex + 1), ($end - $extendsIndex - 1));
3708
        $name = trim($name);
3709
3710
        if ($name === '') {
3711
            return false;
3712
        }
3713
3714
        return $name;
3715
3716
    }//end findExtendedClassName()
3717
3718
3719
    /**
3720
     * Returns the name(s) of the interface(s) that the specified class implements.
3721
     *
3722
     * Returns FALSE on error or if there are no implemented interface names.
3723
     *
3724
     * @param int $stackPtr The stack position of the class.
3725
     *
3726
     * @return array|false
3727
     */
3728
    public function findImplementedInterfaceNames($stackPtr)
3729
    {
3730
        // Check for the existence of the token.
3731
        if (isset($this->_tokens[$stackPtr]) === false) {
3732
            return false;
3733
        }
3734
3735
        if ($this->_tokens[$stackPtr]['code'] !== T_CLASS) {
3736
            return false;
3737
        }
3738
3739
        if (isset($this->_tokens[$stackPtr]['scope_closer']) === false) {
3740
            return false;
3741
        }
3742
3743
        $classOpenerIndex = $this->_tokens[$stackPtr]['scope_opener'];
3744
        $implementsIndex  = $this->findNext(T_IMPLEMENTS, $stackPtr, $classOpenerIndex);
3745
        if ($implementsIndex === false) {
3746
            return false;
3747
        }
3748
3749
        $find = array(
3750
                 T_NS_SEPARATOR,
3751
                 T_STRING,
3752
                 T_WHITESPACE,
3753
                 T_COMMA,
3754
                );
3755
3756
        $end  = $this->findNext($find, ($implementsIndex + 1), ($classOpenerIndex + 1), true);
3757
        $name = $this->getTokensAsString(($implementsIndex + 1), ($end - $implementsIndex - 1));
3758
        $name = trim($name);
3759
3760
        if ($name === '') {
3761
            return false;
3762
        } else {
3763
            $names = explode(',', $name);
3764
            $names = array_map('trim', $names);
3765
            return $names;
3766
        }
3767
3768
    }//end findImplementedInterfaceNames()
3769
3770
3771
}//end class
3772