Completed
Push — feature/fix-102-107-directory-... ( 7ca35c...ddd086 )
by Juliette
01:50
created

process()   D

Complexity

Conditions 16
Paths 31

Size

Total Lines 82
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 82
rs 4.9422
c 0
b 0
f 0
cc 16
eloc 33
nc 31
nop 2

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_NewKeywordsSniff.
4
 *
5
 * @category  PHP
6
 * @package   PHPCompatibility
7
 * @author    Wim Godden <[email protected]>
8
 * @copyright 2013 Cu.be Solutions bvba
9
 */
10
11
/**
12
 * PHPCompatibility_Sniffs_PHP_NewKeywordsSniff.
13
 *
14
 * @category  PHP
15
 * @package   PHPCompatibility
16
 * @author    Wim Godden <[email protected]>
17
 * @copyright 2013 Cu.be Solutions bvba
18
 */
19
class PHPCompatibility_Sniffs_PHP_NewKeywordsSniff extends PHPCompatibility_AbstractNewFeatureSniff
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...
20
{
21
22
    /**
23
     * A list of new keywords, not present in older versions.
24
     *
25
     * The array lists : version number with false (not present) or true (present).
26
     * If's sufficient to list the last version which did not contain the keyword.
27
     *
28
     * Description will be used as part of the error message.
29
     * Condition is the name of a callback method within this class or the parent class
30
     * which checks whether the token complies with a certain condition.
31
     * The callback function will be passed the $phpcsFile and the $stackPtr.
32
     * The callback function should return `true` if the condition is met and the
33
     * error should *not* be thrown.
34
     *
35
     * @var array(string => array(string => int|string|null))
36
     */
37
    protected $newKeywords = array(
38
        'T_HALT_COMPILER' => array(
39
            '5.0' => false,
40
            '5.1' => true,
41
            'description' => '"__halt_compiler" keyword',
42
        ),
43
        'T_CONST' => array(
44
            '5.2' => false,
45
            '5.3' => true,
46
            'description' => '"const" keyword',
47
            'condition' => 'isClassConstant', // Keyword is only new when not in class context.
48
        ),
49
        'T_CALLABLE' => array(
50
            '5.3' => false,
51
            '5.4' => true,
52
            'description' => '"callable" keyword',
53
            'content' => 'callable',
54
        ),
55
        'T_DIR' => array(
56
            '5.2' => false,
57
            '5.3' => true,
58
            'description' => '__DIR__ magic constant',
59
            'content' => '__DIR__',
60
        ),
61
        'T_GOTO' => array(
62
            '5.2' => false,
63
            '5.3' => true,
64
            'description' => '"goto" keyword',
65
            'content' => 'goto',
66
        ),
67
        'T_INSTEADOF' => array(
68
            '5.3' => false,
69
            '5.4' => true,
70
            'description' => '"insteadof" keyword (for traits)',
71
            'content' => 'insteadof',
72
        ),
73
        'T_NAMESPACE' => array(
74
            '5.2' => false,
75
            '5.3' => true,
76
            'description' => '"namespace" keyword',
77
            'content' => 'namespace',
78
        ),
79
        'T_NS_C' => array(
80
            '5.2' => false,
81
            '5.3' => true,
82
            'description' => '__NAMESPACE__ magic constant',
83
            'content' => '__NAMESPACE__',
84
        ),
85
        'T_USE' => array(
86
            '5.2' => false,
87
            '5.3' => true,
88
            'description' => '"use" keyword (for traits/namespaces/anonymous functions)',
89
        ),
90
        'T_TRAIT' => array(
91
            '5.3' => false,
92
            '5.4' => true,
93
            'description' => '"trait" keyword',
94
            'content' => 'trait',
95
        ),
96
        'T_TRAIT_C' => array(
97
            '5.3' => false,
98
            '5.4' => true,
99
            'description' => '__TRAIT__ magic constant',
100
            'content' => '__TRAIT__',
101
        ),
102
        // The specifics for distinguishing between 'yield' and 'yield from' are dealt
103
        // with in the translation logic.
104
        // This token has to be placed above the `T_YIELD` token in this array to allow for this.
105
        'T_YIELD_FROM' => array(
106
            '5.6' => false,
107
            '7.0' => true,
108
            'description' => '"yield from" keyword (for generators)',
109
            'content' => 'yield',
110
        ),
111
        'T_YIELD' => array(
112
            '5.4' => false,
113
            '5.5' => true,
114
            'description' => '"yield" keyword (for generators)',
115
            'content' => 'yield',
116
        ),
117
        'T_FINALLY' => array(
118
            '5.4' => false,
119
            '5.5' => true,
120
            'description' => '"finally" keyword (in exception handling)',
121
            'content' => 'finally',
122
        ),
123
    );
124
125
    /**
126
     * Translation table for T_STRING tokens.
127
     *
128
     * Will be set up from the register() method.
129
     *
130
     * @var array(string => string)
131
     */
132
    protected $translateContentToToken = array();
133
134
135
    /**
136
     * Returns an array of tokens this test wants to listen for.
137
     *
138
     * @return array
139
     */
140
    public function register()
141
    {
142
        $tokens    = array();
143
        $translate = array();
144
        foreach ($this->newKeywords as $token => $versions) {
145
            if (defined($token)) {
146
                $tokens[] = constant($token);
147
            }
148
            if (isset($versions['content'])) {
149
                $translate[$versions['content']] = $token;
150
            }
151
        }
152
153
        /*
154
         * Deal with tokens not recognized by the PHP version the sniffer is run
155
         * under and (not correctly) compensated for by PHPCS.
156
         */
157
        if (empty($translate) === false) {
158
            $this->translateContentToToken = $translate;
159
            $tokens[] = T_STRING;
160
        }
161
162
        return $tokens;
163
164
    }//end register()
165
166
167
    /**
168
     * Processes this test, when one of its tokens is encountered.
169
     *
170
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
171
     * @param int                  $stackPtr  The position of the current token in
172
     *                                        the stack passed in $tokens.
173
     *
174
     * @return void
175
     */
176
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
177
    {
178
        $tokens    = $phpcsFile->getTokens();
179
        $tokenType = $tokens[$stackPtr]['type'];
180
181
        // Allow for dealing with multi-token keywords, like "yield from".
182
        $end = $stackPtr;
183
184
        // Translate T_STRING token if necessary.
185
        if ($tokens[$stackPtr]['type'] === 'T_STRING') {
186
            $content = $tokens[$stackPtr]['content'];
187
            if (isset($this->translateContentToToken[$content]) === false) {
188
                // Not one of the tokens we're looking for.
189
                return;
190
            }
191
192
            $tokenType = $this->translateContentToToken[$content];
193
        }
194
195
        /*
196
         * Special case: distinguish between `yield` and `yield from`.
197
         *
198
         * PHPCS currently (at least up to v 3.0.1) does not backfill for the
199
         * `yield` nor the `yield from` keywords.
200
         * See: https://github.com/squizlabs/PHP_CodeSniffer/issues/1524
201
         *
202
         * In PHP < 5.5, both `yield` as well as `from` are tokenized as T_STRING.
203
         * In PHP 5.5 - 5.6, `yield` is tokenized as T_YIELD and `from` as T_STRING,
204
         * but the `T_YIELD_FROM` token *is* defined in PHP.
205
         * In PHP 7.0+ both are tokenized as their respective token, however,
206
         * a multi-line "yield from" is tokenized as two tokens.
207
         */
208
        if ($tokenType === 'T_YIELD') {
209
            $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($end + 1), null, true);
210
            if ($tokens[$nextToken]['code'] === T_STRING
211
                && $tokens[$nextToken]['content'] === 'from'
212
            ) {
213
                $tokenType = 'T_YIELD_FROM';
214
                $end       = $nextToken;
215
            }
216
            unset($nextToken);
217
        }
218
219
        if ($tokenType === 'T_YIELD_FROM' && $tokens[($stackPtr - 1)]['type'] === 'T_YIELD_FROM') {
220
            // Multi-line "yield from", no need to report it twice.
221
            return;
222
        }
223
224
        if (isset($this->newKeywords[$tokenType]) === false) {
225
            return;
226
        }
227
228
        $nextToken = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($end + 1), null, true);
