Completed
Pull Request — develop (#1492)
by Zack
33:18 queued 12:17
created
php-compatibility/PHPCompatibility/Sniffs/Keywords/NewKeywordsSniff.php 1 patch
Indentation   +323 added lines, -323 removed lines patch added patch discarded remove patch
@@ -25,196 +25,196 @@  discard block
 block discarded – undo
25 25
 class NewKeywordsSniff extends AbstractNewFeatureSniff
26 26
 {
27 27
 
28
-    /**
29
-     * A list of new keywords, not present in older versions.
30
-     *
31
-     * The array lists : version number with false (not present) or true (present).
32
-     * If's sufficient to list the last version which did not contain the keyword.
33
-     *
34
-     * Description will be used as part of the error message.
35
-     * Condition is the name of a callback method within this class or the parent class
36
-     * which checks whether the token complies with a certain condition.
37
-     * The callback function will be passed the $phpcsFile and the $stackPtr.
38
-     * The callback function should return `true` if the condition is met and the
39
-     * error should *not* be thrown.
40
-     *
41
-     * @var array(string => array(string => int|string|null))
42
-     */
43
-    protected $newKeywords = array(
44
-        'T_HALT_COMPILER' => array(
45
-            '5.0'         => false,
46
-            '5.1'         => true,
47
-            'description' => '"__halt_compiler" keyword',
48
-        ),
49
-        'T_CONST' => array(
50
-            '5.2'         => false,
51
-            '5.3'         => true,
52
-            'description' => '"const" keyword',
53
-            'condition'   => 'isClassConstant', // Keyword is only new when not in class context.
54
-        ),
55
-        'T_CALLABLE' => array(
56
-            '5.3'         => false,
57
-            '5.4'         => true,
58
-            'description' => '"callable" keyword',
59
-            'content'     => 'callable',
60
-        ),
61
-        'T_DIR' => array(
62
-            '5.2'         => false,
63
-            '5.3'         => true,
64
-            'description' => '__DIR__ magic constant',
65
-            'content'     => '__DIR__',
66
-        ),
67
-        'T_GOTO' => array(
68
-            '5.2'         => false,
69
-            '5.3'         => true,
70
-            'description' => '"goto" keyword',
71
-            'content'     => 'goto',
72
-        ),
73
-        'T_INSTEADOF' => array(
74
-            '5.3'         => false,
75
-            '5.4'         => true,
76
-            'description' => '"insteadof" keyword (for traits)',
77
-            'content'     => 'insteadof',
78
-        ),
79
-        'T_NAMESPACE' => array(
80
-            '5.2'         => false,
81
-            '5.3'         => true,
82
-            'description' => '"namespace" keyword',
83
-            'content'     => 'namespace',
84
-        ),
85
-        'T_NS_C' => array(
86
-            '5.2'         => false,
87
-            '5.3'         => true,
88
-            'description' => '__NAMESPACE__ magic constant',
89
-            'content'     => '__NAMESPACE__',
90
-        ),
91
-        'T_USE' => array(
92
-            '5.2'         => false,
93
-            '5.3'         => true,
94
-            'description' => '"use" keyword (for traits/namespaces/anonymous functions)',
95
-        ),
96
-        'T_START_NOWDOC' => array(
97
-            '5.2'         => false,
98
-            '5.3'         => true,
99
-            'description' => 'nowdoc functionality',
100
-        ),
101
-        'T_END_NOWDOC' => array(
102
-            '5.2'         => false,
103
-            '5.3'         => true,
104
-            'description' => 'nowdoc functionality',
105
-        ),
106
-        'T_START_HEREDOC' => array(
107
-            '5.2'         => false,
108
-            '5.3'         => true,
109
-            'description' => '(Double) quoted Heredoc identifier',
110
-            'condition'   => 'isNotQuoted', // Heredoc is only new with quoted identifier.
111
-        ),
112
-        'T_TRAIT' => array(
113
-            '5.3'         => false,
114
-            '5.4'         => true,
115
-            'description' => '"trait" keyword',
116
-            'content'     => 'trait',
117
-        ),
118
-        'T_TRAIT_C' => array(
119
-            '5.3'         => false,
120
-            '5.4'         => true,
121
-            'description' => '__TRAIT__ magic constant',
122
-            'content'     => '__TRAIT__',
123
-        ),
124
-        // The specifics for distinguishing between 'yield' and 'yield from' are dealt
125
-        // with in the translation logic.
126
-        // This token has to be placed above the `T_YIELD` token in this array to allow for this.
127
-        'T_YIELD_FROM' => array(
128
-            '5.6'         => false,
129
-            '7.0'         => true,
130
-            'description' => '"yield from" keyword (for generators)',
131
-            'content'     => 'yield',
132
-        ),
133
-        'T_YIELD' => array(
134
-            '5.4'         => false,
135
-            '5.5'         => true,
136
-            'description' => '"yield" keyword (for generators)',
137
-            'content'     => 'yield',
138
-        ),
139
-        'T_FINALLY' => array(
140
-            '5.4'         => false,
141
-            '5.5'         => true,
142
-            'description' => '"finally" keyword (in exception handling)',
143
-            'content'     => 'finally',
144
-        ),
145
-    );
146
-
147
-    /**
148
-     * Translation table for T_STRING tokens.
149
-     *
150
-     * Will be set up from the register() method.
151
-     *
152
-     * @var array(string => string)
153
-     */
154
-    protected $translateContentToToken = array();
155
-
156
-
157
-    /**
158
-     * Returns an array of tokens this test wants to listen for.
159
-     *
160
-     * @return array
161
-     */
162
-    public function register()
163
-    {
164
-        $tokens    = array();
165
-        $translate = array();
166
-        foreach ($this->newKeywords as $token => $versions) {
167
-            if (\defined($token)) {
168
-                $tokens[] = constant($token);
169
-            }
170
-            if (isset($versions['content'])) {
171
-                $translate[strtolower($versions['content'])] = $token;
172
-            }
173
-        }
174
-
175
-        /*
28
+	/**
29
+	 * A list of new keywords, not present in older versions.
30
+	 *
31
+	 * The array lists : version number with false (not present) or true (present).
32
+	 * If's sufficient to list the last version which did not contain the keyword.
33
+	 *
34
+	 * Description will be used as part of the error message.
35
+	 * Condition is the name of a callback method within this class or the parent class
36
+	 * which checks whether the token complies with a certain condition.
37
+	 * The callback function will be passed the $phpcsFile and the $stackPtr.
38
+	 * The callback function should return `true` if the condition is met and the
39
+	 * error should *not* be thrown.
40
+	 *
41
+	 * @var array(string => array(string => int|string|null))
42
+	 */
43
+	protected $newKeywords = array(
44
+		'T_HALT_COMPILER' => array(
45
+			'5.0'         => false,
46
+			'5.1'         => true,
47
+			'description' => '"__halt_compiler" keyword',
48
+		),
49
+		'T_CONST' => array(
50
+			'5.2'         => false,
51
+			'5.3'         => true,
52
+			'description' => '"const" keyword',
53
+			'condition'   => 'isClassConstant', // Keyword is only new when not in class context.
54
+		),
55
+		'T_CALLABLE' => array(
56
+			'5.3'         => false,
57
+			'5.4'         => true,
58
+			'description' => '"callable" keyword',
59
+			'content'     => 'callable',
60
+		),
61
+		'T_DIR' => array(
62
+			'5.2'         => false,
63
+			'5.3'         => true,
64
+			'description' => '__DIR__ magic constant',
65
+			'content'     => '__DIR__',
66
+		),
67
+		'T_GOTO' => array(
68
+			'5.2'         => false,
69
+			'5.3'         => true,
70
+			'description' => '"goto" keyword',
71
+			'content'     => 'goto',
72
+		),
73
+		'T_INSTEADOF' => array(
74
+			'5.3'         => false,
75
+			'5.4'         => true,
76
+			'description' => '"insteadof" keyword (for traits)',
77
+			'content'     => 'insteadof',
78
+		),
79
+		'T_NAMESPACE' => array(
80
+			'5.2'         => false,
81
+			'5.3'         => true,
82
+			'description' => '"namespace" keyword',
83
+			'content'     => 'namespace',
84
+		),
85
+		'T_NS_C' => array(
86
+			'5.2'         => false,
87
+			'5.3'         => true,
88
+			'description' => '__NAMESPACE__ magic constant',
89
+			'content'     => '__NAMESPACE__',
90
+		),
91
+		'T_USE' => array(
92
+			'5.2'         => false,
93
+			'5.3'         => true,
94
+			'description' => '"use" keyword (for traits/namespaces/anonymous functions)',
95
+		),
96
+		'T_START_NOWDOC' => array(
97
+			'5.2'         => false,
98
+			'5.3'         => true,
99
+			'description' => 'nowdoc functionality',
100
+		),
101
+		'T_END_NOWDOC' => array(
102
+			'5.2'         => false,
103
+			'5.3'         => true,
104
+			'description' => 'nowdoc functionality',
105
+		),
106
+		'T_START_HEREDOC' => array(
107
+			'5.2'         => false,
108
+			'5.3'         => true,
109
+			'description' => '(Double) quoted Heredoc identifier',
110
+			'condition'   => 'isNotQuoted', // Heredoc is only new with quoted identifier.
111
+		),
112
+		'T_TRAIT' => array(
113
+			'5.3'         => false,
114
+			'5.4'         => true,
115
+			'description' => '"trait" keyword',
116
+			'content'     => 'trait',
117
+		),
118
+		'T_TRAIT_C' => array(
119
+			'5.3'         => false,
120
+			'5.4'         => true,
121
+			'description' => '__TRAIT__ magic constant',
122
+			'content'     => '__TRAIT__',
123
+		),
124
+		// The specifics for distinguishing between 'yield' and 'yield from' are dealt
125
+		// with in the translation logic.
126
+		// This token has to be placed above the `T_YIELD` token in this array to allow for this.
127
+		'T_YIELD_FROM' => array(
128
+			'5.6'         => false,
129
+			'7.0'         => true,
130
+			'description' => '"yield from" keyword (for generators)',
131
+			'content'     => 'yield',
132
+		),
133
+		'T_YIELD' => array(
134
+			'5.4'         => false,
135
+			'5.5'         => true,
136
+			'description' => '"yield" keyword (for generators)',
137
+			'content'     => 'yield',
138
+		),
139
+		'T_FINALLY' => array(
140
+			'5.4'         => false,
141
+			'5.5'         => true,
142
+			'description' => '"finally" keyword (in exception handling)',
143
+			'content'     => 'finally',
144
+		),
145
+	);
146
+
147
+	/**
148
+	 * Translation table for T_STRING tokens.
149
+	 *
150
+	 * Will be set up from the register() method.
151
+	 *
152
+	 * @var array(string => string)
153
+	 */
154
+	protected $translateContentToToken = array();
155
+
156
+
157
+	/**
158
+	 * Returns an array of tokens this test wants to listen for.
159
+	 *
160
+	 * @return array
161
+	 */
162
+	public function register()
163
+	{
164
+		$tokens    = array();
165
+		$translate = array();
166
+		foreach ($this->newKeywords as $token => $versions) {
167
+			if (\defined($token)) {
168
+				$tokens[] = constant($token);
169
+			}
170
+			if (isset($versions['content'])) {
171
+				$translate[strtolower($versions['content'])] = $token;
172
+			}
173
+		}
174
+
175
+		/*
176 176
          * Deal with tokens not recognized by the PHP version the sniffer is run
177 177
          * under and (not correctly) compensated for by PHPCS.
178 178
          */
179
-        if (empty($translate) === false) {
180
-            $this->translateContentToToken = $translate;
181
-            $tokens[]                      = \T_STRING;
182
-        }
183
-
184
-        return $tokens;
185
-    }
186
-
187
-
188
-    /**
189
-     * Processes this test, when one of its tokens is encountered.
190
-     *
191
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
192
-     * @param int                   $stackPtr  The position of the current token in
193
-     *                                         the stack passed in $tokens.
194
-     *
195
-     * @return void
196
-     */
197
-    public function process(File $phpcsFile, $stackPtr)
198
-    {
199
-        $tokens    = $phpcsFile->getTokens();
200
-        $tokenType = $tokens[$stackPtr]['type'];
201
-
202
-        // Allow for dealing with multi-token keywords, like "yield from".
203
-        $end = $stackPtr;
204
-
205
-        // Translate T_STRING token if necessary.
206
-        if ($tokens[$stackPtr]['type'] === 'T_STRING') {
207
-            $content = strtolower($tokens[$stackPtr]['content']);
208
-
209
-            if (isset($this->translateContentToToken[$content]) === false) {
210
-                // Not one of the tokens we're looking for.
211
-                return;
212
-            }
213
-
214
-            $tokenType = $this->translateContentToToken[$content];
215
-        }
216
-
217
-        /*
179
+		if (empty($translate) === false) {
180
+			$this->translateContentToToken = $translate;
181
+			$tokens[]                      = \T_STRING;
182
+		}
183
+
184
+		return $tokens;
185
+	}
186
+
187
+
188
+	/**
189
+	 * Processes this test, when one of its tokens is encountered.
190
+	 *
191
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
192
+	 * @param int                   $stackPtr  The position of the current token in
193
+	 *                                         the stack passed in $tokens.
194
+	 *
195
+	 * @return void
196
+	 */
197
+	public function process(File $phpcsFile, $stackPtr)
198
+	{
199
+		$tokens    = $phpcsFile->getTokens();
200
+		$tokenType = $tokens[$stackPtr]['type'];
201
+
202
+		// Allow for dealing with multi-token keywords, like "yield from".
203
+		$end = $stackPtr;
204
+
205
+		// Translate T_STRING token if necessary.
206
+		if ($tokens[$stackPtr]['type'] === 'T_STRING') {
207
+			$content = strtolower($tokens[$stackPtr]['content']);
208
+
209
+			if (isset($this->translateContentToToken[$content]) === false) {
210
+				// Not one of the tokens we're looking for.
211
+				return;
212
+			}
213
+
214
+			$tokenType = $this->translateContentToToken[$content];
215
+		}
216
+
217
+		/*
218 218
          * Special case: distinguish between `yield` and `yield from`.
219 219
          *
220 220
          * PHPCS currently (at least up to v 3.0.1) does not backfill for the
@@ -227,140 +227,140 @@  discard block
 block discarded – undo
227 227
          * In PHP 7.0+ both are tokenized as their respective token, however,
228 228
          * a multi-line "yield from" is tokenized as two tokens.
229 229
          */
230
-        if ($tokenType === 'T_YIELD') {
231
-            $nextToken = $phpcsFile->findNext(\T_WHITESPACE, ($end + 1), null, true);
232
-            if ($tokens[$nextToken]['code'] === \T_STRING
233
-                && $tokens[$nextToken]['content'] === 'from'
234
-            ) {
235
-                $tokenType = 'T_YIELD_FROM';
236
-                $end       = $nextToken;
237
-            }
238
-            unset($nextToken);
239
-        }
240
-
241
-        if ($tokenType === 'T_YIELD_FROM' && $tokens[($stackPtr - 1)]['type'] === 'T_YIELD_FROM') {
242
-            // Multi-line "yield from", no need to report it twice.
243
-            return;
244
-        }
245
-
246
-        if (isset($this->newKeywords[$tokenType]) === false) {
247
-            return;
248
-        }
249
-
250
-        $nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true);
251
-        $prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
252
-
253
-        if ($prevToken !== false
254
-            && ($tokens[$prevToken]['code'] === \T_DOUBLE_COLON
255
-            || $tokens[$prevToken]['code'] === \T_OBJECT_OPERATOR)
256
-        ) {
257
-            // Class property of the same name as one of the keywords. Ignore.
258
-            return;
259
-        }
260
-
261
-        // Skip attempts to use keywords as functions or class names - the former
262
-        // will be reported by ForbiddenNamesAsInvokedFunctionsSniff, whilst the
263
-        // latter will be (partially) reported by the ForbiddenNames sniff.
264
-        // Either type will result in false-positives when targetting lower versions
265
-        // of PHP where the name was not reserved, unless we explicitly check for
266
-        // them.
267
-        if (($nextToken === false
268
-                || $tokens[$nextToken]['type'] !== 'T_OPEN_PARENTHESIS')
269
-            && ($prevToken === false
270
-                || $tokens[$prevToken]['type'] !== 'T_CLASS'
271
-                || $tokens[$prevToken]['type'] !== 'T_INTERFACE')
272
-        ) {
273
-            // Skip based on token scope condition.
274
-            if (isset($this->newKeywords[$tokenType]['condition'])
275
-                && \call_user_func(array($this, $this->newKeywords[$tokenType]['condition']), $phpcsFile, $stackPtr) === true
276
-            ) {
277
-                return;
278
-            }
279
-
280
-            $itemInfo = array(
281
-                'name'   => $tokenType,
282
-            );
283
-            $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
284
-        }
285
-    }
286
-
287
-
288
-    /**
289
-     * Get the relevant sub-array for a specific item from a multi-dimensional array.
290
-     *
291
-     * @param array $itemInfo Base information about the item.
292
-     *
293
-     * @return array Version and other information about the item.
294
-     */
295
-    public function getItemArray(array $itemInfo)
296
-    {
297
-        return $this->newKeywords[$itemInfo['name']];
298
-    }
299
-
300
-
301
-    /**
302
-     * Get an array of the non-PHP-version array keys used in a sub-array.
303
-     *
304
-     * @return array
305
-     */
306
-    protected function getNonVersionArrayKeys()
307
-    {
308
-        return array(
309
-            'description',
310
-            'condition',
311
-            'content',
312
-        );
313
-    }
314
-
315
-
316
-    /**
317
-     * Retrieve the relevant detail (version) information for use in an error message.
318
-     *
319
-     * @param array $itemArray Version and other information about the item.
320
-     * @param array $itemInfo  Base information about the item.
321
-     *
322
-     * @return array
323
-     */
324
-    public function getErrorInfo(array $itemArray, array $itemInfo)
325
-    {
326
-        $errorInfo                = parent::getErrorInfo($itemArray, $itemInfo);
327
-        $errorInfo['description'] = $itemArray['description'];
328
-
329
-        return $errorInfo;
330
-    }
331
-
332
-
333
-    /**
334
-     * Allow for concrete child classes to filter the error data before it's passed to PHPCS.
335
-     *
336
-     * @param array $data      The error data array which was created.
337
-     * @param array $itemInfo  Base information about the item this error message applies to.
338
-     * @param array $errorInfo Detail information about an item this error message applies to.
339
-     *
340
-     * @return array
341
-     */
342
-    protected function filterErrorData(array $data, array $itemInfo, array $errorInfo)
343
-    {
344
-        $data[0] = $errorInfo['description'];
345
-        return $data;
346
-    }
347
-
348
-
349
-    /**
350
-     * Callback for the quoted heredoc identifier condition.
351
-     *
352
-     * A double quoted identifier will have the opening quote on position 3
353
-     * in the string: `<<<"ID"`.
354
-     *
355
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
356
-     * @param int                   $stackPtr  The position of the current token in
357
-     *                                         the stack passed in $tokens.
358
-     *
359
-     * @return bool
360
-     */
361
-    public function isNotQuoted(File $phpcsFile, $stackPtr)
362
-    {
363
-        $tokens = $phpcsFile->getTokens();
364
-        return ($tokens[$stackPtr]['content'][3] !== '"');
365
-    }
230
+		if ($tokenType === 'T_YIELD') {
231
+			$nextToken = $phpcsFile->findNext(\T_WHITESPACE, ($end + 1), null, true);
232
+			if ($tokens[$nextToken]['code'] === \T_STRING
233
+				&& $tokens[$nextToken]['content'] === 'from'
234
+			) {
235
+				$tokenType = 'T_YIELD_FROM';
236
+				$end       = $nextToken;
237
+			}
238
+			unset($nextToken);
239
+		}
240
+
241
+		if ($tokenType === 'T_YIELD_FROM' && $tokens[($stackPtr - 1)]['type'] === 'T_YIELD_FROM') {
242
+			// Multi-line "yield from", no need to report it twice.
243
+			return;
244
+		}
245
+
246
+		if (isset($this->newKeywords[$tokenType]) === false) {
247
+			return;
248
+		}
249
+
250
+		$nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true);
251
+		$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
252
+
253
+		if ($prevToken !== false
254
+			&& ($tokens[$prevToken]['code'] === \T_DOUBLE_COLON
255
+			|| $tokens[$prevToken]['code'] === \T_OBJECT_OPERATOR)
256
+		) {
257
+			// Class property of the same name as one of the keywords. Ignore.
258
+			return;
259
+		}
260
+
261
+		// Skip attempts to use keywords as functions or class names - the former
262
+		// will be reported by ForbiddenNamesAsInvokedFunctionsSniff, whilst the
263
+		// latter will be (partially) reported by the ForbiddenNames sniff.
264
+		// Either type will result in false-positives when targetting lower versions
265
+		// of PHP where the name was not reserved, unless we explicitly check for
266
+		// them.
267
+		if (($nextToken === false
268
+				|| $tokens[$nextToken]['type'] !== 'T_OPEN_PARENTHESIS')
269
+			&& ($prevToken === false
270
+				|| $tokens[$prevToken]['type'] !== 'T_CLASS'
271
+				|| $tokens[$prevToken]['type'] !== 'T_INTERFACE')
272
+		) {
273
+			// Skip based on token scope condition.
274
+			if (isset($this->newKeywords[$tokenType]['condition'])
275
+				&& \call_user_func(array($this, $this->newKeywords[$tokenType]['condition']), $phpcsFile, $stackPtr) === true
276
+			) {
277
+				return;
278
+			}
279
+
280
+			$itemInfo = array(
281
+				'name'   => $tokenType,
282
+			);
283
+			$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
284
+		}
285
+	}
286
+
287
+
288
+	/**
289
+	 * Get the relevant sub-array for a specific item from a multi-dimensional array.
290
+	 *
291
+	 * @param array $itemInfo Base information about the item.
292
+	 *
293
+	 * @return array Version and other information about the item.
294
+	 */
295
+	public function getItemArray(array $itemInfo)
296
+	{
297
+		return $this->newKeywords[$itemInfo['name']];
298
+	}
299
+
300
+
301
+	/**
302
+	 * Get an array of the non-PHP-version array keys used in a sub-array.
303
+	 *
304
+	 * @return array
305
+	 */
306
+	protected function getNonVersionArrayKeys()
307
+	{
308
+		return array(
309
+			'description',
310
+			'condition',
311
+			'content',
312
+		);
313
+	}
314
+
315
+
316
+	/**
317
+	 * Retrieve the relevant detail (version) information for use in an error message.
318
+	 *
319
+	 * @param array $itemArray Version and other information about the item.
320
+	 * @param array $itemInfo  Base information about the item.
321
+	 *
322
+	 * @return array
323
+	 */
324
+	public function getErrorInfo(array $itemArray, array $itemInfo)
325
+	{
326
+		$errorInfo                = parent::getErrorInfo($itemArray, $itemInfo);
327
+		$errorInfo['description'] = $itemArray['description'];
328
+
329
+		return $errorInfo;
330
+	}
331
+
332
+
333
+	/**
334
+	 * Allow for concrete child classes to filter the error data before it's passed to PHPCS.
335
+	 *
336
+	 * @param array $data      The error data array which was created.
337
+	 * @param array $itemInfo  Base information about the item this error message applies to.
338
+	 * @param array $errorInfo Detail information about an item this error message applies to.
339
+	 *
340
+	 * @return array
341
+	 */
342
+	protected function filterErrorData(array $data, array $itemInfo, array $errorInfo)
343
+	{
344
+		$data[0] = $errorInfo['description'];
345
+		return $data;
346
+	}
347
+
348
+
349
+	/**
350
+	 * Callback for the quoted heredoc identifier condition.
351
+	 *
352
+	 * A double quoted identifier will have the opening quote on position 3
353
+	 * in the string: `<<<"ID"`.
354
+	 *
355
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
356
+	 * @param int                   $stackPtr  The position of the current token in
357
+	 *                                         the stack passed in $tokens.
358
+	 *
359
+	 * @return bool
360
+	 */
361
+	public function isNotQuoted(File $phpcsFile, $stackPtr)
362
+	{
363
+		$tokens = $phpcsFile->getTokens();
364
+		return ($tokens[$stackPtr]['content'][3] !== '"');
365
+	}
366 366
 }
