Completed
Push — master ( c50882...ec79ab )
by Juliette
9s
created

Sniff.php (11 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * PHPCompatibility_Sniff.
4
 *
5
 * PHP version 5.6
6
 *
7
 * @category  PHP
8
 * @package   PHPCompatibility
9
 * @author    Wim Godden <[email protected]>
10
 * @copyright 2014 Cu.be Solutions bvba
11
 */
12
13
/**
14
 * PHPCompatibility_Sniff.
15
 *
16
 * @category  PHP
17
 * @package   PHPCompatibility
18
 * @author    Wim Godden <[email protected]>
19
 * @version   1.1.0
20
 * @copyright 2014 Cu.be Solutions bvba
21
 */
22
abstract class PHPCompatibility_Sniff implements PHP_CodeSniffer_Sniff
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...
23
{
24
25
    const REGEX_COMPLEX_VARS = '`(?:(\{)?(?<!\\\\)\$)?(\{)?(?<!\\\\)\$(\{)?(?P<varname>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)(?:->\$?(?P>varname)|\[[^\]]+\]|::\$?(?P>varname)|\([^\)]*\))*(?(3)\}|)(?(2)\}|)(?(1)\}|)`';
26
27
    /**
28
     * List of functions using hash algorithm as parameter (always the first parameter).
29
     *
30
     * Used by the new/removed hash algorithm sniffs.
31
     * Key is the function name, value is the 1-based parameter position in the function call.
32
     *
33
     * @var array
34
     */
35
    protected $hashAlgoFunctions = array(
36
        'hash_file'      => 1,
37
        'hash_hmac_file' => 1,
38
        'hash_hmac'      => 1,
39
        'hash_init'      => 1,
40
        'hash_pbkdf2'    => 1,
41
        'hash'           => 1,
42
    );
43
44
45
    /**
46
     * List of functions which take an ini directive as parameter (always the first parameter).
47
     *
48
     * Used by the new/removed ini directives sniffs.
49
     * Key is the function name, value is the 1-based parameter position in the function call.
50
     *
51
     * @var array
52
     */
53
    protected $iniFunctions = array(
54
        'ini_get' => 1,
55
        'ini_set' => 1,
56
    );
57
58
59
    /**
60
     * Get the testVersion configuration variable.
61
     *
62
     * The testVersion configuration variable may be in any of the following formats:
63
     * 1) Omitted/empty, in which case no version is specified. This effectively
64
     *    disables all the checks for new PHP features provided by this standard.
65
     * 2) A single PHP version number, e.g. "5.4" in which case the standard checks that
66
     *    the code will run on that version of PHP (no deprecated features or newer
67
     *    features being used).
68
     * 3) A range, e.g. "5.0-5.5", in which case the standard checks the code will run
69
     *    on all PHP versions in that range, and that it doesn't use any features that
70
     *    were deprecated by the final version in the list, or which were not available
71
     *    for the first version in the list.
72
     * PHP version numbers should always be in Major.Minor format.  Both "5", "5.3.2"
73
     * would be treated as invalid, and ignored.
74
     *
75
     * @return array $arrTestVersions will hold an array containing min/max version
76
     *               of PHP that we are checking against (see above).  If only a
77
     *               single version number is specified, then this is used as
78
     *               both the min and max.
79
     *
80
     * @throws PHP_CodeSniffer_Exception If testVersion is invalid.
81
     */
82
    private function getTestVersion()
83
    {
84
        static $arrTestVersions = array();
85
86
        $testVersion = trim(PHP_CodeSniffer::getConfigData('testVersion'));
87
88
        if (!isset($arrTestVersions[$testVersion]) && !empty($testVersion)) {
89
90
            $arrTestVersions[$testVersion] = array(null, null);
91
            if (preg_match('/^\d+\.\d+$/', $testVersion)) {
92
                $arrTestVersions[$testVersion] = array($testVersion, $testVersion);
93
            }
94
            elseif (preg_match('/^(\d+\.\d+)\s*-\s*(\d+\.\d+)$/', $testVersion,
95
                               $matches))
96
            {
97
                if (version_compare($matches[1], $matches[2], '>')) {
98
                    trigger_error("Invalid range in testVersion setting: '"
99
                                  . $testVersion . "'", E_USER_WARNING);
100
                }
101
                else {
102
                    $arrTestVersions[$testVersion] = array($matches[1], $matches[2]);
103
                }
104
            }
105
            elseif (!$testVersion == '') {
106
                trigger_error("Invalid testVersion setting: '" . $testVersion
107
                              . "'", E_USER_WARNING);
108
            }
109
        }
110
111
        if (isset($arrTestVersions[$testVersion])) {
112
            return $arrTestVersions[$testVersion];
113
        }
114
        else {
115
            return array(null, null);
116
        }
117
    }
118
119
120
    /**
121
     * Check whether a specific PHP version is equal to or higher than the maximum
122
     * supported PHP version as provided by the user in `testVersion`.
123
     *
124
     * Should be used when sniffing for *old* PHP features (deprecated/removed).
125
     *
126
     * @param string $phpVersion A PHP version number in 'major.minor' format.
127
     *
128
     * @return bool True if testVersion has not been provided or if the PHP version
129
     *              is equal to or higher than the highest supported PHP version
130
     *              in testVersion. False otherwise.
131
     */
132 View Code Duplication
    public function supportsAbove($phpVersion)
0 ignored issues
show
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...
133
    {
134
        $testVersion = $this->getTestVersion();
135
        $testVersion = $testVersion[1];
136
137
        if (is_null($testVersion)
138
            || version_compare($testVersion, $phpVersion) >= 0
139
        ) {
140
            return true;
141
        } else {
142
            return false;
143
        }
144
    }//end supportsAbove()
145
146
147
    /**
148
     * Check whether a specific PHP version is equal to or lower than the minimum
149
     * supported PHP version as provided by the user in `testVersion`.
150
     *
151
     * Should be used when sniffing for *new* PHP features.
152
     *
153
     * @param string $phpVersion A PHP version number in 'major.minor' format.
154
     *
155
     * @return bool True if the PHP version is equal to or lower than the lowest
156
     *              supported PHP version in testVersion.
157
     *              False otherwise or if no testVersion is provided.
158
     */
159 View Code Duplication
    public function supportsBelow($phpVersion)
0 ignored issues
show
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...
160
    {
161
        $testVersion = $this->getTestVersion();
162
        $testVersion = $testVersion[0];
163
164
        if (!is_null($testVersion)
165
            && version_compare($testVersion, $phpVersion) <= 0
166
        ) {
167
            return true;
168
        } else {
169
            return false;
170
        }
171
    }//end supportsBelow()
172
173
174
    /**
175
     * Add a PHPCS message to the output stack as either a warning or an error.
176
     *
177
     * @param PHP_CodeSniffer_File $phpcsFile The file the message applies to.
178
     * @param string               $message   The message.
179
     * @param int                  $stackPtr  The position of the token
180
     *                                        the message relates to.
181
     * @param bool                 $isError   Whether to report the message as an
182
     *                                        'error' or 'warning'.
183
     *                                        Defaults to true (error).
184
     * @param string               $code      The error code for the message.
185
     *                                        Defaults to 'Found'.
186
     * @param array                $data      Optional input for the data replacements.
187
     *
188
     * @return void
189
     */
190
    public function addMessage($phpcsFile, $message, $stackPtr, $isError, $code = 'Found', $data = array())
191
    {
192
        if ($isError === true) {
193
            $phpcsFile->addError($message, $stackPtr, $code, $data);
194
        } else {
195
            $phpcsFile->addWarning($message, $stackPtr, $code, $data);
196
        }
197
    }
198
199
200
    /**
201
     * Convert an arbitrary string to an alphanumeric string with underscores.
202
     *
203
     * Pre-empt issues with arbitrary strings being used as error codes in XML and PHP.
204
     *
205
     * @param string $baseString Arbitrary string.
206
     *
207
     * @return string
208
     */
209
    public function stringToErrorCode($baseString)
210
    {
211
        return preg_replace('`[^a-z0-9_]`i', '_', strtolower($baseString));
212
    }
213
214
215
    /**
216
     * Strip quotes surrounding an arbitrary string.
217
     *
218
     * Intended for use with the content of a T_CONSTANT_ENCAPSED_STRING / T_DOUBLE_QUOTED_STRING.
219
     *
220
     * @param string $string The raw string.
221
     *
222
     * @return string String without quotes around it.
223
     */
224
    public function stripQuotes($string) {
225
        return preg_replace('`^([\'"])(.*)\1$`Ds', '$2', $string);
226
    }
227
228
229
    /**
230
     * Strip variables from an arbitrary double quoted string.
231
     *
232
     * Intended for use with the content of a T_DOUBLE_QUOTED_STRING.
233
     *
234
     * @param string $string The raw string.
235
     *
236
     * @return string String without variables in it.
237
     */
238
    public function stripVariables($string) {
239
        if (strpos($string, '$') === false) {
240
            return $string;
241
        }
242
243
        return preg_replace( self::REGEX_COMPLEX_VARS, '', $string );
244
    }
245
246
247
    /**
248
     * Make all top level array keys in an array lowercase.
249
     *
250
     * @param array $array Initial array.
251
     *
252
     * @return array Same array, but with all lowercase top level keys.
253
     */
254
    public function arrayKeysToLowercase($array)
255
    {
256
        $keys = array_keys($array);
257
        $keys = array_map('strtolower', $keys);
258
        return array_combine($keys, $array);
259
    }
260
261
262
    /**
263
     * Returns the name(s) of the interface(s) that the specified class implements.
264
     *
265
     * Returns FALSE on error or if there are no implemented interface names.
266
     *
267
     * {@internal Duplicate of same method as introduced in PHPCS 2.7.
268
     * Once the minimum supported PHPCS version for this sniff library goes beyond
269
     * that, this method can be removed and call to it replaced with
270
     * `$phpcsFile->findImplementedInterfaceNames($stackPtr)` calls.}}
271
     *
272
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
273
     * @param int                  $stackPtr  The position of the class token.
274
     *
275
     * @return array|false
276
     */
277
    public function findImplementedInterfaceNames(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
278
    {
279
        if (method_exists($phpcsFile, 'findImplementedInterfaceNames')) {
280
            return $phpcsFile->findImplementedInterfaceNames($stackPtr);
281
        }
282
283
        $tokens = $phpcsFile->getTokens();
284
285
        // Check for the existence of the token.
286
        if (isset($tokens[$stackPtr]) === false) {
287
            return false;
288
        }
289
290
        if ($tokens[$stackPtr]['code'] !== T_CLASS) {
291
            return false;
292
        }
293
294
        if (isset($tokens[$stackPtr]['scope_closer']) === false) {
295
            return false;
296
        }
297
298
        $classOpenerIndex = $tokens[$stackPtr]['scope_opener'];
299
        $implementsIndex  = $phpcsFile->findNext(T_IMPLEMENTS, $stackPtr, $classOpenerIndex);
300
        if ($implementsIndex === false) {
301
            return false;
302
        }
303
304
        $find = array(
305
                 T_NS_SEPARATOR,
306
                 T_STRING,
307
                 T_WHITESPACE,
308
                 T_COMMA,
309
                );
310
311
        $end  = $phpcsFile->findNext($find, ($implementsIndex + 1), ($classOpenerIndex + 1), true);
312
        $name = $phpcsFile->getTokensAsString(($implementsIndex + 1), ($end - $implementsIndex - 1));
313
        $name = trim($name);
314
315
        if ($name === '') {
316
            return false;
317
        } else {
318
            $names = explode(',', $name);
319
            $names = array_map('trim', $names);
320
            return $names;
321
        }
322
323
    }//end findImplementedInterfaceNames()
324
325
326
    /**
327
     * Checks if a function call has parameters.
328
     *
329
     * Expects to be passed the T_STRING stack pointer for the function call.
330
     * If passed a T_STRING which is *not* a function call, the behaviour is unreliable.
331
     *
332
     * Extra feature: If passed an T_ARRAY or T_OPEN_SHORT_ARRAY stack pointer, it
333
     * will detect whether the array has values or is empty.
334
     *
335
     * @link https://github.com/wimg/PHPCompatibility/issues/120
336
     * @link https://github.com/wimg/PHPCompatibility/issues/152
337
     *
338
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
339
     * @param int                  $stackPtr  The position of the function call token.
340
     *
341
     * @return bool
342
     */
343
    public function doesFunctionCallHaveParameters(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
344
    {
345
        $tokens = $phpcsFile->getTokens();
346
347
        // Check for the existence of the token.
348
        if (isset($tokens[$stackPtr]) === false) {
349
            return false;
350
        }
351
352
        // Is this one of the tokens this function handles ?
353
        if (in_array($tokens[$stackPtr]['code'], array(T_STRING, T_ARRAY, T_OPEN_SHORT_ARRAY), true) === false) {
354
            return false;
355
        }
356
357
        $nextNonEmpty = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
358
359
        // Deal with short array syntax.
360
        if ($tokens[$stackPtr]['code'] === T_OPEN_SHORT_ARRAY) {
361
            if (isset($tokens[$stackPtr]['bracket_closer']) === false) {
362
                return false;
363
            }
364
365
            if ($nextNonEmpty === $tokens[$stackPtr]['bracket_closer']) {
366
                // No parameters.
367
                return false;
368
            }
369
            else {
370
                return true;
371
            }
372
        }
373
374
        // Deal with function calls & long arrays.
375
        // Next non-empty token should be the open parenthesis.
376
        if ($nextNonEmpty === false && $tokens[$nextNonEmpty]['code'] !== T_OPEN_PARENTHESIS) {
377
            return false;
378
        }
379
380
        if (isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false) {
381
            return false;
382
        }
383
384
        $closeParenthesis = $tokens[$nextNonEmpty]['parenthesis_closer'];
385
        $nextNextNonEmpty = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $nextNonEmpty + 1, $closeParenthesis + 1, true);
386
387
        if ($nextNextNonEmpty === $closeParenthesis) {
388
            // No parameters.
389
            return false;
390
        }
391
392
        return true;
393
    }
394
395
396
    /**
397
     * Count the number of parameters a function call has been passed.
398
     *
399
     * Expects to be passed the T_STRING stack pointer for the function call.
400
     * If passed a T_STRING which is *not* a function call, the behaviour is unreliable.
401
     *
402
     * Extra feature: If passed an T_ARRAY or T_OPEN_SHORT_ARRAY stack pointer,
403
     * it will return the number of values in the array.
404
     *
405
     * @link https://github.com/wimg/PHPCompatibility/issues/111
406
     * @link https://github.com/wimg/PHPCompatibility/issues/114
407
     * @link https://github.com/wimg/PHPCompatibility/issues/151
408
     *
409
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
410
     * @param int                  $stackPtr  The position of the function call token.
411
     *
412
     * @return int
413
     */
414
    public function getFunctionCallParameterCount(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
415
    {
416
        if ($this->doesFunctionCallHaveParameters($phpcsFile, $stackPtr) === false) {
417
            return 0;
418
        }
419
420
        return count($this->getFunctionCallParameters($phpcsFile, $stackPtr));
421
    }
422
423
424
    /**
425
     * Get information on all parameters passed to a function call.
426
     *
427
     * Expects to be passed the T_STRING stack pointer for the function call.
428
     * If passed a T_STRING which is *not* a function call, the behaviour is unreliable.
429
     *
430
     * Will return an multi-dimentional array with the start token pointer, end token
431
     * pointer and raw parameter value for all parameters. Index will be 1-based.
432
     * If no parameters are found, will return an empty array.
433
     *
434
     * Extra feature: If passed an T_ARRAY or T_OPEN_SHORT_ARRAY stack pointer,
435
     * it will tokenize the values / key/value pairs contained in the array call.
436
     *
437
     * @param PHP_CodeSniffer_File $phpcsFile     The file being scanned.
438
     * @param int                  $stackPtr      The position of the function call token.
439
     *
440
     * @return array
441
     */
442
    public function getFunctionCallParameters(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
443
    {
444
        if ($this->doesFunctionCallHaveParameters($phpcsFile, $stackPtr) === false) {
445
            return array();
446
        }
447
448
        // Ok, we know we have a T_STRING, T_ARRAY or T_OPEN_SHORT_ARRAY with parameters
449
        // and valid open & close brackets/parenthesis.
450
        $tokens = $phpcsFile->getTokens();
451
452
        // Mark the beginning and end tokens.
453
        if ($tokens[$stackPtr]['code'] === T_OPEN_SHORT_ARRAY) {
454
            $opener = $stackPtr;
455
            $closer = $tokens[$stackPtr]['bracket_closer'];
456
457
            $nestedParenthesisCount = 0;
458
        }
459
        else {
460
            $opener = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
461
            $closer = $tokens[$opener]['parenthesis_closer'];
462
463
            $nestedParenthesisCount = 1;
464
        }
465
466
        // Which nesting level is the one we are interested in ?
467 View Code Duplication
        if (isset($tokens[$opener]['nested_parenthesis'])) {
1 ignored issue
show
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...
468
            $nestedParenthesisCount += count($tokens[$opener]['nested_parenthesis']);
469
        }
470
471
        $parameters = array();
472
        $nextComma  = $opener;
473
        $paramStart = $opener + 1;
474
        $cnt        = 1;
475
        while ($nextComma = $phpcsFile->findNext(array(T_COMMA, $tokens[$closer]['code'], T_OPEN_SHORT_ARRAY), $nextComma + 1, $closer + 1)) {
476
            // Ignore anything within short array definition brackets.
477
            if (
478
                $tokens[$nextComma]['type'] === 'T_OPEN_SHORT_ARRAY'
479
                &&
480
                ( isset($tokens[$nextComma]['bracket_opener']) && $tokens[$nextComma]['bracket_opener'] === $nextComma )
481
                &&
482
                isset($tokens[$nextComma]['bracket_closer'])
483
            ) {
484
                // Skip forward to the end of the short array definition.
485
                $nextComma = $tokens[$nextComma]['bracket_closer'];
486
                continue;
487
            }
488
489
            // Ignore comma's at a lower nesting level.
490
            if (
491
                $tokens[$nextComma]['type'] === 'T_COMMA'
492
                &&
493
                isset($tokens[$nextComma]['nested_parenthesis'])
494
                &&
495
                count($tokens[$nextComma]['nested_parenthesis']) !== $nestedParenthesisCount
496
            ) {
497
                continue;
498
            }
499
500
            // Ignore closing parenthesis/bracket if not 'ours'.
501
            if ($tokens[$nextComma]['type'] === $tokens[$closer]['type'] && $nextComma !== $closer) {
502
                continue;
503
            }
504
505
            // Ok, we've reached the end of the parameter.
506
            $parameters[$cnt]['start'] = $paramStart;
507
            $parameters[$cnt]['end']   = $nextComma - 1;
508
            $parameters[$cnt]['raw']   = trim($phpcsFile->getTokensAsString($paramStart, ($nextComma - $paramStart)));
509
510
            // Check if there are more tokens before the closing parenthesis.
511
            // Prevents code like the following from setting a third parameter:
512
            // functionCall( $param1, $param2, );
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% 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...
513
            $hasNextParam = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $nextComma + 1, $closer, true, null, true);
514
            if ($hasNextParam === false) {
515
                break;
516
            }
517
518
            // Prepare for the next parameter.
519
            $paramStart = $nextComma + 1;
520
            $cnt++;
521
        }
522
523
        return $parameters;
524
    }
525
526
527
    /**
528
     * Get information on a specific parameter passed to a function call.
529
     *
530
     * Expects to be passed the T_STRING stack pointer for the function call.
531
     * If passed a T_STRING which is *not* a function call, the behaviour is unreliable.
532
     *
533
     * Will return a array with the start token pointer, end token pointer and the raw value
534
     * of the parameter at a specific offset.
535
     * If the specified parameter is not found, will return false.
536
     *
537
     * @param PHP_CodeSniffer_File $phpcsFile   The file being scanned.
538
     * @param int                  $stackPtr    The position of the function call token.
539
     * @param int                  $paramOffset The 1-based index position of the parameter to retrieve.
540
     *
541
     * @return array|false
542
     */
543
    public function getFunctionCallParameter(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $paramOffset)
544
    {
545
        $parameters = $this->getFunctionCallParameters($phpcsFile, $stackPtr);
546
547
        if (isset($parameters[$paramOffset]) === false) {
548
            return false;
549
        }
550
        else {
551
            return $parameters[$paramOffset];
552
        }
553
    }
554
555
556
    /**
557
     * Verify whether a token is within a scoped condition.
558
     *
559
     * If the optional $validScopes parameter has been passed, the function
560
     * will check that the token has at least one condition which is of a
561
     * type defined in $validScopes.
562
     *
563
     * @param PHP_CodeSniffer_File $phpcsFile   The file being scanned.
564
     * @param int                  $stackPtr    The position of the token.
565
     * @param array|int            $validScopes Optional. Array of valid scopes
566
     *                                          or int value of a valid scope.
567
     *                                          Pass the T_.. constant(s) for the
568
     *                                          desired scope to this parameter.
569
     *
570
     * @return bool Without the optional $scopeTypes: True if within a scope, false otherwise.
571
     *              If the $scopeTypes are set: True if *one* of the conditions is a
572
     *              valid scope, false otherwise.
573
     */
574
    public function tokenHasScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $validScopes = null)
575
    {
576
        $tokens = $phpcsFile->getTokens();
577
578
        // Check for the existence of the token.
579
        if (isset($tokens[$stackPtr]) === false) {
580
            return false;
581
        }
582
583
        // No conditions = no scope.
584
        if (empty($tokens[$stackPtr]['conditions'])) {
585
            return false;
586
        }
587
588
        // Ok, there are conditions, do we have to check for specific ones ?
589
        if (isset($validScopes) === false) {
590
            return true;
591
        }
592
593
        return $phpcsFile->hasCondition($stackPtr, $validScopes);
594
    }
595
596
597
    /**
598
     * Verify whether a token is within a class scope.
599
     *
600
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
601
     * @param int                  $stackPtr  The position of the token.
602
     * @param bool                 $strict    Whether to strictly check for the T_CLASS
603
     *                                        scope or also accept interfaces and traits
604
     *                                        as scope.
605
     *
606
     * @return bool True if within class scope, false otherwise.
607
     */
608
    public function inClassScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $strict = true)
609
    {
610
        $validScopes = array(T_CLASS);
611
        if (defined('T_ANON_CLASS') === true) {
612
            $validScopes[] = T_ANON_CLASS;
613
        }
614
615
        if ($strict === false) {
616
            $validScopes[] = T_INTERFACE;
617
            $validScopes[] = T_TRAIT;
618
        }
619
620
        return $phpcsFile->hasCondition($stackPtr, $validScopes);
621
    }
622
623
624
    /**
625
     * Verify whether a token is within a scoped use statement.
626
     *
627
     * PHPCS cross-version compatibility method.
628
     *
629
     * In PHPCS 1.x no conditions are set for a scoped use statement.
630
     * This method works around that limitation.
631
     *
632
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
633
     * @param int                  $stackPtr  The position of the token.
634
     *
635
     * @return bool True if within use scope, false otherwise.
636
     */
637
    public function inUseScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
638
    {
639
        static $isLowPHPCS, $ignoreTokens;
640
641
        if (isset($isLowPHPCS) === false) {
642
            $isLowPHPCS = version_compare(PHP_CodeSniffer::VERSION, '2.0', '<');
643
        }
644
        if (isset($ignoreTokens) === false) {
645
            $ignoreTokens              = PHP_CodeSniffer_Tokens::$emptyTokens;
646
            $ignoreTokens[T_STRING]    = T_STRING;
647
            $ignoreTokens[T_AS]        = T_AS;
648
            $ignoreTokens[T_PUBLIC]    = T_PUBLIC;
649
            $ignoreTokens[T_PROTECTED] = T_PROTECTED;
650
            $ignoreTokens[T_PRIVATE]   = T_PRIVATE;
651
        }
652
653
        // PHPCS 2.0.
654
        if ($isLowPHPCS === false) {
655
            return $phpcsFile->hasCondition($stackPtr, T_USE);
656
        } else {
657
            // PHPCS 1.x.
658
            $tokens         = $phpcsFile->getTokens();
659
            $maybeCurlyOpen = $phpcsFile->findPrevious($ignoreTokens, ($stackPtr - 1), null, true);
660
            if ($tokens[$maybeCurlyOpen]['code'] === T_OPEN_CURLY_BRACKET) {
661
                $maybeUseStatement = $phpcsFile->findPrevious($ignoreTokens, ($maybeCurlyOpen - 1), null, true);
662
                if ($tokens[$maybeUseStatement]['code'] === T_USE) {
663
                    return true;
664
                }
665
            }
666
            return false;
667
        }
668
    }
669
670
671
    /**
672
     * Returns the fully qualified class name for a new class instantiation.
673
     *
674
     * Returns an empty string if the class name could not be reliably inferred.
675
     *
676
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
677
     * @param int                  $stackPtr  The position of a T_NEW token.
678
     *
679
     * @return string
680
     */
681
    public function getFQClassNameFromNewToken(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
682
    {
683
        $tokens = $phpcsFile->getTokens();
684
685
        // Check for the existence of the token.
686
        if (isset($tokens[$stackPtr]) === false) {
687
            return '';
688
        }
689
690
        if ($tokens[$stackPtr]['code'] !== T_NEW) {
691
            return '';
692
        }
693
694
        $find = array(
695
                 T_NS_SEPARATOR,
696
                 T_STRING,
697
                 T_NAMESPACE,
698
                 T_WHITESPACE,
699
                );
700
701
        $start = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
702
703
        if ($start === false) {
704
            return '';
705
        }
706
707
        // Bow out if the next token is a variable as we don't know where it was defined.
708
        if ($tokens[$start]['code'] === T_VARIABLE) {
709
            return '';
710
        }
711
712
        $end       = $phpcsFile->findNext($find, ($start + 1), null, true, null, true);
713
        $className = $phpcsFile->getTokensAsString($start, ($end - $start));
714
        $className = trim($className);
715
716
        return $this->getFQName($phpcsFile, $stackPtr, $className);
717
    }
718
719
720
    /**
721
     * Returns the fully qualified name of the class that the specified class extends.
722
     *
723
     * Returns an empty string if the class does not extend another class or if
724
     * the class name could not be reliably inferred.
725
     *
726
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
727
     * @param int                  $stackPtr  The position of a T_CLASS token.
728
     *
729
     * @return string
730
     */
731
    public function getFQExtendedClassName(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
732
    {
733
        $tokens = $phpcsFile->getTokens();
734
735
        // Check for the existence of the token.
736
        if (isset($tokens[$stackPtr]) === false) {
737
            return '';
738
        }
739
740
        if ($tokens[$stackPtr]['code'] !== T_CLASS) {
741
            return '';
742
        }
743
744
        $extends = $phpcsFile->findExtendedClassName($stackPtr);
745
        if (empty($extends) || is_string($extends) === false) {
746
            return '';
747
        }
748
749
        return $this->getFQName($phpcsFile, $stackPtr, $extends);
750
    }
751
752
753
    /**
754
     * Returns the class name for the static usage of a class.
755
     * This can be a call to a method, the use of a property or constant.
756
     *
757
     * Returns an empty string if the class name could not be reliably inferred.
758
     *
759
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
760
     * @param int                  $stackPtr  The position of a T_NEW token.
761
     *
762
     * @return string
763
     */
764
    public function getFQClassNameFromDoubleColonToken(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
765
    {
766
        $tokens = $phpcsFile->getTokens();
767
768
        // Check for the existence of the token.
769
        if (isset($tokens[$stackPtr]) === false) {
770
            return '';
771
        }
772
773
        if ($tokens[$stackPtr]['code'] !== T_DOUBLE_COLON) {
774
            return '';
775
        }
776
777
        // Nothing to do if previous token is a variable as we don't know where it was defined.
778
        if ($tokens[$stackPtr - 1]['code'] === T_VARIABLE) {
779
            return '';
780
        }
781
782
        // Nothing to do if 'parent' or 'static' as we don't know how far the class tree extends.
783
        if (in_array($tokens[$stackPtr - 1]['code'], array(T_PARENT, T_STATIC), true)) {
784
            return '';
785
        }
786
787
        // Get the classname from the class declaration if self is used.
788
        if ($tokens[$stackPtr - 1]['code'] === T_SELF) {
789
            $classDeclarationPtr = $phpcsFile->findPrevious(T_CLASS, $stackPtr - 1);
790
            if ($classDeclarationPtr === false) {
791
                return '';
792
            }
793
            $className = $phpcsFile->getDeclarationName($classDeclarationPtr);
794
            return $this->getFQName($phpcsFile, $classDeclarationPtr, $className);
795
        }
796
797
        $find = array(
798
                 T_NS_SEPARATOR,
799
                 T_STRING,
800
                 T_NAMESPACE,
801
                 T_WHITESPACE,
802
                );
803
804
        $start     = ($phpcsFile->findPrevious($find, $stackPtr - 1, null, true, null, true) + 1);
805
        $className = $phpcsFile->getTokensAsString($start, ($stackPtr - $start));
806
        $className = trim($className);
807
808
        return $this->getFQName($phpcsFile, $stackPtr, $className);
809
    }
810
811
812
    /**
813
     * Get the Fully Qualified name for a class/function/constant etc.
814
     *
815
     * Checks if a class/function/constant name is already fully qualified and
816
     * if not, enrich it with the relevant namespace information.
817
     *
818
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
819
     * @param int                  $stackPtr  The position of the token.
820
     * @param string               $name      The class / function / constant name.
821
     *
822
     * @return string
823
     */
824
    public function getFQName(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $name)
825
    {
826
        if (strpos($name, '\\' ) === 0) {
827
            // Already fully qualified.
828
            return $name;
829
        }
830
831
        // Remove the namespace keyword if used.
832
        if (strpos($name, 'namespace\\') === 0) {
833
            $name = substr($name, 10);
834
        }
835
836
        $namespace = $this->determineNamespace($phpcsFile, $stackPtr);
837
838
        if ($namespace === '') {
839
            return '\\' . $name;
840
        }
841
        else {
842
            return '\\' . $namespace . '\\' . $name;
843
        }
844
    }
845
846
847
    /**
848
     * Is the class/function/constant name namespaced or global ?
849
     *
850
     * @param string $FQName Fully Qualified name of a class, function etc.
851
     *                       I.e. should always start with a `\` !
852
     *
853
     * @return bool True if namespaced, false if global.
854
     */
855
    public function isNamespaced($FQName) {
856
        if (strpos($FQName, '\\') !== 0) {
857
            throw new PHP_CodeSniffer_Exception('$FQName must be a fully qualified name');
858
        }
859
860
        return (strpos(substr($FQName, 1), '\\') !== false);
861
    }
862
863
864
    /**
865
     * Determine the namespace name an arbitrary token lives in.
866
     *
867
     * @param PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile.
868
     * @param int                  $stackPtr  The token position for which to determine the namespace.
869
     *
870
     * @return string Namespace name or empty string if it couldn't be determined or no namespace applies.
871
     */
872
    public function determineNamespace(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
873
    {
874
        $tokens = $phpcsFile->getTokens();
875
876
        // Check for the existence of the token.
877
        if (isset($tokens[$stackPtr]) === false) {
878
            return '';
879
        }
880
881
        // Check for scoped namespace {}.
882
        if (empty($tokens[$stackPtr]['conditions']) === false) {
883
            foreach ($tokens[$stackPtr]['conditions'] as $pointer => $type) {
884
                if ($type === T_NAMESPACE) {
885
                    $namespace = $this->getDeclaredNamespaceName($phpcsFile, $pointer);
886
                    if ($namespace !== false) {
887
                        return $namespace;
888
                    }
889
                    break; // Nested namespaces is not possible.
890
                }
891
            }
892
        }
893
894
        /*
895
         * Not in a scoped namespace, so let's see if we can find a non-scoped namespace instead.
896
         * Keeping in mind that:
897
         * - there can be multiple non-scoped namespaces in a file (bad practice, but it happens).
898
         * - the namespace keyword can also be used as part of a function/method call and such.
899
         * - that a non-named namespace resolves to the global namespace.
900
         */
901
        $previousNSToken = $stackPtr;
902
        $namespace       = false;
903
        do {
904
            $previousNSToken = $phpcsFile->findPrevious(T_NAMESPACE, $previousNSToken -1);
905
906
            // Stop if we encounter a scoped namespace declaration as we already know we're not in one.
907
            if (empty($tokens[$previousNSToken]['scope_condition']) === false && $tokens[$previousNSToken]['scope_condition'] === $previousNSToken) {
908
                break;
909
            }
910
            $namespace = $this->getDeclaredNamespaceName($phpcsFile, $previousNSToken);
911
912
        } while ($namespace === false && $previousNSToken !== false);
913
914
        // If we still haven't got a namespace, return an empty string.
915
        if ($namespace === false) {
916
            return '';
917
        }
918
        else {
919
            return $namespace;
920
        }
921
    }
922
923
    /**
924
     * Get the complete namespace name for a namespace declaration.
925
     *
926
     * For hierarchical namespaces, the name will be composed of several tokens,
927
     * i.e. MyProject\Sub\Level which will be returned together as one string.
928
     *
929
     * @param PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile.
930
     * @param int|bool             $stackPtr  The position of a T_NAMESPACE token.
931
     *
932
     * @return string|false Namespace name or false if not a namespace declaration.
933
     *                      Namespace name can be an empty string for global namespace declaration.
934
     */
935
    public function getDeclaredNamespaceName(PHP_CodeSniffer_File $phpcsFile, $stackPtr )
936
    {
937
        $tokens = $phpcsFile->getTokens();
938
939
        // Check for the existence of the token.
940
        if ($stackPtr === false || isset($tokens[$stackPtr]) === false) {
941
            return false;
942
        }
943
944
        if ($tokens[$stackPtr]['code'] !== T_NAMESPACE) {
945
            return false;
946
        }
947
948
        if ($tokens[$stackPtr + 1]['code'] === T_NS_SEPARATOR) {
949
            // Not a namespace declaration, but use of, i.e. namespace\someFunction();
950
            return false;
951
        }
952
953
        $nextToken = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
954
        if ($tokens[$nextToken]['code'] === T_OPEN_CURLY_BRACKET) {
955
            // Declaration for global namespace when using multiple namespaces in a file.
956
            // I.e.: namespace {}
957
            return '';
958
        }
959
960
        // Ok, this should be a namespace declaration, so get all the parts together.
961
        $validTokens = array(
962
                        T_STRING,
963
                        T_NS_SEPARATOR,
964
                        T_WHITESPACE,
965
                       );
966
967
        $namespaceName = '';
968
        while(in_array($tokens[$nextToken]['code'], $validTokens, true) === true) {
969
            $namespaceName .= trim($tokens[$nextToken]['content']);
970
            $nextToken++;
971
        }
972
973
        return $namespaceName;
974
    }
975
976
977
    /**
978
     * Get the stack pointer for a return type token for a given function.
979
     *
980
     * Compatible layer for older PHPCS versions which don't recognize
981
     * return type hints correctly.
982
     *
983
     * Expects to be passed T_RETURN_TYPE, T_FUNCTION or T_CLOSURE token.
984
     *
985
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
986
     * @param int                  $stackPtr  The position of the token.
987
     *
988
     * @return int|false Stack pointer to the return type token or false if
989
     *                   no return type was found or the passed token was
990
     *                   not of the correct type.
991
     */
992
    public function getReturnTypeHintToken(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
993
    {
994
        $tokens = $phpcsFile->getTokens();
995
996
        if (defined('T_RETURN_TYPE') && $tokens[$stackPtr]['code'] === T_RETURN_TYPE) {
997
            return $tokens[$stackPtr]['code'];
998
        }
999
1000 View Code Duplication
        if ($tokens[$stackPtr]['code'] !== T_FUNCTION && $tokens[$stackPtr]['code'] !== T_CLOSURE) {
1 ignored issue
show
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...
1001
            return false;
1002
        }
1003
1004
        if (isset($tokens[$stackPtr]['parenthesis_closer'], $tokens[$stackPtr]['scope_opener']) === false
1005
            || ($tokens[$stackPtr]['parenthesis_closer'] + 1) === $tokens[$stackPtr]['scope_opener']
1006
        ) {
1007
            return false;
1008
        }
1009
1010
        $hasColon = $phpcsFile->findNext(array(T_COLON, T_INLINE_ELSE), ($tokens[$stackPtr]['parenthesis_closer'] + 1), $tokens[$stackPtr]['scope_opener']);
1011
        if ($hasColon === false) {
1012
            return false;
1013
        }
1014
1015
        // `self` and `callable` are not being recognized as return types in PHPCS < 2.6.0.
1016
        $unrecognizedTypes = array(
1017
            T_CALLABLE,
1018
            T_SELF,
1019
        );
1020
1021
        // Return types are not recognized at all in PHPCS < 2.4.0.
1022
        if (defined('T_RETURN_TYPE') === false) {
1023
            $unrecognizedTypes[] = T_ARRAY;
1024
            $unrecognizedTypes[] = T_STRING;
1025
        }
1026
1027
        return $phpcsFile->findNext($unrecognizedTypes, ($hasColon + 1), $tokens[$stackPtr]['scope_opener']);
1028
    }
1029
1030
1031
    /**
1032
     * Returns the method parameters for the specified function token.
1033
     *
1034
     * Each parameter is in the following format:
1035
     *
1036
     * <code>
1037
     *   0 => array(
1038
     *         'name'              => '$var',  // The variable name.
1039
     *         'content'           => string,  // The full content of the variable definition.
1040
     *         'pass_by_reference' => boolean, // Is the variable passed by reference?
1041
     *         'type_hint'         => string,  // The type hint for the variable.
1042
     *         'nullable_type'     => boolean, // Is the variable using a nullable type?
1043
     *        )
1044
     * </code>
1045
     *
1046
     * Parameters with default values have an additional array index of
1047
     * 'default' with the value of the default as a string.
1048
     *
1049
     * {@internal Duplicate of same method as contained in the `PHP_CodeSniffer_File`
1050
     * class, but with some improvements which have been introduced in
1051
     * PHPCS 2.8.0.
1052
     * {@link https://github.com/squizlabs/PHP_CodeSniffer/pull/1117},
1053
     * {@link https://github.com/squizlabs/PHP_CodeSniffer/pull/1193} and
1054
     * {@link https://github.com/squizlabs/PHP_CodeSniffer/pull/1293}.
1055
     *
1056
     * Once the minimum supported PHPCS version for this standard goes beyond
1057
     * that, this method can be removed and calls to it replaced with
1058
     * `$phpcsFile->getMethodParameters($stackPtr)` calls.
1059
     *
1060
     * NOTE: This version does not deal with the new T_NULLABLE token type.
1061
     * This token is included upstream only in 2.7.2+ and as we defer to upstream
1062
     * in that case, no need to deal with it here.
1063
     *
1064
     * Last synced with PHPCS version: PHPCS 2.7.2-alpha.}}
1065
     *
1066
     * @param PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile.
1067
     * @param int                  $stackPtr  The position in the stack of the
1068
     *                                        function token to acquire the
1069
     *                                        parameters for.
1070
     *
1071
     * @return array|false
1072
     * @throws PHP_CodeSniffer_Exception If the specified $stackPtr is not of
1073
     *                                   type T_FUNCTION or T_CLOSURE.
1074
     */
1075
    public function getMethodParameters(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
1076
    {
1077
        if (version_compare(PHP_CodeSniffer::VERSION, '2.7.1', '>') === true) {
1078
            return $phpcsFile->getMethodParameters($stackPtr);
1079
        }
1080
1081
        $tokens = $phpcsFile->getTokens();
1082
1083
        // Check for the existence of the token.
1084
        if (isset($tokens[$stackPtr]) === false) {
1085
            return false;
1086
        }
1087
1088 View Code Duplication
        if ($tokens[$stackPtr]['code'] !== T_FUNCTION && $tokens[$stackPtr]['code'] !== T_CLOSURE) {
1 ignored issue
show
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...
1089
            throw new PHP_CodeSniffer_Exception('$stackPtr must be of type T_FUNCTION or T_CLOSURE');
1090
        }
1091
1092
        $opener = $tokens[$stackPtr]['parenthesis_opener'];
1093
        $closer = $tokens[$stackPtr]['parenthesis_closer'];
1094
1095
        $vars            = array();
1096
        $currVar         = null;
1097
        $paramStart      = ($opener + 1);
1098
        $defaultStart    = null;
1099
        $paramCount      = 0;
1100
        $passByReference = false;
1101
        $variableLength  = false;
1102
        $typeHint        = '';
1103
        $nullableType    = false;
1104
1105
        for ($i = $paramStart; $i <= $closer; $i++) {
1106
            // Check to see if this token has a parenthesis or bracket opener. If it does
1107
            // it's likely to be an array which might have arguments in it. This
1108
            // could cause problems in our parsing below, so lets just skip to the
1109
            // end of it.
1110 View Code Duplication
            if (isset($tokens[$i]['parenthesis_opener']) === true) {
1 ignored issue
show
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...
1111
                // Don't do this if it's the close parenthesis for the method.
1112
                if ($i !== $tokens[$i]['parenthesis_closer']) {
1113
                    $i = ($tokens[$i]['parenthesis_closer'] + 1);
1114
                }
1115
            }
1116
1117 View Code Duplication
            if (isset($tokens[$i]['bracket_opener']) === true) {
1 ignored issue
show
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...
1118
                // Don't do this if it's the close parenthesis for the method.
1119
                if ($i !== $tokens[$i]['bracket_closer']) {
1120
                    $i = ($tokens[$i]['bracket_closer'] + 1);
1121
                }
1122
            }
1123
1124
            switch ($tokens[$i]['code']) {
1125
            case T_BITWISE_AND:
1126
                $passByReference = true;
1127
                break;
1128
            case T_VARIABLE:
1129
                $currVar = $i;
1130
                break;
1131
            case T_ELLIPSIS:
1132
                $variableLength = true;
1133
                break;
1134
            case T_ARRAY_HINT:
1135
            case T_CALLABLE:
1136
                $typeHint .= $tokens[$i]['content'];
1137
                break;
1138
            case T_SELF:
1139
            case T_PARENT:
1140
            case T_STATIC:
1141
                // Self is valid, the others invalid, but were probably intended as type hints.
1142
                if (isset($defaultStart) === false) {
1143
                    $typeHint .= $tokens[$i]['content'];
1144
                }
1145
                break;
1146
            case T_STRING:
1147
                // This is a string, so it may be a type hint, but it could
1148
                // also be a constant used as a default value.
1149
                $prevComma = false;
1150 View Code Duplication
                for ($t = $i; $t >= $opener; $t--) {
1 ignored issue
show
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...
1151
                    if ($tokens[$t]['code'] === T_COMMA) {
1152
                        $prevComma = $t;
1153
                        break;
1154
                    }
1155
                }
1156
1157
                if ($prevComma !== false) {
1158
                    $nextEquals = false;
1159 View Code Duplication
                    for ($t = $prevComma; $t < $i; $t++) {
1 ignored issue
show
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...
1160
                        if ($tokens[$t]['code'] === T_EQUAL) {
1161
                            $nextEquals = $t;
1162
                            break;
1163
                        }
1164
                    }
1165
1166
                    if ($nextEquals !== false) {
1167
                        break;
1168
                    }
1169
                }
1170
1171
                if ($defaultStart === null) {
1172
                    $typeHint .= $tokens[$i]['content'];
1173
                }
1174
                break;
1175
            case T_NS_SEPARATOR:
1176
                // Part of a type hint or default value.
1177
                if ($defaultStart === null) {
1178
                    $typeHint .= $tokens[$i]['content'];
1179
                }
1180
                break;
1181
            case T_INLINE_THEN:
1182
                if ($defaultStart === null) {
1183
                    $nullableType = true;
1184
                    $typeHint    .= $tokens[$i]['content'];
1185
                }
1186
                break;
1187
            case T_CLOSE_PARENTHESIS:
1188
            case T_COMMA:
1189
                // If it's null, then there must be no parameters for this
1190
                // method.
1191
                if ($currVar === null) {
1192
                    continue;
1193
                }
1194
1195
                $vars[$paramCount]            = array();
1196
                $vars[$paramCount]['name']    = $tokens[$currVar]['content'];
1197
                $vars[$paramCount]['content'] = trim($phpcsFile->getTokensAsString($paramStart, ($i - $paramStart)));
1198
1199
                if ($defaultStart !== null) {
1200
                    $vars[$paramCount]['default']
1201
                        = $phpcsFile->getTokensAsString(
1202
                            $defaultStart,
1203
                            ($i - $defaultStart)
1204
                        );
1205
                }
1206
1207
                $vars[$paramCount]['pass_by_reference'] = $passByReference;
1208
                $vars[$paramCount]['variable_length']   = $variableLength;
1209
                $vars[$paramCount]['type_hint']         = $typeHint;
1210
                $vars[$paramCount]['nullable_type']     = $nullableType;
1211
1212
                // Reset the vars, as we are about to process the next parameter.
1213
                $defaultStart    = null;
1214
                $paramStart      = ($i + 1);
1215
                $passByReference = false;
1216
                $variableLength  = false;
1217
                $typeHint        = '';
1218
                $nullableType    = false;
1219
1220
                $paramCount++;
1221
                break;
1222
            case T_EQUAL:
1223
                $defaultStart = ($i + 1);
1224
                break;
1225
            }//end switch
1226
        }//end for
1227
1228
        return $vars;
1229
1230
    }//end getMethodParameters()
1231
1232
1233
    /**
1234
     * Get the hash algorithm name from the parameter in a hash function call.
1235
     *
1236
     * @param PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile.
1237
     * @param int                  $stackPtr  The position of the T_STRING function token.
1238
     *
1239
     * @return string|false The algorithm name without quotes if this was a relevant hash
1240
     *                      function call or false if it was not.
1241
     */
1242
    public function getHashAlgorithmParameter(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
1243
    {
1244
        $tokens = $phpcsFile->getTokens();
1245
1246
        // Check for the existence of the token.
1247
        if (isset($tokens[$stackPtr]) === false) {
1248
            return false;
1249
        }
1250
1251
        if ($tokens[$stackPtr]['code'] !== T_STRING) {
1252
            return false;
1253
        }
1254
1255
        $functionName   = $tokens[$stackPtr]['content'];
1256
        $functionNameLc = strtolower($functionName);
1257
1258
        // Bow out if not one of the functions we're targetting.
1259
        if (isset($this->hashAlgoFunctions[$functionNameLc]) === false) {
1260
            return false;
1261
        }
1262
1263
        // Get the parameter from the function call which should contain the algorithm name.
1264
        $algoParam = $this->getFunctionCallParameter($phpcsFile, $stackPtr, $this->hashAlgoFunctions[$functionNameLc]);
1265
        if ($algoParam === false) {
1266
            return false;
1267
        }
1268
1269
        /**
1270
         * Algorithm is a text string, so we need to remove the quotes.
1271
         */
1272
        $algo = strtolower(trim($algoParam['raw']));
1273
        $algo = $this->stripQuotes($algo);
1274
1275
        return $algo;
1276
    }
1277
1278
}//end class
1279