ForbiddenNamesAsInvokedFunctionsSniff   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 165
Duplicated Lines 3.03 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 20
lcom 1
cbo 1
dl 5
loc 165
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A register() 0 20 4
C process() 5 76 16

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * \PHPCompatibility\Sniffs\PHP\ForbiddenNamesAsInvokedFunctionsSniff.
4
 *
5
 * @category  PHP
6
 * @package   PHPCompatibility
7
 * @author    Jansen Price <[email protected]>
8
 * @copyright 2012 Cu.be Solutions bvba
9
 */
10
11
namespace PHPCompatibility\Sniffs\PHP;
12
13
use PHPCompatibility\Sniff;
14
15
/**
16
 * \PHPCompatibility\Sniffs\PHP\ForbiddenNamesAsInvokedFunctionsSniff.
17
 *
18
 * Prohibits the use of reserved keywords invoked as functions.
19
 *
20
 * @category  PHP
21
 * @package   PHPCompatibility
22
 * @author    Jansen Price <[email protected]>
23
 * @copyright 2012 Cu.be Solutions bvba
24
 */
25
class ForbiddenNamesAsInvokedFunctionsSniff extends Sniff
26
{
27
28
    /**
29
     * List of tokens to register.
30
     *
31
     * @var array
32
     */
33
    protected $targetedTokens = array(
34
        T_ABSTRACT   => '5.0',
35
        T_CALLABLE   => '5.4',
36
        T_CATCH      => '5.0',
37
        T_FINAL      => '5.0',
38
        T_GOTO       => '5.3',
39
        T_IMPLEMENTS => '5.0',
40
        T_INTERFACE  => '5.0',
41
        T_INSTANCEOF => '5.0',
42
        T_NAMESPACE  => '5.3',
43
        T_PRIVATE    => '5.0',
44
        T_PROTECTED  => '5.0',
45
        T_PUBLIC     => '5.0',
46
        T_TRY        => '5.0',
47
    );
48
49
    /**
50
     * T_STRING keywords to recognize as targetted tokens.
51
     *
52
     * Compatibility for PHP versions where the keyword is not yet recognized
53
     * as its own token and for PHPCS versions which change the token to
54
     * T_STRING when used in a method call.
55
     *
56
     * @var array
57
     */
58
    protected $targetedStringTokens = array(
59
        'abstract'   => '5.0',
60
        'callable'   => '5.4',
61
        'catch'      => '5.0',
62
        'final'      => '5.0',
63
        'finally'    => '5.5',
64
        'goto'       => '5.3',
65
        'implements' => '5.0',
66
        'interface'  => '5.0',
67
        'instanceof' => '5.0',
68
        'insteadof'  => '5.4',
69
        'namespace'  => '5.3',
70
        'private'    => '5.0',
71
        'protected'  => '5.0',
72
        'public'     => '5.0',
73
        'trait'      => '5.4',
74
        'try'        => '5.0',
75
    );
76
77
    /**
78
     * Returns an array of tokens this test wants to listen for.
79
     *
80
     * @return array
81
     */
82
    public function register()
83
    {
84
        if (defined('T_FINALLY')) {
85
            // phpcs:ignore PHPCompatibility.PHP.NewConstants.t_finallyFound
86
            $this->targetedTokens[T_FINALLY] = '5.5';
87
        }
88
        if (defined('T_INSTEADOF')) {
89
            // phpcs:ignore PHPCompatibility.PHP.NewConstants.t_insteadofFound
90
            $this->targetedTokens[T_INSTEADOF] = '5.4';
91
        }
92
        if (defined('T_TRAIT')) {
93
            // phpcs:ignore PHPCompatibility.PHP.NewConstants.t_traitFound
94
            $this->targetedTokens[T_TRAIT] = '5.4';
95
        }
96
97
        $tokens   = array_keys($this->targetedTokens);
98
        $tokens[] = T_STRING;
99
100
        return $tokens;
101
    }//end register()
102
103
    /**
104
     * Processes this test, when one of its tokens is encountered.
105
     *
106
     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
107
     * @param int                   $stackPtr  The position of the current token in the
108
     *                                         stack passed in $tokens.
109
     *
110
     * @return void
111
     */
112
    public function process(\PHP_CodeSniffer_File $phpcsFile, $stackPtr)
113
    {
114
        $tokens         = $phpcsFile->getTokens();
115
        $tokenCode      = $tokens[$stackPtr]['code'];
116
        $tokenContentLc = strtolower($tokens[$stackPtr]['content']);
117
        $isString       = false;
118
119
        /*
120
         * For string tokens we only care if the string is a reserved word used
121
         * as a function. This only happens in older versions of PHP where the
122
         * token doesn't exist yet for that keyword or in later versions when the
123
         * token is used in a method invocation.
124
         */
125
        if ($tokenCode === T_STRING
126
            && (isset($this->targetedStringTokens[$tokenContentLc]) === false)
127
        ) {
128
            return;
129
        }
130
131
        if ($tokenCode === T_STRING) {
132
            $isString = true;
133
        }
134
135
        // Make sure this is a function call.
136
        $next = $phpcsFile->findNext(\PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true);
137
        if ($next === false || $tokens[$next]['code'] !== T_OPEN_PARENTHESIS) {
138
            // Not a function call.
139
            return;
140
        }
141
142
        // This sniff isn't concerned about function declarations.
143
        $prev = $phpcsFile->findPrevious(\PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true);
144
        if ($prev !== false && $tokens[$prev]['code'] === T_FUNCTION) {
145
            return;
146
        }
147
148
        /*
149
         * Deal with PHP 7 relaxing the rules.
150
         * "As of PHP 7.0.0 these keywords are allowed as property, constant, and method names
151
         * of classes, interfaces and traits...", i.e. they can be invoked as a method call.
152
         *
153
         * Only needed for those keywords which we sniff out via T_STRING.
154
         */
155 View Code Duplication
        if (($tokens[$prev]['code'] === T_OBJECT_OPERATOR || $tokens[$prev]['code'] === T_DOUBLE_COLON)
156
            && $this->supportsBelow('5.6') === false
157
        ) {
158
            return;
159
        }
160
161
        // For the word catch, it is valid to have an open parenthesis
162
        // after it, but only if it is preceded by a right curly brace.
163
        if ($tokenCode === T_CATCH) {
164
            if ($prev !== false && $tokens[$prev]['code'] === T_CLOSE_CURLY_BRACKET) {
165
                // Ok, it's fine.
166
                return;
167
            }
168
        }
169
170
        if ($isString === true) {
171
            $version = $this->targetedStringTokens[$tokenContentLc];
172
        } else {
173
            $version = $this->targetedTokens[$tokenCode];
174
        }
175
176
        if ($this->supportsAbove($version)) {
177
            $error     = "'%s' is a reserved keyword introduced in PHP version %s and cannot be invoked as a function (%s)";
178
            $errorCode = $this->stringToErrorCode($tokenContentLc).'Found';
179
            $data      = array(
180
                $tokenContentLc,
181
                $version,
182
                $tokens[$stackPtr]['type'],
183
            );
184
185
            $phpcsFile->addError($error, $stackPtr, $errorCode, $data);
186
        }
187
    }//end process()
188
189
}//end class
190