Please login to merge, or discard this patch.
PHPCompatibility/Sniffs/Keywords/ForbiddenNamesAsInvokedFunctionsSniff.php 1 patch
Indentation   +140 added lines, -140 removed lines patch added patch discarded remove patch
@@ -27,155 +27,155 @@
 block discarded – undo
27 27
 class ForbiddenNamesAsInvokedFunctionsSniff extends Sniff
28 28
 {
29 29
 
30
-    /**
31
-     * List of tokens to register.
32
-     *
33
-     * @var array
34
-     */
35
-    protected $targetedTokens = array(
36
-        \T_ABSTRACT   => '5.0',
37
-        \T_CALLABLE   => '5.4',
38
-        \T_CATCH      => '5.0',
39
-        \T_FINAL      => '5.0',
40
-        \T_FINALLY    => '5.5',
41
-        \T_GOTO       => '5.3',
42
-        \T_IMPLEMENTS => '5.0',
43
-        \T_INTERFACE  => '5.0',
44
-        \T_INSTANCEOF => '5.0',
45
-        \T_INSTEADOF  => '5.4',
46
-        \T_NAMESPACE  => '5.3',
47
-        \T_PRIVATE    => '5.0',
48
-        \T_PROTECTED  => '5.0',
49
-        \T_PUBLIC     => '5.0',
50
-        \T_TRAIT      => '5.4',
51
-        \T_TRY        => '5.0',
52
-
53
-    );
54
-
55
-    /**
56
-     * T_STRING keywords to recognize as targetted tokens.
57
-     *
58
-     * Compatibility for PHP versions where the keyword is not yet recognized
59
-     * as its own token and for PHPCS versions which change the token to
60
-     * T_STRING when used in a method call.
61
-     *
62
-     * @var array
63
-     */
64
-    protected $targetedStringTokens = array(
65
-        'abstract'   => '5.0',
66
-        'callable'   => '5.4',
67
-        'catch'      => '5.0',
68
-        'final'      => '5.0',
69
-        'finally'    => '5.5',
70
-        'goto'       => '5.3',
71
-        'implements' => '5.0',
72
-        'interface'  => '5.0',
73
-        'instanceof' => '5.0',
74
-        'insteadof'  => '5.4',
75
-        'namespace'  => '5.3',
76
-        'private'    => '5.0',
77
-        'protected'  => '5.0',
78
-        'public'     => '5.0',
79
-        'trait'      => '5.4',
80
-        'try'        => '5.0',
81
-    );
82
-
83
-    /**
84
-     * Returns an array of tokens this test wants to listen for.
85
-     *
86
-     * @return array
87
-     */
88
-    public function register()
89
-    {
90
-        $tokens   = array_keys($this->targetedTokens);
91
-        $tokens[] = \T_STRING;
92
-
93
-        return $tokens;
94
-    }
95
-
96
-    /**
97
-     * Processes this test, when one of its tokens is encountered.
98
-     *
99
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
100
-     * @param int                   $stackPtr  The position of the current token in the
101
-     *                                         stack passed in $tokens.
102
-     *
103
-     * @return void
104
-     */
105
-    public function process(File $phpcsFile, $stackPtr)
106
-    {
107
-        $tokens         = $phpcsFile->getTokens();
108
-        $tokenCode      = $tokens[$stackPtr]['code'];
109
-        $tokenContentLc = strtolower($tokens[$stackPtr]['content']);
110
-        $isString       = false;
111
-
112
-        /*
30
+	/**
31
+	 * List of tokens to register.
32
+	 *
33
+	 * @var array
34
+	 */
35
+	protected $targetedTokens = array(
36
+		\T_ABSTRACT   => '5.0',
37
+		\T_CALLABLE   => '5.4',
38
+		\T_CATCH      => '5.0',
39
+		\T_FINAL      => '5.0',
40
+		\T_FINALLY    => '5.5',
41
+		\T_GOTO       => '5.3',
42
+		\T_IMPLEMENTS => '5.0',
43
+		\T_INTERFACE  => '5.0',
44
+		\T_INSTANCEOF => '5.0',
45
+		\T_INSTEADOF  => '5.4',
46
+		\T_NAMESPACE  => '5.3',
47
+		\T_PRIVATE    => '5.0',
48
+		\T_PROTECTED  => '5.0',
49
+		\T_PUBLIC     => '5.0',
50
+		\T_TRAIT      => '5.4',
51
+		\T_TRY        => '5.0',
52
+
53
+	);
54
+
55
+	/**
56
+	 * T_STRING keywords to recognize as targetted tokens.
57
+	 *
58
+	 * Compatibility for PHP versions where the keyword is not yet recognized
59
+	 * as its own token and for PHPCS versions which change the token to
60
+	 * T_STRING when used in a method call.
61
+	 *
62
+	 * @var array
63
+	 */
64
+	protected $targetedStringTokens = array(
65
+		'abstract'   => '5.0',
66
+		'callable'   => '5.4',
67
+		'catch'      => '5.0',
68
+		'final'      => '5.0',
69
+		'finally'    => '5.5',
70
+		'goto'       => '5.3',
71
+		'implements' => '5.0',
72
+		'interface'  => '5.0',
73
+		'instanceof' => '5.0',
74
+		'insteadof'  => '5.4',
75
+		'namespace'  => '5.3',
76
+		'private'    => '5.0',
77
+		'protected'  => '5.0',
78
+		'public'     => '5.0',
79
+		'trait'      => '5.4',
80
+		'try'        => '5.0',
81
+	);
82
+
83
+	/**
84
+	 * Returns an array of tokens this test wants to listen for.
85
+	 *
86
+	 * @return array
87
+	 */
88
+	public function register()
89
+	{
90
+		$tokens   = array_keys($this->targetedTokens);
91
+		$tokens[] = \T_STRING;
92
+
93
+		return $tokens;
94
+	}
95
+
96
+	/**
97
+	 * Processes this test, when one of its tokens is encountered.
98
+	 *
99
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
100
+	 * @param int                   $stackPtr  The position of the current token in the
101
+	 *                                         stack passed in $tokens.
102
+	 *
103
+	 * @return void
104
+	 */
105
+	public function process(File $phpcsFile, $stackPtr)
106
+	{
107
+		$tokens         = $phpcsFile->getTokens();
108
+		$tokenCode      = $tokens[$stackPtr]['code'];
109
+		$tokenContentLc = strtolower($tokens[$stackPtr]['content']);
110
+		$isString       = false;
111
+
112
+		/*
113 113
          * For string tokens we only care if the string is a reserved word used
114 114
          * as a function. This only happens in older versions of PHP where the
115 115
          * token doesn't exist yet for that keyword or in later versions when the
116 116
          * token is used in a method invocation.
117 117
          */
118
-        if ($tokenCode === \T_STRING
119
-            && (isset($this->targetedStringTokens[$tokenContentLc]) === false)
120
-        ) {
121
-            return;
122
-        }
123
-
124
-        if ($tokenCode === \T_STRING) {
125
-            $isString = true;
126
-        }
127
-
128
-        // Make sure this is a function call.
129
-        $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
130
-        if ($next === false || $tokens[$next]['code'] !== \T_OPEN_PARENTHESIS) {
131
-            // Not a function call.
132
-            return;
133
-        }
134
-
135
-        // This sniff isn't concerned about function declarations.
136
-        $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
137
-        if ($prev !== false && $tokens[$prev]['code'] === \T_FUNCTION) {
138
-            return;
139
-        }
140
-
141
-        /*
118
+		if ($tokenCode === \T_STRING
119
+			&& (isset($this->targetedStringTokens[$tokenContentLc]) === false)
120
+		) {
121
+			return;
122
+		}
123
+
124
+		if ($tokenCode === \T_STRING) {
125
+			$isString = true;
126
+		}
127
+
128
+		// Make sure this is a function call.
129
+		$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
130
+		if ($next === false || $tokens[$next]['code'] !== \T_OPEN_PARENTHESIS) {
131
+			// Not a function call.
132
+			return;
133
+		}
134
+
135
+		// This sniff isn't concerned about function declarations.
136
+		$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
137
+		if ($prev !== false && $tokens[$prev]['code'] === \T_FUNCTION) {
138
+			return;
139
+		}
140
+
141
+		/*
142 142
          * Deal with PHP 7 relaxing the rules.
143 143
          * "As of PHP 7.0.0 these keywords are allowed as property, constant, and method names
144 144
          * of classes, interfaces and traits...", i.e. they can be invoked as a method call.
145 145
          *
146 146
          * Only needed for those keywords which we sniff out via T_STRING.
147 147
          */
148
-        if (($tokens[$prev]['code'] === \T_OBJECT_OPERATOR || $tokens[$prev]['code'] === \T_DOUBLE_COLON)
149
-            && $this->supportsBelow('5.6') === false
150
-        ) {
151
-            return;
152
-        }
153
-
154
-        // For the word catch, it is valid to have an open parenthesis
155
-        // after it, but only if it is preceded by a right curly brace.
156
-        if ($tokenCode === \T_CATCH) {
157
-            if ($prev !== false && $tokens[$prev]['code'] === \T_CLOSE_CURLY_BRACKET) {
158
-                // Ok, it's fine.
159
-                return;
160
-            }
161
-        }
162
-
163
-        if ($isString === true) {
164
-            $version = $this->targetedStringTokens[$tokenContentLc];
165
-        } else {
166
-            $version = $this->targetedTokens[$tokenCode];
167
-        }
168
-
169
-        if ($this->supportsAbove($version)) {
170
-            $error     = "'%s' is a reserved keyword introduced in PHP version %s and cannot be invoked as a function (%s)";
171
-            $errorCode = $this->stringToErrorCode($tokenContentLc) . 'Found';
172
-            $data      = array(
173
-                $tokenContentLc,
174
-                $version,
175
-                $tokens[$stackPtr]['type'],
176
-            );
177
-
178
-            $phpcsFile->addError($error, $stackPtr, $errorCode, $data);
179
-        }
180
-    }
148
+		if (($tokens[$prev]['code'] === \T_OBJECT_OPERATOR || $tokens[$prev]['code'] === \T_DOUBLE_COLON)
149
+			&& $this->supportsBelow('5.6') === false
150
+		) {
151
+			return;
152
+		}
153
+
154
+		// For the word catch, it is valid to have an open parenthesis
155
+		// after it, but only if it is preceded by a right curly brace.
156
+		if ($tokenCode === \T_CATCH) {
157
+			if ($prev !== false && $tokens[$prev]['code'] === \T_CLOSE_CURLY_BRACKET) {
158
+				// Ok, it's fine.
159
+				return;
160
+			}
161
+		}
162
+
163
+		if ($isString === true) {
164
+			$version = $this->targetedStringTokens[$tokenContentLc];
165
+		} else {
166
+			$version = $this->targetedTokens[$tokenCode];
167
+		}
168
+
169
+		if ($this->supportsAbove($version)) {
170
+			$error     = "'%s' is a reserved keyword introduced in PHP version %s and cannot be invoked as a function (%s)";
171
+			$errorCode = $this->stringToErrorCode($tokenContentLc) . 'Found';
172
+			$data      = array(
173
+				$tokenContentLc,
174
+				$version,
175
+				$tokens[$stackPtr]['type'],
176
+			);
177
+
178
+			$phpcsFile->addError($error, $stackPtr, $errorCode, $data);
179
+		}
180
+	}
181 181
 }
