Completed
Push — master ( 8a0c50...f00019 )
by Wim
02:16
created

processNonString()   C

Complexity

Conditions 18
Paths 19

Size

Total Lines 67
Code Lines 33

Duplication

Lines 8
Ratio 11.94 %

Importance

Changes 0
Metric Value
dl 8
loc 67
rs 5.7575
c 0
b 0
f 0
cc 18
eloc 33
nc 19
nop 3

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * PHPCompatibility_Sniffs_PHP_ForbiddenNamesSniff.
4
 *
5
 * PHP version 5.4
6
 *
7
 * @category  PHP
8
 * @package   PHPCompatibility
9
 * @author    Wim Godden <[email protected]>
10
 * @copyright 2012 Cu.be Solutions bvba
11
 */
12
13
/**
14
 * PHPCompatibility_Sniffs_PHP_ForbiddenNamesSniff.
15
 *
16
 * Prohibits the use of reserved keywords as class, function, namespace or constant names.
17
 *
18
 * PHP version 5.4
19
 *
20
 * @category  PHP
21
 * @package   PHPCompatibility
22
 * @author    Wim Godden <[email protected]>
23
 * @copyright 2012 Cu.be Solutions bvba
24
 */
25
class PHPCompatibility_Sniffs_PHP_ForbiddenNamesSniff extends PHPCompatibility_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...
26
{
27
28
    /**
29
     * A list of keywords that can not be used as function, class and namespace name or constant name.
30
     * Mentions since which version it's not allowed.
31
     *
32
     * @var array(string => string)
33
     */
34
    protected $invalidNames = array(
35
        'abstract' => '5.0',
36
        'and' => 'all',
37
        'array' => 'all',
38
        'as' => 'all',
39
        'break' => 'all',
40
        'callable' => '5.4',
41
        'case' => 'all',
42
        'catch' => '5.0',
43
        'class' => 'all',
44
        'clone' => '5.0',
45
        'const' => 'all',
46
        'continue' => 'all',
47
        'declare' => 'all',
48
        'default' => 'all',
49
        'do' => 'all',
50
        'else' => 'all',
51
        'elseif' => 'all',
52
        'enddeclare' => 'all',
53
        'endfor' => 'all',
54
        'endforeach' => 'all',
55
        'endif' => 'all',
56
        'endswitch' => 'all',
57
        'endwhile' => 'all',
58
        'extends' => 'all',
59
        'final' => '5.0',
60
        'finally' => '5.5',
61
        'for' => 'all',
62
        'foreach' => 'all',
63
        'function' => 'all',
64
        'global' => 'all',
65
        'goto' => '5.3',
66
        'if' => 'all',
67
        'implements' => '5.0',
68
        'interface' => '5.0',
69
        'instanceof' => '5.0',
70
        'insteadof' => '5.4',
71
        'namespace' => '5.3',
72
        'new' => 'all',
73
        'or' => 'all',
74
        'private' => '5.0',
75
        'protected' => '5.0',
76
        'public' => '5.0',
77
        'static' => 'all',
78
        'switch' => 'all',
79
        'throw' => '5.0',
80
        'trait' => '5.4',
81
        'try' => '5.0',
82
        'use' => 'all',
83
        'var' => 'all',
84
        'while' => 'all',
85
        'xor' => 'all',
86
        '__class__' => 'all',
87
        '__dir__' => '5.3',
88
        '__file__' => 'all',
89
        '__function__' => 'all',
90
        '__method__' => 'all',
91
        '__namespace__' => '5.3',
92
        'bool' => '7.0',
93
        'int' => '7.0',
94
        'float' => '7.0',
95
        'string' => '7.0',
96
        'null' => '7.0',
97
        'true' => '7.0',
98
        'false' => '7.0',
99
        'resource' => '7.0',
100
        'object' => '7.0',
101
        'mixed' => '7.0',
102
        'numeric' => '7.0'
103
    );
104
105
    /**
106
     * A list of keywords that can follow use statements.
107
     *
108
     * @var array(string => string)
109
     */
110
    protected $validUseNames = array(
111
        'const'    => true,
112
        'function' => true,
113
    );
114
115
    /**
116
     * Whether PHPCS 1.x is used or not.
117
     *
118
     * @var bool
119
     */
120
    protected $isLowPHPCS = false;
121
122
    /**
123
     * targetedTokens
124
     *
125
     * @var array
126
     */
127
    protected $targetedTokens = array(
128
        T_CLASS,
129
        T_FUNCTION,
130
        T_NAMESPACE,
131
        T_STRING,
132
        T_CONST,
133
        T_USE,
134
        T_AS,
135
        T_EXTENDS,
136
        T_TRAIT,
137
        T_INTERFACE,
138
    );
139
140
    /**
141
     * Returns an array of tokens this test wants to listen for.
142
     *
143
     * @return array
144
     */
145
    public function register()
146
    {
147
        $this->isLowPHPCS = version_compare(PHP_CodeSniffer::VERSION, '2.0', '<');
148
149
        $tokens = $this->targetedTokens;
150
        if (defined('T_ANON_CLASS')) {
151
            $tokens[] = constant('T_ANON_CLASS');
152
        }
153
        return $tokens;
154
    }//end register()
155
156
    /**
157
     * Processes this test, when one of its tokens is encountered.
158
     *
159
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
160
     * @param int                  $stackPtr  The position of the current token in the
161
     *                                        stack passed in $tokens.
162
     *
163
     * @return void
164
     */
165
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
166
    {
167
        $tokens = $phpcsFile->getTokens();
168
169
        /**
170
         * We distinguish between the class, function and namespace names vs the define statements.
171
         */
172
        if ($tokens[$stackPtr]['type'] === 'T_STRING') {
173
            $this->processString($phpcsFile, $stackPtr, $tokens);
174
        } else {
175
            $this->processNonString($phpcsFile, $stackPtr, $tokens);
176
        }
177
    }
178
179
    /**
180
     * Processes this test, when one of its tokens is encountered.
181
     *
182
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
183
     * @param int                  $stackPtr  The position of the current token in the
184
     *                                        stack passed in $tokens.
185
     * @param array                $tokens    The stack of tokens that make up
186
     *                                        the file.
187
     *
188
     * @return void
189
     */
190
    public function processNonString(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $tokens)
191
    {
192
        $nextNonEmpty = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true);
193
        if ($nextNonEmpty === false) {
194
            return;
195
        }
196
197
        /*
198
         * PHP 5.6 allows for use const and use function, but only if followed by the function/constant name.
199
         * - `use function HelloWorld` => move to the next token (HelloWorld) to verify.
200
         * - `use const HelloWorld` => move to the next token (HelloWorld) to verify.
201
         */
202
        if ($tokens[$stackPtr]['type'] === 'T_USE'
203
            && isset($this->validUseNames[strtolower($tokens[$nextNonEmpty]['content'])]) === true
204
            && $this->supportsAbove('5.6')
205
        ) {
206
            $maybeUseNext = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true, null, true);
207
            if ($maybeUseNext !== false && $this->isEndOfUseStatement($tokens[$maybeUseNext]) === false) {
208
                // Prevent duplicate messages: `const` is T_CONST in PHPCS 1.x and T_STRING in PHPCS 2.x.
209
                if ($this->isLowPHPCS === true) {
210
                    return;
211
                }
212
                $nextNonEmpty = $maybeUseNext;
213
            }
214
        }