229
        $prevToken = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true);
230
231
232
        // Skip attempts to use keywords as functions or class names - the former
233
        // will be reported by ForbiddenNamesAsInvokedFunctionsSniff, whilst the
234
        // latter will be (partially) reported by the ForbiddenNames sniff.
235
        // Either type will result in false-positives when targetting lower versions
236
        // of PHP where the name was not reserved, unless we explicitly check for
237
        // them.
238
        if (($nextToken === false
239
                || $tokens[$nextToken]['type'] !== 'T_OPEN_PARENTHESIS')
240
            && ($prevToken === false
241
                || $tokens[$prevToken]['type'] !== 'T_CLASS'
242
                || $tokens[$prevToken]['type'] !== 'T_INTERFACE')
243
        ) {
244
            // Skip based on token scope condition.
245
            if (isset($this->newKeywords[$tokenType]['condition'])
246
                && call_user_func(array($this, $this->newKeywords[$tokenType]['condition']), $phpcsFile, $stackPtr) === true
247
            ) {
248
                return;
249
            }
250
251
            $itemInfo = array(
252
                'name'   => $tokenType,
253
            );
254
            $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
255
        }
256
257
    }//end process()
258
259
260
    /**
261
     * Get the relevant sub-array for a specific item from a multi-dimensional array.
262
     *
263
     * @param array $itemInfo Base information about the item.
264
     *
265
     * @return array Version and other information about the item.
266
     */
267
    public function getItemArray(array $itemInfo)
268
    {
269
        return $this->newKeywords[$itemInfo['name']];
270
    }
271
272
273
    /**
274
     * Get an array of the non-PHP-version array keys used in a sub-array.
275
     *
276
     * @return array
277
     */
278
    protected function getNonVersionArrayKeys()
279
    {
280
        return array(
281
            'description',
282
            'condition',
283
            'content',
284
        );
285
    }
286
287
288
    /**
289
     * Retrieve the relevant detail (version) information for use in an error message.
290
     *
291
     * @param array $itemArray Version and other information about the item.
292
     * @param array $itemInfo  Base information about the item.
293
     *
294
     * @return array
295
     */
296
    public function getErrorInfo(array $itemArray, array $itemInfo)
297
    {
298
        $errorInfo = parent::getErrorInfo($itemArray, $itemInfo);
299
        $errorInfo['description'] = $itemArray['description'];
300
301
        return $errorInfo;
302
303
    }
304
305
306
    /**
307
     * Allow for concrete child classes to filter the error data before it's passed to PHPCS.
308
     *
309
     * @param array $data      The error data array which was created.
310
     * @param array $itemInfo  Base information about the item this error message applied to.
311
     * @param array $errorInfo Detail information about an item this error message applied to.
312
     *
313
     * @return array
314
     */
315
    protected function filterErrorData(array $data, array $itemInfo, array $errorInfo)
316
    {
317
        $data[0] = $errorInfo['description'];
318
        return $data;
319
    }
320
321
322
}//end class
323