Please login to merge, or discard this patch.
PHPCompatibility/Sniffs/Keywords/ForbiddenNamesAsDeclaredSniff.php 1 patch
Indentation   +213 added lines, -213 removed lines patch added patch discarded remove patch
@@ -32,217 +32,217 @@
 block discarded – undo
32 32
 class ForbiddenNamesAsDeclaredSniff extends Sniff
33 33
 {
34 34
 
35
-    /**
36
-     * List of tokens which can not be used as class, interface, trait names or as part of a namespace.
37
-     *
38
-     * @var array
39
-     */
40
-    protected $forbiddenTokens = array(
41
-        \T_NULL  => '7.0',
42
-        \T_TRUE  => '7.0',
43
-        \T_FALSE => '7.0',
44
-    );
45
-
46
-    /**
47
-     * T_STRING keywords to recognize as forbidden names.
48
-     *
49
-     * @var array
50
-     */
51
-    protected $forbiddenNames = array(
52
-        'null'     => '7.0',
53
-        'true'     => '7.0',
54
-        'false'    => '7.0',
55
-        'bool'     => '7.0',
56
-        'int'      => '7.0',
57
-        'float'    => '7.0',
58
-        'string'   => '7.0',
59
-        'iterable' => '7.1',
60
-        'void'     => '7.1',
61
-        'object'   => '7.2',
62
-    );
63
-
64
-    /**
65
-     * T_STRING keywords to recognize as soft reserved names.
66
-     *
67
-     * Using any of these keywords to name a class, interface, trait or namespace
68
-     * is highly discouraged since they may be used in future versions of PHP.
69
-     *
70
-     * @var array
71
-     */
72
-    protected $softReservedNames = array(
73
-        'resource' => '7.0',
74
-        'object'   => '7.0',
75
-        'mixed'    => '7.0',
76
-        'numeric'  => '7.0',
77
-    );
78
-
79
-    /**
80
-     * Combined list of the two lists above.
81
-     *
82
-     * Used for quick check whether or not something is a reserved
83
-     * word.
84
-     * Set from the `register()` method.
85
-     *
86
-     * @var array
87
-     */
88
-    private $allForbiddenNames = array();
89
-
90
-
91
-    /**
92
-     * Returns an array of tokens this test wants to listen for.
93
-     *
94
-     * @return array
95
-     */
96
-    public function register()
97
-    {
98
-        // Do the list merge only once.
99
-        $this->allForbiddenNames = array_merge($this->forbiddenNames, $this->softReservedNames);
100
-
101
-        $targets = array(
102
-            \T_CLASS,
103
-            \T_INTERFACE,
104
-            \T_TRAIT,
105
-            \T_NAMESPACE,
106
-            \T_STRING, // Compat for PHPCS < 2.4.0 and PHP < 5.3.
107
-        );
108
-
109
-        return $targets;
110
-    }
111
-
112
-
113
-    /**
114
-     * Processes this test, when one of its tokens is encountered.
115
-     *
116
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
117
-     * @param int                   $stackPtr  The position of the current token in the
118
-     *                                         stack passed in $tokens.
119
-     *
120
-     * @return void
121
-     */
122
-    public function process(File $phpcsFile, $stackPtr)
123
-    {
124
-        if ($this->supportsAbove('7.0') === false) {
125
-            return;
126
-        }
127
-
128
-        $tokens         = $phpcsFile->getTokens();
129
-        $tokenCode      = $tokens[$stackPtr]['code'];
130
-        $tokenType      = $tokens[$stackPtr]['type'];
131
-        $tokenContentLc = strtolower($tokens[$stackPtr]['content']);
132
-
133
-        // For string tokens we only care about 'trait' as that is the only one
134
-        // which may not be correctly recognized as it's own token.
135
-        // This only happens in older versions of PHP where the token doesn't exist yet as a keyword.
136
-        if ($tokenCode === \T_STRING && $tokenContentLc !== 'trait') {
137
-            return;
138
-        }
139
-
140
-        if (\in_array($tokenType, array('T_CLASS', 'T_INTERFACE', 'T_TRAIT'), true)) {
141
-            // Check for the declared name being a name which is not tokenized as T_STRING.
142
-            $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
143
-            if ($nextNonEmpty !== false && isset($this->forbiddenTokens[$tokens[$nextNonEmpty]['code']]) === true) {
144
-                $name = $tokens[$nextNonEmpty]['content'];
145
-            } else {
146
-                // Get the declared name if it's a T_STRING.
147
-                $name = $phpcsFile->getDeclarationName($stackPtr);
148
-            }
149
-            unset($nextNonEmpty);
150
-
151
-            if (isset($name) === false || \is_string($name) === false || $name === '') {
152
-                return;
153
-            }
154
-
155
-            $nameLc = strtolower($name);
156
-            if (isset($this->allForbiddenNames[$nameLc]) === false) {
157
-                return;
158
-            }
159
-
160
-        } elseif ($tokenCode === \T_NAMESPACE) {
161
-            $namespaceName = $this->getDeclaredNamespaceName($phpcsFile, $stackPtr);
162
-
163
-            if ($namespaceName === false || $namespaceName === '') {
164
-                return;
165
-            }
166
-
167
-            $namespaceParts = explode('\\', $namespaceName);
168
-            foreach ($namespaceParts as $namespacePart) {
169
-                $partLc = strtolower($namespacePart);
170
-                if (isset($this->allForbiddenNames[$partLc]) === true) {
171
-                    $name   = $namespacePart;
172
-                    $nameLc = $partLc;
173
-                    break;
174
-                }
175
-            }
176
-        } elseif ($tokenCode === \T_STRING) {
177
-            // Traits which are not yet tokenized as T_TRAIT.
178
-            $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
179
-            if ($nextNonEmpty === false) {
180
-                return;
181
-            }
182
-
183
-            $nextNonEmptyCode = $tokens[$nextNonEmpty]['code'];
184
-
185
-            if ($nextNonEmptyCode !== \T_STRING && isset($this->forbiddenTokens[$nextNonEmptyCode]) === true) {
186
-                $name   = $tokens[$nextNonEmpty]['content'];
187
-                $nameLc = strtolower($tokens[$nextNonEmpty]['content']);
188
-            } elseif ($nextNonEmptyCode === \T_STRING) {
189
-                $endOfStatement = $phpcsFile->findNext(array(\T_SEMICOLON, \T_OPEN_CURLY_BRACKET), ($stackPtr + 1));
190
-                if ($endOfStatement === false) {
191
-                    return;
192
-                }
193
-
194
-                do {
195
-                    $nextNonEmptyLc = strtolower($tokens[$nextNonEmpty]['content']);
196
-
197
-                    if (isset($this->allForbiddenNames[$nextNonEmptyLc]) === true) {
198
-                        $name   = $tokens[$nextNonEmpty]['content'];
199
-                        $nameLc = $nextNonEmptyLc;
200
-                        break;
201
-                    }
202
-
203
-                    $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), $endOfStatement, true);
204
-                } while ($nextNonEmpty !== false);
205
-            }
206
-            unset($nextNonEmptyCode, $nextNonEmptyLc, $endOfStatement);
207
-        }
208
-
209
-        if (isset($name, $nameLc) === false) {
210
-            return;
211
-        }
212
-
213
-        // Still here, so this is one of the reserved words.
214
-        // Build up the error message.
215
-        $error     = "'%s' is a";
216
-        $isError   = null;
217
-        $errorCode = $this->stringToErrorCode($nameLc) . 'Found';
218
-        $data      = array(
219
-            $nameLc,
220
-        );
221
-
222
-        if (isset($this->softReservedNames[$nameLc]) === true
223
-            && $this->supportsAbove($this->softReservedNames[$nameLc]) === true
224
-        ) {
225
-            $error  .= ' soft reserved keyword as of PHP version %s';
226
-            $isError = false;
227
-            $data[]  = $this->softReservedNames[$nameLc];
228
-        }
229
-
230
-        if (isset($this->forbiddenNames[$nameLc]) === true
231
-            && $this->supportsAbove($this->forbiddenNames[$nameLc]) === true
232
-        ) {
233
-            if (isset($isError) === true) {
234
-                $error .= ' and a';
235
-            }
236
-            $error  .= ' reserved keyword as of PHP version %s';
237
-            $isError = true;
238
-            $data[]  = $this->forbiddenNames[$nameLc];
239
-        }
240
-
241
-        if (isset($isError) === true) {
242
-            $error .= ' and should not be used to name a class, interface or trait or as part of a namespace (%s)';
243
-            $data[] = $tokens[$stackPtr]['type'];
244
-
245
-            $this->addMessage($phpcsFile, $error, $stackPtr, $isError, $errorCode, $data);
246
-        }
247
-    }
35
+	/**
36
+	 * List of tokens which can not be used as class, interface, trait names or as part of a namespace.
37
+	 *
38
+	 * @var array
39
+	 */
40
+	protected $forbiddenTokens = array(
41
+		\T_NULL  => '7.0',
42
+		\T_TRUE  => '7.0',
43
+		\T_FALSE => '7.0',
44
+	);
45
+
46
+	/**
47
+	 * T_STRING keywords to recognize as forbidden names.
48
+	 *
49
+	 * @var array
50
+	 */
51
+	protected $forbiddenNames = array(
52
+		'null'     => '7.0',
53
+		'true'     => '7.0',
54
+		'false'    => '7.0',
55
+		'bool'     => '7.0',
56
+		'int'      => '7.0',
57
+		'float'    => '7.0',
58
+		'string'   => '7.0',
59
+		'iterable' => '7.1',
60
+		'void'     => '7.1',
61
+		'object'   => '7.2',
62
+	);
63
+
64
+	/**
65
+	 * T_STRING keywords to recognize as soft reserved names.
66
+	 *
67
+	 * Using any of these keywords to name a class, interface, trait or namespace
68
+	 * is highly discouraged since they may be used in future versions of PHP.
69
+	 *
70
+	 * @var array
71
+	 */
72
+	protected $softReservedNames = array(
73
+		'resource' => '7.0',
74
+		'object'   => '7.0',
75
+		'mixed'    => '7.0',
76
+		'numeric'  => '7.0',
77
+	);
78
+
79
+	/**
80
+	 * Combined list of the two lists above.
81
+	 *
82
+	 * Used for quick check whether or not something is a reserved
83
+	 * word.
84
+	 * Set from the `register()` method.
85
+	 *
86
+	 * @var array
87
+	 */
88
+	private $allForbiddenNames = array();
89
+
90
+
91
+	/**
92
+	 * Returns an array of tokens this test wants to listen for.
93
+	 *
94
+	 * @return array
95
+	 */
96
+	public function register()
97
+	{
98
+		// Do the list merge only once.
99
+		$this->allForbiddenNames = array_merge($this->forbiddenNames, $this->softReservedNames);
100
+
101
+		$targets = array(
102
+			\T_CLASS,
103
+			\T_INTERFACE,
104
+			\T_TRAIT,
105
+			\T_NAMESPACE,
106
+			\T_STRING, // Compat for PHPCS < 2.4.0 and PHP < 5.3.
107
+		);
108
+
109
+		return $targets;
110
+	}
111
+
112
+
113
+	/**
114
+	 * Processes this test, when one of its tokens is encountered.
115
+	 *
116
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
117
+	 * @param int                   $stackPtr  The position of the current token in the
118
+	 *                                         stack passed in $tokens.
119
+	 *
120
+	 * @return void
121
+	 */
122
+	public function process(File $phpcsFile, $stackPtr)
123
+	{
124
+		if ($this->supportsAbove('7.0') === false) {
125
+			return;
126
+		}
127
+
128
+		$tokens         = $phpcsFile->getTokens();
129
+		$tokenCode      = $tokens[$stackPtr]['code'];
130
+		$tokenType      = $tokens[$stackPtr]['type'];
131
+		$tokenContentLc = strtolower($tokens[$stackPtr]['content']);
132
+
133
+		// For string tokens we only care about 'trait' as that is the only one
134
+		// which may not be correctly recognized as it's own token.
135
+		// This only happens in older versions of PHP where the token doesn't exist yet as a keyword.
136
+		if ($tokenCode === \T_STRING && $tokenContentLc !== 'trait') {
137
+			return;
138
+		}
139
+
140
+		if (\in_array($tokenType, array('T_CLASS', 'T_INTERFACE', 'T_TRAIT'), true)) {
141
+			// Check for the declared name being a name which is not tokenized as T_STRING.
142
+			$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
143
+			if ($nextNonEmpty !== false && isset($this->forbiddenTokens[$tokens[$nextNonEmpty]['code']]) === true) {
144
+				$name = $tokens[$nextNonEmpty]['content'];
145
+			} else {
146
+				// Get the declared name if it's a T_STRING.
147
+				$name = $phpcsFile->getDeclarationName($stackPtr);
148
+			}
149
+			unset($nextNonEmpty);
150
+
151
+			if (isset($name) === false || \is_string($name) === false || $name === '') {
152
+				return;
153
+			}
154
+
155
+			$nameLc = strtolower($name);
156
+			if (isset($this->allForbiddenNames[$nameLc]) === false) {
157
+				return;
158
+			}
159
+
160
+		} elseif ($tokenCode === \T_NAMESPACE) {
161
+			$namespaceName = $this->getDeclaredNamespaceName($phpcsFile, $stackPtr);
162
+
163
+			if ($namespaceName === false || $namespaceName === '') {
164
+				return;
165
+			}
166
+
167
+			$namespaceParts = explode('\\', $namespaceName);
168
+			foreach ($namespaceParts as $namespacePart) {
169
+				$partLc = strtolower($namespacePart);
170
+				if (isset($this->allForbiddenNames[$partLc]) === true) {
171
+					$name   = $namespacePart;
172
+					$nameLc = $partLc;
173
+					break;
174
+				}
175
+			}
176
+		} elseif ($tokenCode === \T_STRING) {
177
+			// Traits which are not yet tokenized as T_TRAIT.
178
+			$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
179
+			if ($nextNonEmpty === false) {
180
+				return;
181
+			}
182
+
183
+			$nextNonEmptyCode = $tokens[$nextNonEmpty]['code'];
184
+
185
+			if ($nextNonEmptyCode !== \T_STRING && isset($this->forbiddenTokens[$nextNonEmptyCode]) === true) {
186
+				$name   = $tokens[$nextNonEmpty]['content'];
187
+				$nameLc = strtolower($tokens[$nextNonEmpty]['content']);
188
+			} elseif ($nextNonEmptyCode === \T_STRING) {
189
+				$endOfStatement = $phpcsFile->findNext(array(\T_SEMICOLON, \T_OPEN_CURLY_BRACKET), ($stackPtr + 1));
190
+				if ($endOfStatement === false) {
191
+					return;
192
+				}
193
+
194
+				do {
195
+					$nextNonEmptyLc = strtolower($tokens[$nextNonEmpty]['content']);
196
+
197
+					if (isset($this->allForbiddenNames[$nextNonEmptyLc]) === true) {
198
+						$name   = $tokens[$nextNonEmpty]['content'];
199
+						$nameLc = $nextNonEmptyLc;
200
+						break;
201
+					}
202
+
203
+					$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), $endOfStatement, true);
204
+				} while ($nextNonEmpty !== false);
205
+			}
206
+			unset($nextNonEmptyCode, $nextNonEmptyLc, $endOfStatement);
207
+		}
208
+
209
+		if (isset($name, $nameLc) === false) {
210
+			return;
211
+		}
212
+
213
+		// Still here, so this is one of the reserved words.
214
+		// Build up the error message.
215
+		$error     = "'%s' is a";
216
+		$isError   = null;
217
+		$errorCode = $this->stringToErrorCode($nameLc) . 'Found';
218
+		$data      = array(
219
+			$nameLc,
220
+		);
221
+
222
+		if (isset($this->softReservedNames[$nameLc]) === true
223
+			&& $this->supportsAbove($this->softReservedNames[$nameLc]) === true
224
+		) {
225
+			$error  .= ' soft reserved keyword as of PHP version %s';
226
+			$isError = false;
227
+			$data[]  = $this->softReservedNames[$nameLc];
228
+		}
229
+
230
+		if (isset($this->forbiddenNames[$nameLc]) === true
231
+			&& $this->supportsAbove($this->forbiddenNames[$nameLc]) === true
232
+		) {
233
+			if (isset($isError) === true) {
234
+				$error .= ' and a';
235
+			}
236
+			$error  .= ' reserved keyword as of PHP version %s';
237
+			$isError = true;
238
+			$data[]  = $this->forbiddenNames[$nameLc];
239
+		}
240
+
241
+		if (isset($isError) === true) {
242
+			$error .= ' and should not be used to name a class, interface or trait or as part of a namespace (%s)';
243
+			$data[] = $tokens[$stackPtr]['type'];
244
+
245
+			$this->addMessage($phpcsFile, $error, $stackPtr, $isError, $errorCode, $data);
246
+		}
247
+	}
248 248
 }
