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