ForbiddenClosureUseVariableNamesSniff   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 91
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 18
lcom 1
cbo 2
dl 0
loc 91
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A register() 0 5 1
C process() 0 66 17
1
<?php
2
/**
3
 * PHP 7.1 Forbidden variable names in closure use statements.
4
 *
5
 * PHP version 7.1
6
 *
7
 * @category PHP
8
 * @package  PHPCompatibility
9
 * @author   Juliette Reinders Folmer <[email protected]>
10
 */
11
12
namespace PHPCompatibility\Sniffs\PHP;
13
14
use PHPCompatibility\Sniff;
15
use PHPCompatibility\PHPCSHelper;
16
17
/**
18
 * PHP 7.1 Forbidden variable names in closure use statements.
19
 *
20
 * Variables bound to a closure via the use construct cannot use the same name
21
 * as any superglobals, $this, or any parameter since PHP 7.1.
22
 *
23
 * PHP version 7.1
24
 *
25
 * @category PHP
26
 * @package  PHPCompatibility
27
 * @author   Juliette Reinders Folmer <[email protected]>
28
 */
29
class ForbiddenClosureUseVariableNamesSniff extends Sniff
30
{
31
32
    /**
33
     * Returns an array of tokens this test wants to listen for.
34
     *
35
     * @return array
36
     */
37
    public function register()
38
    {
39
        return array(T_USE);
40
41
    }//end register()
42
43
    /**
44
     * Processes this test, when one of its tokens is encountered.
45
     *
46
     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
47
     * @param int                   $stackPtr  The position of the current token
48
     *                                         in the stack passed in $tokens.
49
     *
50
     * @return void
51
     */
52
    public function process(\PHP_CodeSniffer_File $phpcsFile, $stackPtr)
53
    {
54
        if ($this->supportsAbove('7.1') === false) {
55
            return;
56
        }
57
58
        $tokens = $phpcsFile->getTokens();
59
60
        // Verify this use statement is used with a closure - if so, it has to have parenthesis before it.
61
        $previousNonEmpty = $phpcsFile->findPrevious(\PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true, null, true);
62
        if ($previousNonEmpty === false || $tokens[$previousNonEmpty]['code'] !== T_CLOSE_PARENTHESIS
63
            || isset($tokens[$previousNonEmpty]['parenthesis_opener']) === false
64
        ) {
65
            return;
66
        }
67
68
        // ... and (a variable within) parenthesis after it.
69
        $nextNonEmpty = $phpcsFile->findNext(\PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true, null, true);
70
        if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== T_OPEN_PARENTHESIS) {
71
            return;
72
        }
73
74
        if (isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false) {
75
            // Live coding.
76
            return;
77
        }
78
79
        $closurePtr = $phpcsFile->findPrevious(\PHP_CodeSniffer_Tokens::$emptyTokens, ($tokens[$previousNonEmpty]['parenthesis_opener'] - 1), null, true);
80
        if ($closurePtr === false || $tokens[$closurePtr]['code'] !== T_CLOSURE) {
81
            return;
82
        }
83
84
        // Get the parameters declared by the closure.
85
        $closureParams = PHPCSHelper::getMethodParameters($phpcsFile, $closurePtr);
86
87
        $errorMsg = 'Variables bound to a closure via the use construct cannot use the same name as superglobals, $this, or a declared parameter since PHP 7.1. Found: %s';
88
89
        for ($i = ($nextNonEmpty + 1); $i < $tokens[$nextNonEmpty]['parenthesis_closer']; $i++) {
90
            if ($tokens[$i]['code'] !== T_VARIABLE) {
91
                continue;
92
            }
93
94
            $variableName = $tokens[$i]['content'];
95
96
            if ($variableName === '$this') {
97
                $phpcsFile->addError($errorMsg, $i, 'FoundThis', array($variableName));
98
                continue;
99
            }
100
101
            if (in_array($variableName, $this->superglobals, true) === true) {
102
                $phpcsFile->addError($errorMsg, $i, 'FoundSuperglobal', array($variableName));
103
                continue;
104
            }
105
106
            // Check whether it is one of the parameters declared by the closure.
107
            if (empty($closureParams) === false) {
108
                foreach ($closureParams as $param) {
109
                    if ($param['name'] === $variableName) {
110
                        $phpcsFile->addError($errorMsg, $i, 'FoundShadowParam', array($variableName));
111
                        continue 2;
112
                    }
113
                }
114
            }
115
        }
116
117
    }//end process()
118
119
}//end class
120