Please login to merge, or discard this patch.
PHPCompatibility/Sniffs/Keywords/CaseSensitiveKeywordsSniff.php 1 patch
Indentation   +38 added lines, -38 removed lines patch added patch discarded remove patch
@@ -29,45 +29,45 @@
 block discarded – undo
29 29
 class CaseSensitiveKeywordsSniff extends Sniff
30 30
 {
31 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(
40
-            \T_SELF,
41
-            \T_STATIC,
42
-            \T_PARENT,
43
-        );
44
-    }
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(
40
+			\T_SELF,
41
+			\T_STATIC,
42
+			\T_PARENT,
43
+		);
44
+	}
45 45
 
46
-    /**
47
-     * Processes this test, when one of its tokens is encountered.
48
-     *
49
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
50
-     * @param int                   $stackPtr  The position of the current token in the
51
-     *                                         stack passed in $tokens.
52
-     *
53
-     * @return void
54
-     */
55
-    public function process(File $phpcsFile, $stackPtr)
56
-    {
57
-        if ($this->supportsBelow('5.4') === false) {
58
-            return;
59
-        }
46
+	/**
47
+	 * Processes this test, when one of its tokens is encountered.
48
+	 *
49
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
50
+	 * @param int                   $stackPtr  The position of the current token in the
51
+	 *                                         stack passed in $tokens.
52
+	 *
53
+	 * @return void
54
+	 */
55
+	public function process(File $phpcsFile, $stackPtr)
56
+	{
57
+		if ($this->supportsBelow('5.4') === false) {
58
+			return;
59
+		}
60 60
 
61
-        $tokens         = $phpcsFile->getTokens();
62
-        $tokenContentLC = strtolower($tokens[$stackPtr]['content']);
61
+		$tokens         = $phpcsFile->getTokens();
62
+		$tokenContentLC = strtolower($tokens[$stackPtr]['content']);
63 63
 
64
-        if ($tokenContentLC !== $tokens[$stackPtr]['content']) {
65
-            $phpcsFile->addError(
66
-                'The keyword \'%s\' was treated in a case-sensitive fashion in certain cases in PHP 5.4 or earlier. Use the lowercase version for consistent support.',
67
-                $stackPtr,
68
-                'NonLowercaseFound',
69
-                array($tokenContentLC)
70
-            );
71
-        }
72
-    }
64
+		if ($tokenContentLC !== $tokens[$stackPtr]['content']) {
65
+			$phpcsFile->addError(
66
+				'The keyword \'%s\' was treated in a case-sensitive fashion in certain cases in PHP 5.4 or earlier. Use the lowercase version for consistent support.',
67
+				$stackPtr,
68
+				'NonLowercaseFound',
69
+				array($tokenContentLC)
70
+			);
71
+		}
72
+	}
73 73
 }
Please login to merge, or discard this patch.
php-compatibility/PHPCompatibility/Sniffs/Keywords/ForbiddenNamesSniff.php 1 patch
Indentation   +335 added lines, -335 removed lines patch added patch discarded remove patch
@@ -27,365 +27,365 @@
 block discarded – undo
27 27
 class ForbiddenNamesSniff extends Sniff