215
216
        /*
217
         * Deal with visibility modifiers.
218
         * - `use HelloWorld { sayHello as protected; }` => valid, bow out.
219
         * - `use HelloWorld { sayHello as private myPrivateHello; }` => move to the next token to verify.
220
         */
221
        else if ($tokens[$stackPtr]['type'] === 'T_AS'
222
            && in_array($tokens[$nextNonEmpty]['code'], PHP_CodeSniffer_Tokens::$scopeModifiers, true) === true
223
            && $this->inUseScope($phpcsFile, $stackPtr) === true
224
        ) {
225
            $maybeUseNext = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true, null, true);
226
            if ($maybeUseNext === false || $this->isEndOfUseStatement($tokens[$maybeUseNext]) === true) {
227
                return;
228
            }
229
230
            $nextNonEmpty = $maybeUseNext;
231
        }
232
233
        $nextContentLc = strtolower($tokens[$nextNonEmpty]['content']);
234
        if (isset($this->invalidNames[$nextContentLc]) === false) {
235
            return;
236
        }
237
238
        // Deal with anonymous classes.
239
        $prevNonEmpty = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true);
240
        if ($prevNonEmpty !== false
241
            && $tokens[$prevNonEmpty]['type'] === 'T_NEW'
242
            && $tokens[$stackPtr]['type'] === 'T_ANON_CLASS'
243
        ) {
244
            return;
245
        }
246
247 View Code Duplication
        if ($this->supportsAbove($this->invalidNames[$nextContentLc])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
248
            $error = "Function name, class name, namespace name or constant name can not be reserved keyword '%s' (since version %s)";
249
            $data  = array(
250
                $tokens[$nextNonEmpty]['content'],
251
                $this->invalidNames[$nextContentLc],
252
            );
253
            $phpcsFile->addError($error, $stackPtr, 'Found', $data);
254
        }
255
256
    }//end processNonString()
257
258
    /**
259
     * Processes this test, when one of its tokens is encountered.
260
     *
261
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
262
     * @param int                  $stackPtr  The position of the current token in the
263
     *                                        stack passed in $tokens.
264
     * @param array                $tokens    The stack of tokens that make up
265
     *                                        the file.
266
     *
267
     * @return void
268
     */
269
    public function processString(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $tokens)
270
    {
271
        $tokenContentLc = strtolower($tokens[$stackPtr]['content']);
272
273
        // Special case for 5.3 where we want to find usage of traits, but
274
        // trait is not a token.
275
        if ($tokenContentLc === 'trait') {
276
            $this->processNonString($phpcsFile, $stackPtr, $tokens);
277
            return;
278
        }
279
280
        // Look for any define/defined tokens (both T_STRING ones, blame Tokenizer).
281
        if ($tokenContentLc !== 'define' && $tokenContentLc !== 'defined') {
282
            return;
283
        }
284
285
        // Retrieve the define(d) constant name.
286
        $firstParam = $this->getFunctionCallParameter($phpcsFile, $stackPtr, 1);
287
        if ($firstParam === false) {
288
            return;
289
        }
290
291
        $defineName = strtolower($firstParam['raw']);
292
        $defineName = $this->stripQuotes($defineName);
293
294 View Code Duplication
        if (isset($this->invalidNames[$defineName]) && $this->supportsAbove($this->invalidNames[$defineName])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
295
            $error = "Function name, class name, namespace name or constant name can not be reserved keyword '%s' (since PHP version %s)";
296
            $data  = array(
297
                $defineName,
298
                $this->invalidNames[$defineName],
299
            );
300
            $phpcsFile->addError($error, $stackPtr, 'Found', $data);
301
        }
302
    }//end processString()
303
304
305
    /**
306
     * Check if the current token code is for a token which can be considered
307
     * the end of a (partial) use statement.
308
     *
309
     * @param int $token The current token information.
310
     *
311
     * @return bool
312
     */
313
    protected function isEndOfUseStatement($token)
314
    {
315
        return in_array($token['code'], array(T_CLOSE_CURLY_BRACKET, T_SEMICOLON, T_COMMA), true);
316
    }
317
}//end class
318