28 28
 {
29 29
 
30
-    /**
31
-     * A list of keywords that can not be used as function, class and namespace name or constant name.
32
-     * Mentions since which version it's not allowed.
33
-     *
34
-     * @var array(string => string)
35
-     */
36
-    protected $invalidNames = array(
37
-        'abstract'      => '5.0',
38
-        'and'           => 'all',
39
-        'array'         => 'all',
40
-        'as'            => 'all',
41
-        'break'         => 'all',
42
-        'callable'      => '5.4',
43
-        'case'          => 'all',
44
-        'catch'         => '5.0',
45
-        'class'         => 'all',
46
-        'clone'         => '5.0',
47
-        'const'         => 'all',
48
-        'continue'      => 'all',
49
-        'declare'       => 'all',
50
-        'default'       => 'all',
51
-        'do'            => 'all',
52
-        'else'          => 'all',
53
-        'elseif'        => 'all',
54
-        'enddeclare'    => 'all',
55
-        'endfor'        => 'all',
56
-        'endforeach'    => 'all',
57
-        'endif'         => 'all',
58
-        'endswitch'     => 'all',
59
-        'endwhile'      => 'all',
60
-        'extends'       => 'all',
61
-        'final'         => '5.0',
62
-        'finally'       => '5.5',
63
-        'for'           => 'all',
64
-        'foreach'       => 'all',
65
-        'function'      => 'all',
66
-        'global'        => 'all',
67
-        'goto'          => '5.3',
68
-        'if'            => 'all',
69
-        'implements'    => '5.0',
70
-        'interface'     => '5.0',
71
-        'instanceof'    => '5.0',
72
-        'insteadof'     => '5.4',
73
-        'namespace'     => '5.3',
74
-        'new'           => 'all',
75
-        'or'            => 'all',
76
-        'private'       => '5.0',
77
-        'protected'     => '5.0',
78
-        'public'        => '5.0',
79
-        'static'        => 'all',
80
-        'switch'        => 'all',
81
-        'throw'         => '5.0',
82
-        'trait'         => '5.4',
83
-        'try'           => '5.0',
84
-        'use'           => 'all',
85
-        'var'           => 'all',
86
-        'while'         => 'all',
87
-        'xor'           => 'all',
88
-        '__class__'     => 'all',
89
-        '__dir__'       => '5.3',
90
-        '__file__'      => 'all',
91
-        '__function__'  => 'all',
92
-        '__method__'    => 'all',
93
-        '__namespace__' => '5.3',
94
-    );
95
-
96
-    /**
97
-     * A list of keywords that can follow use statements.
98
-     *
99
-     * @var array(string => string)
100
-     */
101
-    protected $validUseNames = array(
102
-        'const'    => true,
103
-        'function' => true,
104
-    );
105
-
106
-    /**
107
-     * Scope modifiers and other keywords allowed in trait use statements.
108
-     *
109
-     * @var array
110
-     */
111
-    private $allowedModifiers = array();
112
-
113
-    /**
114
-     * Targeted tokens.
115
-     *
116
-     * @var array
117
-     */
118
-    protected $targetedTokens = array(
119
-        \T_CLASS,
120
-        \T_FUNCTION,
121
-        \T_NAMESPACE,
122
-        \T_STRING,
123
-        \T_CONST,
124
-        \T_USE,
125
-        \T_AS,
126
-        \T_EXTENDS,
127
-        \T_INTERFACE,
128
-        \T_TRAIT,
129
-    );
130
-
131
-    /**
132
-     * Returns an array of tokens this test wants to listen for.
133
-     *
134
-     * @return array
135
-     */
136
-    public function register()
137
-    {
138
-        $this->allowedModifiers           = Tokens::$scopeModifiers;
139
-        $this->allowedModifiers[\T_FINAL] = \T_FINAL;
140
-
141
-        $tokens = $this->targetedTokens;
142
-
143
-        if (\defined('T_ANON_CLASS')) {
144
-            $tokens[] = \T_ANON_CLASS;
145
-        }
146
-
147
-        return $tokens;
148
-    }
149
-
150
-    /**
151
-     * Processes this test, when one of its tokens is encountered.
152
-     *
153
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
154
-     * @param int                   $stackPtr  The position of the current token in the
155
-     *                                         stack passed in $tokens.
156
-     *
157
-     * @return void
158
-     */
159
-    public function process(File $phpcsFile, $stackPtr)
160
-    {
161
-        $tokens = $phpcsFile->getTokens();
162
-
163
-        /*
30
+	/**
31
+	 * A list of keywords that can not be used as function, class and namespace name or constant name.
32
+	 * Mentions since which version it's not allowed.
33
+	 *
34
+	 * @var array(string => string)
35
+	 */
36
+	protected $invalidNames = array(
37
+		'abstract'      => '5.0',
38
+		'and'           => 'all',
39
+		'array'         => 'all',
40
+		'as'            => 'all',
41
+		'break'         => 'all',
42
+		'callable'      => '5.4',
43
+		'case'          => 'all',
44
+		'catch'         => '5.0',
45
+		'class'         => 'all',
46
+		'clone'         => '5.0',
47
+		'const'         => 'all',
48
+		'continue'      => 'all',
49
+		'declare'       => 'all',
50
+		'default'       => 'all',
51
+		'do'            => 'all',
52
+		'else'          => 'all',
53
+		'elseif'        => 'all',
54
+		'enddeclare'    => 'all',
55
+		'endfor'        => 'all',
56
+		'endforeach'    => 'all',
57
+		'endif'         => 'all',
58
+		'endswitch'     => 'all',
59
+		'endwhile'      => 'all',
60
+		'extends'       => 'all',
61
+		'final'         => '5.0',
62
+		'finally'       => '5.5',
63
+		'for'           => 'all',
64
+		'foreach'       => 'all',
65
+		'function'      => 'all',
66
+		'global'        => 'all',
67
+		'goto'          => '5.3',
68
+		'if'            => 'all',
69
+		'implements'    => '5.0',
70
+		'interface'     => '5.0',
71
+		'instanceof'    => '5.0',
72
+		'insteadof'     => '5.4',
73
+		'namespace'     => '5.3',
74
+		'new'           => 'all',
75
+		'or'            => 'all',
76
+		'private'       => '5.0',
77
+		'protected'     => '5.0',
78
+		'public'        => '5.0',
79
+		'static'        => 'all',
80
+		'switch'        => 'all',
81
+		'throw'         => '5.0',
82
+		'trait'         => '5.4',
83
+		'try'           => '5.0',
84
+		'use'           => 'all',
85
+		'var'           => 'all',
86
+		'while'         => 'all',
87
+		'xor'           => 'all',
88
+		'__class__'     => 'all',
89
+		'__dir__'       => '5.3',
90
+		'__file__'      => 'all',
91
+		'__function__'  => 'all',
92
+		'__method__'    => 'all',
93
+		'__namespace__' => '5.3',
94
+	);
95
+
96
+	/**
97
+	 * A list of keywords that can follow use statements.
98
+	 *
99
+	 * @var array(string => string)
100
+	 */
101
+	protected $validUseNames = array(
102
+		'const'    => true,
103
+		'function' => true,
104
+	);
105
+
106
+	/**
107
+	 * Scope modifiers and other keywords allowed in trait use statements.
108
+	 *
109
+	 * @var array
110
+	 */
111
+	private $allowedModifiers = array();
112
+
113
+	/**
114
+	 * Targeted tokens.
115
+	 *
116
+	 * @var array
117
+	 */
118
+	protected $targetedTokens = array(
119
+		\T_CLASS,
120
+		\T_FUNCTION,
121
+		\T_NAMESPACE,
122
+		\T_STRING,
123
+		\T_CONST,
124
+		\T_USE,
125
+		\T_AS,
126
+		\T_EXTENDS,
127
+		\T_INTERFACE,
128
+		\T_TRAIT,
129
+	);
130
+
131
+	/**
132
+	 * Returns an array of tokens this test wants to listen for.
133
+	 *
134
+	 * @return array
135
+	 */
136
+	public function register()
137
+	{
138
+		$this->allowedModifiers           = Tokens::$scopeModifiers;
139
+		$this->allowedModifiers[\T_FINAL] = \T_FINAL;
140
+
141
+		$tokens = $this->targetedTokens;
142
+
143
+		if (\defined('T_ANON_CLASS')) {
144
+			$tokens[] = \T_ANON_CLASS;
145
+		}
146
+
147
+		return $tokens;
148
+	}
149
+
150
+	/**
151
+	 * Processes this test, when one of its tokens is encountered.
152
+	 *
153
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
154
+	 * @param int                   $stackPtr  The position of the current token in the
155
+	 *                                         stack passed in $tokens.
156
+	 *
157
+	 * @return void
158
+	 */
159
+	public function process(File $phpcsFile, $stackPtr)
160
+	{
161
+		$tokens = $phpcsFile->getTokens();
162
+
163
+		/*
164 164
          * We distinguish between the class, function and namespace names vs the define statements.
165 165
          */
166
-        if ($tokens[$stackPtr]['type'] === 'T_STRING') {
167
-            $this->processString($phpcsFile, $stackPtr, $tokens);
168
-        } else {
169
-            $this->processNonString($phpcsFile, $stackPtr, $tokens);
170
-        }
171
-    }
172
-
173
-    /**
174
-     * Processes this test, when one of its tokens is encountered.
175
-     *
176
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
177
-     * @param int                   $stackPtr  The position of the current token in the
178
-     *                                         stack passed in $tokens.
179
-     * @param array                 $tokens    The stack of tokens that make up
180
-     *                                         the file.
181
-     *
182
-     * @return void
183
-     */
184
-    public function processNonString(File $phpcsFile, $stackPtr, $tokens)
185
-    {
186
-        $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
187
-        if ($nextNonEmpty === false) {
188
-            return;
189
-        }
190
-
191
-        /*
166
+		if ($tokens[$stackPtr]['type'] === 'T_STRING') {
167
+			$this->processString($phpcsFile, $stackPtr, $tokens);
168
+		} else {
169
+			$this->processNonString($phpcsFile, $stackPtr, $tokens);
170
+		}
171
+	}
172
+
173
+	/**
174
+	 * Processes this test, when one of its tokens is encountered.
175
+	 *
176
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
177
+	 * @param int                   $stackPtr  The position of the current token in the
178
+	 *                                         stack passed in $tokens.
179
+	 * @param array                 $tokens    The stack of tokens that make up
180
+	 *                                         the file.
181
+	 *
182
+	 * @return void
183
+	 */
184
+	public function processNonString(File $phpcsFile, $stackPtr, $tokens)
185
+	{
186
+		$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
187
+		if ($nextNonEmpty === false) {
188
+			return;
189
+		}
190
+
191
+		/*
192 192
          * Deal with anonymous classes - `class` before a reserved keyword is sometimes
193 193
          * misidentified as `T_ANON_CLASS`.
194 194
          * In PHPCS < 2.3.4 these were tokenized as T_CLASS no matter what.
195 195
          */
196
-        if ($tokens[$stackPtr]['type'] === 'T_ANON_CLASS' || $tokens[$stackPtr]['type'] === 'T_CLASS') {
197
-            $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
198
-            if ($prevNonEmpty !== false && $tokens[$prevNonEmpty]['type'] === 'T_NEW') {
199
-                return;
200
-            }
201
-        }
202
-
203
-        /*
196
+		if ($tokens[$stackPtr]['type'] === 'T_ANON_CLASS' || $tokens[$stackPtr]['type'] === 'T_CLASS') {
197
+			$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
198
+			if ($prevNonEmpty !== false && $tokens[$prevNonEmpty]['type'] === 'T_NEW') {
199
+				return;
200
+			}
201
+		}
202
+
203
+		/*
204 204
          * PHP 5.6 allows for use const and use function, but only if followed by the function/constant name.
205 205
          * - `use function HelloWorld` => move to the next token (HelloWorld) to verify.
206 206
          * - `use const HelloWorld` => move to the next token (HelloWorld) to verify.
207 207
          */
208
-        elseif ($tokens[$stackPtr]['type'] === 'T_USE'
209
-            && isset($this->validUseNames[strtolower($tokens[$nextNonEmpty]['content'])]) === true
210
-        ) {
211
-            $maybeUseNext = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true, null, true);
212
-            if ($maybeUseNext !== false && $this->isEndOfUseStatement($tokens[$maybeUseNext]) === false) {
213
-                $nextNonEmpty = $maybeUseNext;
214
-            }
215
-        }
216
-
217
-        /*
208
+		elseif ($tokens[$stackPtr]['type'] === 'T_USE'
209
+			&& isset($this->validUseNames[strtolower($tokens[$nextNonEmpty]['content'])]) === true
210
+		) {
211
+			$maybeUseNext = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true, null, true);
212
+			if ($maybeUseNext !== false && $this->isEndOfUseStatement($tokens[$maybeUseNext]) === false) {
213
+				$nextNonEmpty = $maybeUseNext;
214
+			}
215
+		}
216
+
217
+		/*
218 218
          * Deal with visibility modifiers.
219 219
          * - `use HelloWorld { sayHello as protected; }` => valid, bow out.
220 220
          * - `use HelloWorld { sayHello as private myPrivateHello; }` => move to the next token to verify.
221 221
          */
222
-        elseif ($tokens[$stackPtr]['type'] === 'T_AS'
223
-            && isset($this->allowedModifiers[$tokens[$nextNonEmpty]['code']]) === true
224
-            && $phpcsFile->hasCondition($stackPtr, \T_USE) === true
225
-        ) {
226
-            $maybeUseNext = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true, null, true);
227
-            if ($maybeUseNext === false || $this->isEndOfUseStatement($tokens[$maybeUseNext]) === true) {
228
-                return;
229
-            }
230
-
231
-            $nextNonEmpty = $maybeUseNext;
232
-        }
233
-
234
-        /*
222
+		elseif ($tokens[$stackPtr]['type'] === 'T_AS'
223
+			&& isset($this->allowedModifiers[$tokens[$nextNonEmpty]['code']]) === true
224
+			&& $phpcsFile->hasCondition($stackPtr, \T_USE) === true
225
+		) {
226
+			$maybeUseNext = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true, null, true);
227
+			if ($maybeUseNext === false || $this->isEndOfUseStatement($tokens[$maybeUseNext]) === true) {
228
+				return;
229
+			}
230
+
231
+			$nextNonEmpty = $maybeUseNext;
232
+		}
233
+
234
+		/*
235 235
          * Deal with functions declared to return by reference.
236 236
          */
237
-        elseif ($tokens[$stackPtr]['type'] === 'T_FUNCTION'
238
-            && $tokens[$nextNonEmpty]['type'] === 'T_BITWISE_AND'
239
-        ) {
240
-            $maybeUseNext = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true, null, true);
241
-            if ($maybeUseNext === false) {
242
-                // Live coding.
243
-                return;
244
-            }
245
-
246
-            $nextNonEmpty = $maybeUseNext;
247
-        }
248
-
249
-        /*
237
+		elseif ($tokens[$stackPtr]['type'] === 'T_FUNCTION'
238
+			&& $tokens[$nextNonEmpty]['type'] === 'T_BITWISE_AND'
239
+		) {
240
+			$maybeUseNext = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true, null, true);
241
+			if ($maybeUseNext === false) {
242
+				// Live coding.
243
+				return;
244
+			}
245
+
246
+			$nextNonEmpty = $maybeUseNext;
247
+		}
248
+
249
+		/*
250 250
          * Deal with nested namespaces.
251 251
          */
252
-        elseif ($tokens[$stackPtr]['type'] === 'T_NAMESPACE') {
253
-            if ($tokens[$stackPtr + 1]['code'] === \T_NS_SEPARATOR) {
254
-                // Not a namespace declaration, but use of, i.e. namespace\someFunction();
255
-                return;
256
-            }
257
-
258
-            $endToken      = $phpcsFile->findNext(array(\T_SEMICOLON, \T_OPEN_CURLY_BRACKET), ($stackPtr + 1), null, false, null, true);
259
-            $namespaceName = trim($phpcsFile->getTokensAsString(($stackPtr + 1), ($endToken - $stackPtr - 1)));
260
-            if (empty($namespaceName) === true) {
261
-                return;
262
-            }
263
-
264
-            $namespaceParts = explode('\\', $namespaceName);
265
-            foreach ($namespaceParts as $namespacePart) {
266
-                $partLc = strtolower($namespacePart);
267
-                if (isset($this->invalidNames[$partLc]) === false) {
268
-                    continue;
269
-                }
270
-
271
-                // Find the token position of the part which matched.
272
-                for ($i = ($stackPtr + 1); $i < $endToken; $i++) {
273
-                    if ($tokens[$i]['content'] === $namespacePart) {
274
-                        $nextNonEmpty = $i;
275
-                        break;
276
-                    }
277
-                }
278
-            }
279
-            unset($i, $namespacePart, $partLc);
280
-        }
281
-
282
-        $nextContentLc = strtolower($tokens[$nextNonEmpty]['content']);
283
-        if (isset($this->invalidNames[$nextContentLc]) === false) {
284
-            return;
285
-        }
286
-
287
-        /*
252
+		elseif ($tokens[$stackPtr]['type'] === 'T_NAMESPACE') {
253
+			if ($tokens[$stackPtr + 1]['code'] === \T_NS_SEPARATOR) {
254
+				// Not a namespace declaration, but use of, i.e. namespace\someFunction();
255
+				return;
256
+			}
257
+
258
+			$endToken      = $phpcsFile->findNext(array(\T_SEMICOLON, \T_OPEN_CURLY_BRACKET), ($stackPtr + 1), null, false, null, true);
259
+			$namespaceName = trim($phpcsFile->getTokensAsString(($stackPtr + 1), ($endToken - $stackPtr - 1)));
260
+			if (empty($namespaceName) === true) {
261
+				return;
262
+			}
263
+
264
+			$namespaceParts = explode('\\', $namespaceName);
265
+			foreach ($namespaceParts as $namespacePart) {
266
+				$partLc = strtolower($namespacePart);
267
+				if (isset($this->invalidNames[$partLc]) === false) {
268
+					continue;
269
+				}
270
+
271
+				// Find the token position of the part which matched.
272
+				for ($i = ($stackPtr + 1); $i < $endToken; $i++) {
273
+					if ($tokens[$i]['content'] === $namespacePart) {
274
+						$nextNonEmpty = $i;
275
+						break;
276
+					}
277
+				}
278
+			}
279
+			unset($i, $namespacePart, $partLc);
280
+		}
281
+
282
+		$nextContentLc = strtolower($tokens[$nextNonEmpty]['content']);
283
+		if (isset($this->invalidNames[$nextContentLc]) === false) {
284
+			return;
285
+		}
286
+
287
+		/*
288 288
          * Deal with PHP 7 relaxing the rules.
289 289
          * "As of PHP 7.0.0 these keywords are allowed as property, constant, and method names
290 290
          * of classes, interfaces and traits, except that class may not be used as constant name."
291 291
          */
292
-        if ((($tokens[$stackPtr]['type'] === 'T_FUNCTION'
293
-                && $this->inClassScope($phpcsFile, $stackPtr, false) === true)
294
-            || ($tokens[$stackPtr]['type'] === 'T_CONST'
295
-                && $this->isClassConstant($phpcsFile, $stackPtr) === true
296
-                && $nextContentLc !== 'class'))
297
-            && $this->supportsBelow('5.6') === false
298
-        ) {
299
-            return;
300
-        }
301
-
302
-        if ($this->supportsAbove($this->invalidNames[$nextContentLc])) {
303
-            $data = array(
304
-                $tokens[$nextNonEmpty]['content'],
305
-                $this->invalidNames[$nextContentLc],
306
-            );
307
-            $this->addError($phpcsFile, $stackPtr, $tokens[$nextNonEmpty]['content'], $data);
308
-        }
309
-    }
310
-
311
-    /**
312
-     * Processes this test, when one of its tokens is encountered.
313
-     *
314
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
315
-     * @param int                   $stackPtr  The position of the current token in the
316
-     *                                         stack passed in $tokens.
317
-     * @param array                 $tokens    The stack of tokens that make up
318
-     *                                         the file.
319
-     *
320
-     * @return void
321
-     */
322
-    public function processString(File $phpcsFile, $stackPtr, $tokens)
323
-    {
324
-        $tokenContentLc = strtolower($tokens[$stackPtr]['content']);
325
-
326
-        /*
292
+		if ((($tokens[$stackPtr]['type'] === 'T_FUNCTION'
293
+				&& $this->inClassScope($phpcsFile, $stackPtr, false) === true)
294
+			|| ($tokens[$stackPtr]['type'] === 'T_CONST'
295
+				&& $this->isClassConstant($phpcsFile, $stackPtr) === true
296
+				&& $nextContentLc !== 'class'))
297
+			&& $this->supportsBelow('5.6') === false
298
+		) {
299
+			return;
300
+		}
301
+
302
+		if ($this->supportsAbove($this->invalidNames[$nextContentLc])) {
303
+			$data = array(
304
+				$tokens[$nextNonEmpty]['content'],
305
+				$this->invalidNames[$nextContentLc],
306
+			);
307
+			$this->addError($phpcsFile, $stackPtr, $tokens[$nextNonEmpty]['content'], $data);
308
+		}
309
+	}
310
+
311
+	/**
312
+	 * Processes this test, when one of its tokens is encountered.
313
+	 *
314
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
315
+	 * @param int                   $stackPtr  The position of the current token in the
316
+	 *                                         stack passed in $tokens.
317
+	 * @param array                 $tokens    The stack of tokens that make up
318
+	 *                                         the file.
319
+	 *
320
+	 * @return void
321
+	 */
322
+	public function processString(File $phpcsFile, $stackPtr, $tokens)
323
+	{
324
+		$tokenContentLc = strtolower($tokens[$stackPtr]['content']);
325
+
326
+		/*
327 327
          * Special case for PHP versions where the target is not yet identified as
328 328
          * its own token, but presents as T_STRING.
329 329
          * - trait keyword in PHP < 5.4
330 330
          */
331
-        if (version_compare(\PHP_VERSION_ID, '50400', '<') && $tokenContentLc === 'trait') {
332
-            $this->processNonString($phpcsFile, $stackPtr, $tokens);
333
-            return;
334
-        }
335
-
336
-        // Look for any define/defined tokens (both T_STRING ones, blame Tokenizer).
337
-        if ($tokenContentLc !== 'define' && $tokenContentLc !== 'defined') {
338
-            return;
339
-        }
340
-
341
-        // Retrieve the define(d) constant name.
342
-        $firstParam = $this->getFunctionCallParameter($phpcsFile, $stackPtr, 1);
343
-        if ($firstParam === false) {
344
-            return;
345
-        }
346
-
347
-        $defineName   = $this->stripQuotes($firstParam['raw']);
348
-        $defineNameLc = strtolower($defineName);
349
-
350
-        if (isset($this->invalidNames[$defineNameLc]) && $this->supportsAbove($this->invalidNames[$defineNameLc])) {
351
-            $data = array(
352
-                $defineName,
353
-                $this->invalidNames[$defineNameLc],
354
-            );
355
-            $this->addError($phpcsFile, $stackPtr, $defineNameLc, $data);
356
-        }
357
-    }
358
-
359
-
360
-    /**
361
-     * Add the error message.
362
-     *
363
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
364
-     * @param int                   $stackPtr  The position of the current token in the
365
-     *                                         stack passed in $tokens.
366
-     * @param string                $content   The token content found.
367
-     * @param array                 $data      The data to pass into the error message.
368
-     *
369
-     * @return void
370
-     */
371
-    protected function addError(File $phpcsFile, $stackPtr, $content, $data)
372
-    {
373
-        $error     = "Function name, class name, namespace name or constant name can not be reserved keyword '%s' (since version %s)";
374
-        $errorCode = $this->stringToErrorCode($content) . 'Found';
375
-        $phpcsFile->addError($error, $stackPtr, $errorCode, $data);
376
-    }
377
-
378
-
379
-    /**
380
-     * Check if the current token code is for a token which can be considered
381
-     * the end of a (partial) use statement.
382
-     *
383
-     * @param int $token The current token information.
384
-     *
385
-     * @return bool
386
-     */
387
-    protected function isEndOfUseStatement($token)
388
-    {
389
-        return \in_array($token['code'], array(\T_CLOSE_CURLY_BRACKET, \T_SEMICOLON, \T_COMMA), true);
390
-    }
331
+		if (version_compare(\PHP_VERSION_ID, '50400', '<') && $tokenContentLc === 'trait') {
332
+			$this->processNonString($phpcsFile, $stackPtr, $tokens);
333
+			return;
334
+		}
335
+
336
+		// Look for any define/defined tokens (both T_STRING ones, blame Tokenizer).
337
+		if ($tokenContentLc !== 'define' && $tokenContentLc !== 'defined') {
338
+			return;
339
+		}
340
+
341
+		// Retrieve the define(d) constant name.
342
+		$firstParam = $this->getFunctionCallParameter($phpcsFile, $stackPtr, 1);
343
+		if ($firstParam === false) {
344
+			return;
345
+		}
346
+
347
+		$defineName   = $this->stripQuotes($firstParam['raw']);
348
+		$defineNameLc = strtolower($defineName);
349
+
350
+		if (isset($this->invalidNames[$defineNameLc]) && $this->supportsAbove($this->invalidNames[$defineNameLc])) {
351
+			$data = array(
352
+				$defineName,
353
+				$this->invalidNames[$defineNameLc],
354
+			);
355
+			$this->addError($phpcsFile, $stackPtr, $defineNameLc, $data);
356
+		}
357
+	}
358
+
359
+
360
+	/**
361
+	 * Add the error message.
362
+	 *
363
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
364
+	 * @param int                   $stackPtr  The position of the current token in the
365
+	 *                                         stack passed in $tokens.
366
+	 * @param string                $content   The token content found.
367
+	 * @param array                 $data      The data to pass into the error message.
368
+	 *
369
+	 * @return void
370
+	 */
371
+	protected function addError(File $phpcsFile, $stackPtr, $content, $data)
372
+	{
373
+		$error     = "Function name, class name, namespace name or constant name can not be reserved keyword '%s' (since version %s)";
374
+		$errorCode = $this->stringToErrorCode($content) . 'Found';
375
+		$phpcsFile->addError($error, $stackPtr, $errorCode, $data);
376
+	}
377
+
378
+
379
+	/**
380
+	 * Check if the current token code is for a token which can be considered
381
+	 * the end of a (partial) use statement.
382
+	 *
383
+	 * @param int $token The current token information.
384
+	 *
385
+	 * @return bool
386
+	 */
387
+	protected function isEndOfUseStatement($token)
388
+	{
389
+		return \in_array($token['code'], array(\T_CLOSE_CURLY_BRACKET, \T_SEMICOLON, \T_COMMA), true);
390
+	}
391 391
 }
Please login to merge, or discard this patch.
Sniffs/ControlStructures/DiscouragedSwitchContinueSniff.php 1 patch
Indentation   +191 added lines, -191 removed lines patch added patch discarded remove patch
@@ -29,195 +29,195 @@
 block discarded – undo
29 29
 class DiscouragedSwitchContinueSniff extends Sniff
30 30
 {
31 31
 
32
-    /**
33
-     * Token codes of control structures which can be targeted using continue.
34
-     *
35
-     * @var array
36
-     */
37
-    protected $loopStructures = array(
38
-        \T_FOR     => \T_FOR,
39
-        \T_FOREACH => \T_FOREACH,
40
-        \T_WHILE   => \T_WHILE,
41
-        \T_DO      => \T_DO,
42
-        \T_SWITCH  => \T_SWITCH,
43
-    );
44
-
45
-    /**
46
-     * Tokens which start a new case within a switch.
47
-     *
48
-     * @var array
49
-     */
50
-    protected $caseTokens = array(
51
-        \T_CASE    => \T_CASE,
52
-        \T_DEFAULT => \T_DEFAULT,
53
-    );
54
-
55
-    /**
56
-     * Token codes which are accepted to determine the level for the continue.
57
-     *
58
-     * This array is enriched with the arithmetic operators in the register() method.
59
-     *
60
-     * @var array
61
-     */
62
-    protected $acceptedLevelTokens = array(
63
-        \T_LNUMBER           => \T_LNUMBER,
64
-        \T_OPEN_PARENTHESIS  => \T_OPEN_PARENTHESIS,
65
-        \T_CLOSE_PARENTHESIS => \T_CLOSE_PARENTHESIS,
66
-    );
67
-
68
-
69
-    /**
70
-     * Returns an array of tokens this test wants to listen for.
71
-     *
72
-     * @return array
73
-     */
74
-    public function register()
75
-    {
76
-        $this->acceptedLevelTokens += Tokens::$arithmeticTokens;
77
-        $this->acceptedLevelTokens += Tokens::$emptyTokens;
78
-
79
-        return array(\T_SWITCH);
80
-    }
81
-
82
-    /**
83
-     * Processes this test, when one of its tokens is encountered.
84
-     *
85
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
86
-     * @param int                   $stackPtr  The position of the current token in the
87
-     *                                         stack passed in $tokens.
88
-     *
89
-     * @return void
90
-     */
91
-    public function process(File $phpcsFile, $stackPtr)
92
-    {
93
-        if ($this->supportsAbove('7.3') === false) {
94
-            return;
95
-        }
96
-
97
-        $tokens = $phpcsFile->getTokens();
98
-
99
-        if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
100
-            return;
101
-        }
102
-
103
-        $switchOpener = $tokens[$stackPtr]['scope_opener'];
104
-        $switchCloser = $tokens[$stackPtr]['scope_closer'];
105
-
106
-        // Quick check whether we need to bother with the more complex logic.
107
-        $hasContinue = $phpcsFile->findNext(\T_CONTINUE, ($switchOpener + 1), $switchCloser);
108
-        if ($hasContinue === false) {
109
-            return;
110
-        }
111
-
112
-        $caseDefault = $switchOpener;
113
-
114
-        do {
115
-            $caseDefault = $phpcsFile->findNext($this->caseTokens, ($caseDefault + 1), $switchCloser);
116
-            if ($caseDefault === false) {
117
-                break;
118
-            }
119
-
120
-            if (isset($tokens[$caseDefault]['scope_opener']) === false) {
121
-                // Unknown start of the case, skip.
122
-                continue;
123
-            }
124
-
125
-            $caseOpener      = $tokens[$caseDefault]['scope_opener'];
126
-            $nextCaseDefault = $phpcsFile->findNext($this->caseTokens, ($caseDefault + 1), $switchCloser);
127
-            if ($nextCaseDefault === false) {
128
-                $caseCloser = $switchCloser;
129
-            } else {
130
-                $caseCloser = $nextCaseDefault;
131
-            }
132
-
133
-            // Check for unscoped control structures within the case.
134
-            $controlStructure = $caseOpener;
135
-            $doCount          = 0;
136
-            while (($controlStructure = $phpcsFile->findNext($this->loopStructures, ($controlStructure + 1), $caseCloser)) !== false) {
137
-                if ($tokens[$controlStructure]['code'] === \T_DO) {
138
-                    $doCount++;
139
-                }
140
-
141
-                if (isset($tokens[$controlStructure]['scope_opener'], $tokens[$controlStructure]['scope_closer']) === false) {
142
-                    if ($tokens[$controlStructure]['code'] === \T_WHILE && $doCount > 0) {
143
-                        // While in a do-while construct.
144
-                        $doCount--;
145
-                        continue;
146
-                    }
147
-
148
-                    // Control structure without braces found within the case, ignore this case.
149
-                    continue 2;
150
-                }
151
-            }
152
-
153
-            // Examine the contents of the case.
154
-            $continue = $caseOpener;
155
-
156
-            do {
157
-                $continue = $phpcsFile->findNext(\T_CONTINUE, ($continue + 1), $caseCloser);
158
-                if ($continue === false) {
159
-                    break;
160
-                }
161
-
162
-                $nextSemicolon = $phpcsFile->findNext(array(\T_SEMICOLON, \T_CLOSE_TAG), ($continue + 1), $caseCloser);
163
-                $codeString    = '';
164
-                for ($i = ($continue + 1); $i < $nextSemicolon; $i++) {
165
-                    if (isset($this->acceptedLevelTokens[$tokens[$i]['code']]) === false) {
166
-                        // Function call/variable or other token which make numeric level impossible to determine.
167
-                        continue 2;
168
-                    }
169
-
170
-                    if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
171
-                        continue;
172
-                    }
173
-
174
-                    $codeString .= $tokens[$i]['content'];
175
-                }
176
-
177
-                $level = null;
178
-                if ($codeString !== '') {
179
-                    if (is_numeric($codeString)) {
180
-                        $level = (int) $codeString;
181
-                    } else {
182
-                        // With the above logic, the string can only contain digits and operators, eval!
183
-                        $level = eval("return ( $codeString );");
184
-                    }
185
-                }
186
-
187
-                if (isset($level) === false || $level === 0) {
188
-                    $level = 1;
189
-                }
190
-
191
-                // Examine which control structure is being targeted by the continue statement.
192
-                if (isset($tokens[$continue]['conditions']) === false) {
193
-                    continue;
194
-                }
195
-
196
-                $conditions = array_reverse($tokens[$continue]['conditions'], true);
197
-                // PHPCS adds more structures to the conditions array than we want to take into
198
-                // consideration, so clean up the array.
199
-                foreach ($conditions as $tokenPtr => $tokenCode) {
200
-                    if (isset($this->loopStructures[$tokenCode]) === false) {
201
-                        unset($conditions[$tokenPtr]);
202
-                    }
203
-                }
204
-
205
-                $targetCondition = \array_slice($conditions, ($level - 1), 1, true);
206
-                if (empty($targetCondition)) {
207
-                    continue;
208
-                }
209
-
210
-                $conditionToken = key($targetCondition);
211
-                if ($conditionToken === $stackPtr) {
212
-                    $phpcsFile->addWarning(
213
-                        "Targeting a 'switch' control structure with a 'continue' statement is strongly discouraged and will throw a warning as of PHP 7.3.",
214
-                        $continue,
215
-                        'Found'
216
-                    );
217
-                }
218
-
219
-            } while ($continue < $caseCloser);
220
-
221
-        } while ($caseDefault < $switchCloser);
222
-    }
32
+	/**
33
+	 * Token codes of control structures which can be targeted using continue.
34
+	 *
35
+	 * @var array
36
+	 */
37
+	protected $loopStructures = array(
38
+		\T_FOR     => \T_FOR,
39
+		\T_FOREACH => \T_FOREACH,
40
+		\T_WHILE   => \T_WHILE,
41
+		\T_DO      => \T_DO,
42
+		\T_SWITCH  => \T_SWITCH,
43
+	);
44
+
45
+	/**
46
+	 * Tokens which start a new case within a switch.
47
+	 *
48
+	 * @var array
49
+	 */
50
+	protected $caseTokens = array(
51
+		\T_CASE    => \T_CASE,
52
+		\T_DEFAULT => \T_DEFAULT,
53
+	);
54
+
55
+	/**
56
+	 * Token codes which are accepted to determine the level for the continue.
57
+	 *
58
+	 * This array is enriched with the arithmetic operators in the register() method.
59
+	 *
60
+	 * @var array
61
+	 */
62
+	protected $acceptedLevelTokens = array(
63
+		\T_LNUMBER           => \T_LNUMBER,
64
+		\T_OPEN_PARENTHESIS  => \T_OPEN_PARENTHESIS,
65
+		\T_CLOSE_PARENTHESIS => \T_CLOSE_PARENTHESIS,
66
+	);
67
+
68
+
69
+	/**
70
+	 * Returns an array of tokens this test wants to listen for.
71
+	 *
72
+	 * @return array
73
+	 */
74
+	public function register()
75
+	{
76
+		$this->acceptedLevelTokens += Tokens::$arithmeticTokens;
77
+		$this->acceptedLevelTokens += Tokens::$emptyTokens;
78
+
79
+		return array(\T_SWITCH);
80
+	}
81
+
82
+	/**
83
+	 * Processes this test, when one of its tokens is encountered.
84
+	 *
85
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
86
+	 * @param int                   $stackPtr  The position of the current token in the
87
+	 *                                         stack passed in $tokens.
88
+	 *
89
+	 * @return void
90
+	 */
91
+	public function process(File $phpcsFile, $stackPtr)
92
+	{
93
+		if ($this->supportsAbove('7.3') === false) {
94
+			return;
95
+		}
96
+
97
+		$tokens = $phpcsFile->getTokens();
98
+
99
+		if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
100
+			return;
101
+		}
102
+
103
+		$switchOpener = $tokens[$stackPtr]['scope_opener'];
104
+		$switchCloser = $tokens[$stackPtr]['scope_closer'];
105
+
106
+		// Quick check whether we need to bother with the more complex logic.
107
+		$hasContinue = $phpcsFile->findNext(\T_CONTINUE, ($switchOpener + 1), $switchCloser);
108
+		if ($hasContinue === false) {
109
+			return;
110
+		}
111
+
112
+		$caseDefault = $switchOpener;
113
+
114
+		do {
115
+			$caseDefault = $phpcsFile->findNext($this->caseTokens, ($caseDefault + 1), $switchCloser);
116
+			if ($caseDefault === false) {
117
+				break;
118
+			}
119
+
120
+			if (isset($tokens[$caseDefault]['scope_opener']) === false) {
121
+				// Unknown start of the case, skip.
122
+				continue;
123
+			}
124
+
125
+			$caseOpener      = $tokens[$caseDefault]['scope_opener'];
126
+			$nextCaseDefault = $phpcsFile->findNext($this->caseTokens, ($caseDefault + 1), $switchCloser);
127
+			if ($nextCaseDefault === false) {
128
+				$caseCloser = $switchCloser;
129
+			} else {
130
+				$caseCloser = $nextCaseDefault;
131
+			}
132
+
133
+			// Check for unscoped control structures within the case.
134
+			$controlStructure = $caseOpener;
135
+			$doCount          = 0;
136
+			while (($controlStructure = $phpcsFile->findNext($this->loopStructures, ($controlStructure + 1), $caseCloser)) !== false) {
137
+				if ($tokens[$controlStructure]['code'] === \T_DO) {
138
+					$doCount++;
139
+				}
140
+
141
+				if (isset($tokens[$controlStructure]['scope_opener'], $tokens[$controlStructure]['scope_closer']) === false) {
142
+					if ($tokens[$controlStructure]['code'] === \T_WHILE && $doCount > 0) {
143
+						// While in a do-while construct.
144
+						$doCount--;
145
+						continue;
146
+					}
147
+
148
+					// Control structure without braces found within the case, ignore this case.
149
+					continue 2;
150
+				}
151
+			}
152
+
153
+			// Examine the contents of the case.
154
+			$continue = $caseOpener;
155
+
156
+			do {
157
+				$continue = $phpcsFile->findNext(\T_CONTINUE, ($continue + 1), $caseCloser);
158
+				if ($continue === false) {
159
+					break;
160
+				}
161
+
162
+				$nextSemicolon = $phpcsFile->findNext(array(\T_SEMICOLON, \T_CLOSE_TAG), ($continue + 1), $caseCloser);
163
+				$codeString    = '';
164
+				for ($i = ($continue + 1); $i < $nextSemicolon; $i++) {
165
+					if (isset($this->acceptedLevelTokens[$tokens[$i]['code']]) === false) {
166
+						// Function call/variable or other token which make numeric level impossible to determine.
167
+						continue 2;
168
+					}
169
+
170
+					if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
171
+						continue;
172
+					}
173
+
174
+					$codeString .= $tokens[$i]['content'];
175
+				}
176
+
177
+				$level = null;
178
+				if ($codeString !== '') {
179
+					if (is_numeric($codeString)) {
180
+						$level = (int) $codeString;
181
+					} else {
182
+						// With the above logic, the string can only contain digits and operators, eval!
183
+						$level = eval("return ( $codeString );");
184
+					}
185
+				}
186
+
187
+				if (isset($level) === false || $level === 0) {
188
+					$level = 1;
189
+				}
190
+
191
+				// Examine which control structure is being targeted by the continue statement.
192
+				if (isset($tokens[$continue]['conditions']) === false) {
193
+					continue;
194
+				}
195
+
196
+				$conditions = array_reverse($tokens[$continue]['conditions'], true);
197
+				// PHPCS adds more structures to the conditions array than we want to take into
198
+				// consideration, so clean up the array.
199
+				foreach ($conditions as $tokenPtr => $tokenCode) {
200
+					if (isset($this->loopStructures[$tokenCode]) === false) {
201
+						unset($conditions[$tokenPtr]);
202
+					}
203
+				}
204
+
205
+				$targetCondition = \array_slice($conditions, ($level - 1), 1, true);
206
+				if (empty($targetCondition)) {
207
+					continue;
208
+				}
209
+
210
+				$conditionToken = key($targetCondition);
211
+				if ($conditionToken === $stackPtr) {
212
+					$phpcsFile->addWarning(
213
+						"Targeting a 'switch' control structure with a 'continue' statement is strongly discouraged and will throw a warning as of PHP 7.3.",
214
+						$continue,
215
+						'Found'
216
+					);
217
+				}
218
+
219
+			} while ($continue < $caseCloser);
220
+
221
+		} while ($caseDefault < $switchCloser);
222
+	}
223 223
 }
Please login to merge, or discard this patch.
Sniffs/ControlStructures/ForbiddenBreakContinueVariableArgumentsSniff.php 1 patch
Indentation   +61 added lines, -61 removed lines patch added patch discarded remove patch
@@ -30,72 +30,72 @@
 block discarded – undo
30 30
  */
31 31
 class ForbiddenBreakContinueVariableArgumentsSniff extends Sniff
32 32
 {
33
-    /**
34
-     * Error types this sniff handles for forbidden break/continue arguments.
35
-     *
36
-     * Array key is the error code. Array value will be used as part of the error message.
37
-     *
38
-     * @var array
39
-     */
40
-    private $errorTypes = array(
41
-        'variableArgument' => 'a variable argument',
42
-        'zeroArgument'     => '0 as an argument',
43
-    );
33
+	/**
34
+	 * Error types this sniff handles for forbidden break/continue arguments.
35
+	 *
36
+	 * Array key is the error code. Array value will be used as part of the error message.
37
+	 *
38
+	 * @var array
39
+	 */
40
+	private $errorTypes = array(
41
+		'variableArgument' => 'a variable argument',
42
+		'zeroArgument'     => '0 as an argument',
43
+	);
44 44
 
45
-    /**
46
-     * Returns an array of tokens this test wants to listen for.
47
-     *
48
-     * @return array
49
-     */
50
-    public function register()
51
-    {
52
-        return array(\T_BREAK, \T_CONTINUE);
53
-    }
45
+	/**
46
+	 * Returns an array of tokens this test wants to listen for.
47
+	 *
48
+	 * @return array
49
+	 */
50
+	public function register()
51
+	{
52
+		return array(\T_BREAK, \T_CONTINUE);
53
+	}
54 54
 
55
-    /**
56
-     * Processes this test, when one of its tokens is encountered.
57
-     *
58
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
59
-     * @param int                   $stackPtr  The position of the current token in the
60
-     *                                         stack passed in $tokens.
61
-     *
62
-     * @return void
63
-     */
64
-    public function process(File $phpcsFile, $stackPtr)
65
-    {
66
-        if ($this->supportsAbove('5.4') === false) {
67
-            return;
68
-        }
55
+	/**
56
+	 * Processes this test, when one of its tokens is encountered.
57
+	 *
58
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
59
+	 * @param int                   $stackPtr  The position of the current token in the
60
+	 *                                         stack passed in $tokens.
61
+	 *
62
+	 * @return void
63
+	 */
64
+	public function process(File $phpcsFile, $stackPtr)
65
+	{
66
+		if ($this->supportsAbove('5.4') === false) {
67
+			return;
68
+		}
69 69
 
70
-        $tokens             = $phpcsFile->getTokens();
71
-        $nextSemicolonToken = $phpcsFile->findNext(array(\T_SEMICOLON, \T_CLOSE_TAG), ($stackPtr), null, false);
72
-        $errorType          = '';
73
-        for ($curToken = $stackPtr + 1; $curToken < $nextSemicolonToken; $curToken++) {
74
-            if ($tokens[$curToken]['type'] === 'T_STRING') {
75
-                // If the next non-whitespace token after the string
76
-                // is an opening parenthesis then it's a function call.
77
-                $openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, $curToken + 1, null, true);
78
-                if ($tokens[$openBracket]['code'] === \T_OPEN_PARENTHESIS) {
79
-                    $errorType = 'variableArgument';
80
-                    break;
81
-                }
70
+		$tokens             = $phpcsFile->getTokens();
71
+		$nextSemicolonToken = $phpcsFile->findNext(array(\T_SEMICOLON, \T_CLOSE_TAG), ($stackPtr), null, false);
72
+		$errorType          = '';
73
+		for ($curToken = $stackPtr + 1; $curToken < $nextSemicolonToken; $curToken++) {
74
+			if ($tokens[$curToken]['type'] === 'T_STRING') {
75
+				// If the next non-whitespace token after the string
76
+				// is an opening parenthesis then it's a function call.
77
+				$openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, $curToken + 1, null, true);
78
+				if ($tokens[$openBracket]['code'] === \T_OPEN_PARENTHESIS) {
79
+					$errorType = 'variableArgument';
80
+					break;
81
+				}
82 82
 
83
-            } elseif (\in_array($tokens[$curToken]['type'], array('T_VARIABLE', 'T_FUNCTION', 'T_CLOSURE'), true)) {
84
-                $errorType = 'variableArgument';
85
-                break;
83
+			} elseif (\in_array($tokens[$curToken]['type'], array('T_VARIABLE', 'T_FUNCTION', 'T_CLOSURE'), true)) {
84
+				$errorType = 'variableArgument';
85
+				break;
86 86
 
87
-            } elseif ($tokens[$curToken]['type'] === 'T_LNUMBER' && $tokens[$curToken]['content'] === '0') {
88
-                $errorType = 'zeroArgument';
89
-                break;
90
-            }
91
-        }
87
+			} elseif ($tokens[$curToken]['type'] === 'T_LNUMBER' && $tokens[$curToken]['content'] === '0') {
88
+				$errorType = 'zeroArgument';
89
+				break;
90
+			}
91
+		}
92 92
 
93
-        if ($errorType !== '') {
94
-            $error     = 'Using %s on break or continue is forbidden since PHP 5.4';
95
-            $errorCode = $errorType . 'Found';
96
-            $data      = array($this->errorTypes[$errorType]);
93
+		if ($errorType !== '') {
94
+			$error     = 'Using %s on break or continue is forbidden since PHP 5.4';
95
+			$errorCode = $errorType . 'Found';
96
+			$data      = array($this->errorTypes[$errorType]);
97 97
 
98
-            $phpcsFile->addError($error, $stackPtr, $errorCode, $data);
99
-        }
100
-    }
98
+			$phpcsFile->addError($error, $stackPtr, $errorCode, $data);
99
+		}
100
+	}
101 101
 }
Please login to merge, or discard this patch.
Sniffs/ControlStructures/ForbiddenSwitchWithMultipleDefaultBlocksSniff.php 1 patch
Indentation   +44 added lines, -44 removed lines patch added patch discarded remove patch
@@ -28,52 +28,52 @@
 block discarded – undo
28 28
 class ForbiddenSwitchWithMultipleDefaultBlocksSniff extends Sniff
29 29
 {
30 30
 
31
-    /**
32
-     * Returns an array of tokens this test wants to listen for.
33
-     *
34
-     * @return array
35
-     */
36
-    public function register()
37
-    {
38
-        return array(\T_SWITCH);
39
-    }
31
+	/**
32
+	 * Returns an array of tokens this test wants to listen for.
33
+	 *
34
+	 * @return array
35
+	 */
36
+	public function register()
37
+	{
38
+		return array(\T_SWITCH);
39
+	}
40 40
 
41
-    /**
42
-     * Processes this test, when one of its tokens is encountered.
43
-     *
44
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
45
-     * @param int                   $stackPtr  The position of the current token
46
-     *                                         in the stack passed in $tokens.
47
-     *
48
-     * @return void
49
-     */
50
-    public function process(File $phpcsFile, $stackPtr)
51
-    {
52
-        if ($this->supportsAbove('7.0') === false) {
53
-            return;
54
-        }
41
+	/**
42
+	 * Processes this test, when one of its tokens is encountered.
43
+	 *
44
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
45
+	 * @param int                   $stackPtr  The position of the current token
46
+	 *                                         in the stack passed in $tokens.
47
+	 *
48
+	 * @return void
49
+	 */
50
+	public function process(File $phpcsFile, $stackPtr)
51
+	{
52
+		if ($this->supportsAbove('7.0') === false) {
53
+			return;
54
+		}
55 55
 
56
-        $tokens = $phpcsFile->getTokens();
57
-        if (isset($tokens[$stackPtr]['scope_closer']) === false) {
58
-            return;
59
-        }
56
+		$tokens = $phpcsFile->getTokens();
57
+		if (isset($tokens[$stackPtr]['scope_closer']) === false) {
58
+			return;
59
+		}
60 60
 
61
-        $defaultToken = $stackPtr;
62
-        $defaultCount = 0;
63
-        $targetLevel  = $tokens[$stackPtr]['level'] + 1;
64
-        while ($defaultCount < 2 && ($defaultToken = $phpcsFile->findNext(array(\T_DEFAULT), $defaultToken + 1, $tokens[$stackPtr]['scope_closer'])) !== false) {
65
-            // Same level or one below (= two default cases after each other).
66
-            if ($tokens[$defaultToken]['level'] === $targetLevel || $tokens[$defaultToken]['level'] === ($targetLevel + 1)) {
67
-                $defaultCount++;
68
-            }
69
-        }
61
+		$defaultToken = $stackPtr;
62
+		$defaultCount = 0;
63
+		$targetLevel  = $tokens[$stackPtr]['level'] + 1;
64
+		while ($defaultCount < 2 && ($defaultToken = $phpcsFile->findNext(array(\T_DEFAULT), $defaultToken + 1, $tokens[$stackPtr]['scope_closer'])) !== false) {
65
+			// Same level or one below (= two default cases after each other).
66
+			if ($tokens[$defaultToken]['level'] === $targetLevel || $tokens[$defaultToken]['level'] === ($targetLevel + 1)) {
67
+				$defaultCount++;
68
+			}
69
+		}
70 70
 
71
-        if ($defaultCount > 1) {
72
-            $phpcsFile->addError(
73
-                'Switch statements can not have multiple default blocks since PHP 7.0',
74
-                $stackPtr,
75
-                'Found'
76
-            );
77
-        }
78
-    }
71
+		if ($defaultCount > 1) {
72
+			$phpcsFile->addError(
73
+				'Switch statements can not have multiple default blocks since PHP 7.0',
74
+				$stackPtr,
75
+				'Found'
76
+			);
77
+		}
78
+	}
79 79
 }
Please login to merge, or discard this patch.
Sniffs/ControlStructures/ForbiddenBreakContinueOutsideLoopSniff.php 1 patch
Indentation   +71 added lines, -71 removed lines patch added patch discarded remove patch
@@ -28,82 +28,82 @@
 block discarded – undo
28 28
 class ForbiddenBreakContinueOutsideLoopSniff extends Sniff
29 29
 {
30 30
 
31
-    /**
32
-     * Token codes of control structure in which usage of break/continue is valid.
33
-     *
34
-     * @var array
35
-     */
36
-    protected $validLoopStructures = array(
37
-        \T_FOR     => true,
38
-        \T_FOREACH => true,
39
-        \T_WHILE   => true,
40
-        \T_DO      => true,
41
-        \T_SWITCH  => true,
42
-    );
31
+	/**
32
+	 * Token codes of control structure in which usage of break/continue is valid.
33
+	 *
34
+	 * @var array
35
+	 */
36
+	protected $validLoopStructures = array(
37
+		\T_FOR     => true,
38
+		\T_FOREACH => true,
39
+		\T_WHILE   => true,
40
+		\T_DO      => true,
41
+		\T_SWITCH  => true,
42
+	);
43 43
 
44
-    /**
45
-     * Token codes which did not correctly get a condition assigned in older PHPCS versions.
46
-     *
47
-     * @var array
48
-     */
49
-    protected $backCompat = array(
50
-        \T_CASE    => true,
51
-        \T_DEFAULT => true,
52
-    );
44
+	/**
45
+	 * Token codes which did not correctly get a condition assigned in older PHPCS versions.
46
+	 *
47
+	 * @var array
48
+	 */
49
+	protected $backCompat = array(
50
+		\T_CASE    => true,
51
+		\T_DEFAULT => true,
52
+	);
53 53
 
54
-    /**
55
-     * Returns an array of tokens this test wants to listen for.
56
-     *
57
-     * @return array
58
-     */
59
-    public function register()
60
-    {
61
-        return array(
62
-            \T_BREAK,
63
-            \T_CONTINUE,
64
-        );
65
-    }
54
+	/**
55
+	 * Returns an array of tokens this test wants to listen for.
56
+	 *
57
+	 * @return array
58
+	 */
59
+	public function register()
60
+	{
61
+		return array(
62
+			\T_BREAK,
63
+			\T_CONTINUE,
64
+		);
65
+	}
66 66
 
67
-    /**
68
-     * Processes this test, when one of its tokens is encountered.
69
-     *
70
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
71
-     * @param int                   $stackPtr  The position of the current token in the
72
-     *                                         stack passed in $tokens.
73
-     *
74
-     * @return void
75
-     */
76
-    public function process(File $phpcsFile, $stackPtr)
77
-    {
78
-        $tokens = $phpcsFile->getTokens();
79
-        $token  = $tokens[$stackPtr];
67
+	/**
68
+	 * Processes this test, when one of its tokens is encountered.
69
+	 *
70
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
71
+	 * @param int                   $stackPtr  The position of the current token in the
72
+	 *                                         stack passed in $tokens.
73
+	 *
74
+	 * @return void
75
+	 */
76
+	public function process(File $phpcsFile, $stackPtr)
77
+	{
78
+		$tokens = $phpcsFile->getTokens();
79
+		$token  = $tokens[$stackPtr];
80 80
 
81
-        // Check if the break/continue is within a valid loop structure.
82
-        if (empty($token['conditions']) === false) {
83
-            foreach ($token['conditions'] as $tokenCode) {
84
-                if (isset($this->validLoopStructures[$tokenCode]) === true) {
85
-                    return;
86
-                }
87
-            }
88
-        } else {
89
-            // Deal with older PHPCS versions.
90
-            if (isset($token['scope_condition']) === true && isset($this->backCompat[$tokens[$token['scope_condition']]['code']]) === true) {
91
-                return;
92
-            }
93
-        }
81
+		// Check if the break/continue is within a valid loop structure.
82
+		if (empty($token['conditions']) === false) {
83
+			foreach ($token['conditions'] as $tokenCode) {
84
+				if (isset($this->validLoopStructures[$tokenCode]) === true) {
85
+					return;
86
+				}
87
+			}
88
+		} else {
89
+			// Deal with older PHPCS versions.
90
+			if (isset($token['scope_condition']) === true && isset($this->backCompat[$tokens[$token['scope_condition']]['code']]) === true) {
91
+				return;
92
+			}
93
+		}
94 94
 
95
-        // If we're still here, no valid loop structure container has been found, so throw an error.
96
-        $error     = "Using '%s' outside of a loop or switch structure is invalid";
97
-        $isError   = false;
98
-        $errorCode = 'Found';
99
-        $data      = array($token['content']);
95
+		// If we're still here, no valid loop structure container has been found, so throw an error.
96
+		$error     = "Using '%s' outside of a loop or switch structure is invalid";
97
+		$isError   = false;
98
+		$errorCode = 'Found';
99
+		$data      = array($token['content']);
100 100
 
101
-        if ($this->supportsAbove('7.0')) {
102
-            $error    .= ' and will throw a fatal error since PHP 7.0';
103
-            $isError   = true;
104
-            $errorCode = 'FatalError';
105
-        }
101
+		if ($this->supportsAbove('7.0')) {
102
+			$error    .= ' and will throw a fatal error since PHP 7.0';
103
+			$isError   = true;
104
+			$errorCode = 'FatalError';
105
+		}
106 106
 
107
-        $this->addMessage($phpcsFile, $error, $stackPtr, $isError, $errorCode, $data);
108
-    }
107
+		$this->addMessage($phpcsFile, $error, $stackPtr, $isError, $errorCode, $data);
108
+	}
109 109
 }
Please login to merge, or discard this patch.