Completed
Pull Request — develop (#1492)
by Zack
28:58 queued 09:00
created
PHPCompatibility/Sniffs/Classes/RemovedOrphanedParentSniff.php 1 patch
Indentation   +72 added lines, -72 removed lines patch added patch discarded remove patch
@@ -28,88 +28,88 @@
 block discarded – undo
28 28
 class RemovedOrphanedParentSniff extends Sniff
29 29
 {
30 30
 
31
-    /**
32
-     * Class scopes to check the class declaration.
33
-     *
34
-     * @since 9.2.0
35
-     *
36
-     * @var array
37
-     */
38
-    public $classScopeTokens = array(
39
-        'T_CLASS'      => true,
40
-        'T_ANON_CLASS' => true,
41
-    );
31
+	/**
32
+	 * Class scopes to check the class declaration.
33
+	 *
34
+	 * @since 9.2.0
35
+	 *
36
+	 * @var array
37
+	 */
38
+	public $classScopeTokens = array(
39
+		'T_CLASS'      => true,
40
+		'T_ANON_CLASS' => true,
41
+	);
42 42
 
43
-    /**
44
-     * Returns an array of tokens this test wants to listen for.
45
-     *
46
-     * @since 9.2.0
47
-     *
48
-     * @return array
49
-     */
50
-    public function register()
51
-    {
52
-        return array(\T_PARENT);
53
-    }
43
+	/**
44
+	 * Returns an array of tokens this test wants to listen for.
45
+	 *
46
+	 * @since 9.2.0
47
+	 *
48
+	 * @return array
49
+	 */
50
+	public function register()
51
+	{
52
+		return array(\T_PARENT);
53
+	}
54 54
 
55
-    /**
56
-     * Processes this test, when one of its tokens is encountered.
57
-     *
58
-     * @since 9.2.0
59
-     *
60
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
61
-     * @param int                   $stackPtr  The position of the current token
62
-     *                                         in the stack passed in $tokens.
63
-     *
64
-     * @return void
65
-     */
66
-    public function process(File $phpcsFile, $stackPtr)
67
-    {
68
-        if ($this->supportsAbove('7.4') === false) {
69
-            return;
70
-        }
55
+	/**
56
+	 * Processes this test, when one of its tokens is encountered.
57
+	 *
58
+	 * @since 9.2.0
59
+	 *
60
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
61
+	 * @param int                   $stackPtr  The position of the current token
62
+	 *                                         in the stack passed in $tokens.
63
+	 *
64
+	 * @return void
65
+	 */
66
+	public function process(File $phpcsFile, $stackPtr)
67
+	{
68
+		if ($this->supportsAbove('7.4') === false) {
69
+			return;
70
+		}
71 71
 
72
-        $tokens = $phpcsFile->getTokens();
72
+		$tokens = $phpcsFile->getTokens();
73 73
 
74
-        if (empty($tokens[$stackPtr]['conditions']) === true) {
75
-            // Use within the global namespace. Not our concern.
76
-            return;
77
-        }
74
+		if (empty($tokens[$stackPtr]['conditions']) === true) {
75
+			// Use within the global namespace. Not our concern.
76
+			return;
77
+		}
78 78
 
79
-        /*
79
+		/*
80 80
          * Find the class within which this parent keyword is used.
81 81
          */
82
-        $conditions = $tokens[$stackPtr]['conditions'];
83
-        $conditions = array_reverse($conditions, true);
84
-        $classPtr   = false;
82
+		$conditions = $tokens[$stackPtr]['conditions'];
83
+		$conditions = array_reverse($conditions, true);
84
+		$classPtr   = false;
85 85
 
86
-        foreach ($conditions as $ptr => $type) {
87
-            if (isset($this->classScopeTokens[$tokens[$ptr]['type']])) {
88
-                $classPtr = $ptr;
89
-                break;
90
-            }
91
-        }
86
+		foreach ($conditions as $ptr => $type) {
87
+			if (isset($this->classScopeTokens[$tokens[$ptr]['type']])) {
88
+				$classPtr = $ptr;
89
+				break;
90
+			}
91
+		}
92 92
 
93
-        if ($classPtr === false) {
94
-            // Use outside of a class scope. Not our concern.
95
-            return;
96
-        }
93
+		if ($classPtr === false) {
94
+			// Use outside of a class scope. Not our concern.
95
+			return;
96
+		}
97 97
 
98
-        if (isset($tokens[$classPtr]['scope_opener']) === false) {
99
-            // No scope opener known. Probably a parse error.
100
-            return;
101
-        }
98
+		if (isset($tokens[$classPtr]['scope_opener']) === false) {
99
+			// No scope opener known. Probably a parse error.
100
+			return;
101
+		}
102 102
 
103
-        $extends = $phpcsFile->findNext(\T_EXTENDS, ($classPtr + 1), $tokens[$classPtr]['scope_opener']);
104
-        if ($extends !== false) {
105
-            // Class has a parent.
106
-            return;
107
-        }
103
+		$extends = $phpcsFile->findNext(\T_EXTENDS, ($classPtr + 1), $tokens[$classPtr]['scope_opener']);
104
+		if ($extends !== false) {
105
+			// Class has a parent.
106
+			return;
107
+		}
108 108
 
109
-        $phpcsFile->addError(
110
-            'Using "parent" inside a class without parent is deprecated since PHP 7.4',
111
-            $stackPtr,
112
-            'Deprecated'
113
-        );
114
-    }
109
+		$phpcsFile->addError(
110
+			'Using "parent" inside a class without parent is deprecated since PHP 7.4',
111
+			$stackPtr,
112
+			'Deprecated'
113
+		);
114
+	}
115 115
 }
Please login to merge, or discard this patch.
PHPCompatibility/Sniffs/Classes/NewAnonymousClassesSniff.php 1 patch
Indentation   +49 added lines, -49 removed lines patch added patch discarded remove patch
@@ -29,59 +29,59 @@
 block discarded – undo
29 29
 class NewAnonymousClassesSniff extends Sniff
30 30
 {
31 31
 
32
-    /**
33
-     * Tokens which in various PHP versions indicate the `class` keyword.
34
-     *
35
-     * The dedicated anonymous class token is added from the `register()`
36
-     * method if the token is available.
37
-     *
38
-     * @var array
39
-     */
40
-    private $indicators = array(
41
-        \T_CLASS => \T_CLASS,
42
-    );
32
+	/**
33
+	 * Tokens which in various PHP versions indicate the `class` keyword.
34
+	 *
35
+	 * The dedicated anonymous class token is added from the `register()`
36
+	 * method if the token is available.
37
+	 *
38
+	 * @var array
39
+	 */
40
+	private $indicators = array(
41
+		\T_CLASS => \T_CLASS,
42
+	);
43 43
 
44
-    /**
45
-     * Returns an array of tokens this test wants to listen for.
46
-     *
47
-     * @return array
48
-     */
49
-    public function register()
50
-    {
51
-        if (\defined('T_ANON_CLASS')) {
52
-            $this->indicators[\T_ANON_CLASS] = \T_ANON_CLASS;
53
-        }
44
+	/**
45
+	 * Returns an array of tokens this test wants to listen for.
46
+	 *
47
+	 * @return array
48
+	 */
49
+	public function register()
50
+	{
51
+		if (\defined('T_ANON_CLASS')) {
52
+			$this->indicators[\T_ANON_CLASS] = \T_ANON_CLASS;
53
+		}
54 54
 
55
-        return array(\T_NEW);
56
-    }
55
+		return array(\T_NEW);
56
+	}
57 57
 
58 58
 
59
-    /**
60
-     * Processes this test, when one of its tokens is encountered.
61
-     *
62
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
63
-     * @param int                   $stackPtr  The position of the current token in the
64
-     *                                         stack passed in $tokens.
65
-     *
66
-     * @return void
67
-     */
68
-    public function process(File $phpcsFile, $stackPtr)
69
-    {
70
-        if ($this->supportsBelow('5.6') === false) {
71
-            return;
72
-        }
59
+	/**
60
+	 * Processes this test, when one of its tokens is encountered.
61
+	 *
62
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
63
+	 * @param int                   $stackPtr  The position of the current token in the
64
+	 *                                         stack passed in $tokens.
65
+	 *
66
+	 * @return void
67
+	 */
68
+	public function process(File $phpcsFile, $stackPtr)
69
+	{
70
+		if ($this->supportsBelow('5.6') === false) {
71
+			return;
72
+		}
73 73
 
74
-        $tokens       = $phpcsFile->getTokens();
75
-        $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
76
-        if ($nextNonEmpty === false || isset($this->indicators[$tokens[$nextNonEmpty]['code']]) === false) {
77
-            return;
78
-        }
74
+		$tokens       = $phpcsFile->getTokens();
75
+		$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
76
+		if ($nextNonEmpty === false || isset($this->indicators[$tokens[$nextNonEmpty]['code']]) === false) {
77
+			return;
78
+		}
79 79
 
80
-        // Still here ? In that case, it is an anonymous class.
81
-        $phpcsFile->addError(
82
-            'Anonymous classes are not supported in PHP 5.6 or earlier',
83
-            $stackPtr,
84
-            'Found'
85
-        );
86
-    }
80
+		// Still here ? In that case, it is an anonymous class.
81
+		$phpcsFile->addError(
82
+			'Anonymous classes are not supported in PHP 5.6 or earlier',
83
+			$stackPtr,
84
+			'Found'
85
+		);
86
+	}
87 87
 }
Please login to merge, or discard this patch.
PHPCompatibility/Sniffs/Classes/NewConstVisibilitySniff.php 1 patch
Indentation   +42 added lines, -42 removed lines patch added patch discarded remove patch
@@ -28,51 +28,51 @@
 block discarded – undo
28 28
  */
29 29
 class NewConstVisibilitySniff extends Sniff
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_CONST);
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_CONST);
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->supportsBelow('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->supportsBelow('7.0') === false) {
53
+			return;
54
+		}
55 55
 
56
-        $tokens    = $phpcsFile->getTokens();
57
-        $prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true, null, true);
56
+		$tokens    = $phpcsFile->getTokens();
57
+		$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true, null, true);
58 58
 
59
-        // Is the previous token a visibility indicator ?
60
-        if ($prevToken === false || isset(Tokens::$scopeModifiers[$tokens[$prevToken]['code']]) === false) {
61
-            return;
62
-        }
59
+		// Is the previous token a visibility indicator ?
60
+		if ($prevToken === false || isset(Tokens::$scopeModifiers[$tokens[$prevToken]['code']]) === false) {
61
+			return;
62
+		}
63 63
 
64
-        // Is this a class constant ?
65
-        if ($this->isClassConstant($phpcsFile, $stackPtr) === false) {
66
-            // This may be a constant declaration in the global namespace with visibility,
67
-            // but that would throw a parse error, i.e. not our concern.
68
-            return;
69
-        }
64
+		// Is this a class constant ?
65
+		if ($this->isClassConstant($phpcsFile, $stackPtr) === false) {
66
+			// This may be a constant declaration in the global namespace with visibility,
67
+			// but that would throw a parse error, i.e. not our concern.
68
+			return;
69
+		}
70 70
 
71
-        $phpcsFile->addError(
72
-            'Visibility indicators for class constants are not supported in PHP 7.0 or earlier. Found "%s const"',
73
-            $stackPtr,
74
-            'Found',
75
-            array($tokens[$prevToken]['content'])
76
-        );
77
-    }
71
+		$phpcsFile->addError(
72
+			'Visibility indicators for class constants are not supported in PHP 7.0 or earlier. Found "%s const"',
73
+			$stackPtr,
74
+			'Found',
75
+			array($tokens[$prevToken]['content'])
76
+		);
77
+	}
78 78
 }
Please login to merge, or discard this patch.
php-compatibility/PHPCompatibility/Sniffs/Classes/NewClassesSniff.php 1 patch
Indentation   +820 added lines, -820 removed lines patch added patch discarded remove patch
@@ -24,824 +24,824 @@
 block discarded – undo
24 24
 class NewClassesSniff extends AbstractNewFeatureSniff
25 25
 {
26 26
 
27
-    /**
28
-     * A list of new classes, not present in older versions.
29
-     *
30
-     * The array lists : version number with false (not present) or true (present).
31
-     * If's sufficient to list the first version where the class appears.
32
-     *
33
-     * @var array(string => array(string => bool))
34
-     */
35
-    protected $newClasses = array(
36
-        'ArrayObject' => array(
37
-            '4.4' => false,
38
-            '5.0' => true,
39
-        ),
40
-        'ArrayIterator' => array(
41
-            '4.4' => false,
42
-            '5.0' => true,
43
-        ),
44
-        'CachingIterator' => array(
45
-            '4.4' => false,
46
-            '5.0' => true,
47
-        ),
48
-        'DirectoryIterator' => array(
49
-            '4.4' => false,
50
-            '5.0' => true,
51
-        ),
52
-        'RecursiveDirectoryIterator' => array(
53
-            '4.4' => false,
54
-            '5.0' => true,
55
-        ),
56
-        'RecursiveIteratorIterator' => array(
57
-            '4.4' => false,
58
-            '5.0' => true,
59
-        ),
60
-        'php_user_filter' => array(
61
-            '4.4' => false,
62
-            '5.0' => true,
63
-        ),
64
-        'tidy' => array(
65
-            '4.4' => false,
66
-            '5.0' => true,
67
-        ),
68
-
69
-        'SimpleXMLElement' => array(
70
-            '5.0.0' => false,
71
-            '5.0.1' => true,
72
-        ),
73
-        'tidyNode' => array(
74
-            '5.0.0' => false,
75
-            '5.0.1' => true,
76
-        ),
77
-
78
-        'libXMLError' => array(
79
-            '5.0' => false,
80
-            '5.1' => true,
81
-        ),
82
-        'PDO' => array(
83
-            '5.0' => false,
84
-            '5.1' => true,
85
-        ),
86
-        'PDOStatement' => array(
87
-            '5.0' => false,
88
-            '5.1' => true,
89
-        ),
90
-        'AppendIterator' => array(
91
-            '5.0' => false,
92
-            '5.1' => true,
93
-        ),
94
-        'EmptyIterator' => array(
95
-            '5.0' => false,
96
-            '5.1' => true,
97
-        ),
98
-        'FilterIterator' => array(
99
-            '5.0' => false,
100
-            '5.1' => true,
101
-        ),
102
-        'InfiniteIterator' => array(
103
-            '5.0' => false,
104
-            '5.1' => true,
105
-        ),
106
-        'IteratorIterator' => array(
107
-            '5.0' => false,
108
-            '5.1' => true,
109
-        ),
110
-        'LimitIterator' => array(
111
-            '5.0' => false,
112
-            '5.1' => true,
113
-        ),
114
-        'NoRewindIterator' => array(
115
-            '5.0' => false,
116
-            '5.1' => true,
117
-        ),
118
-        'ParentIterator' => array(
119
-            '5.0' => false,
120
-            '5.1' => true,
121
-        ),
122
-        'RecursiveArrayIterator' => array(
123
-            '5.0' => false,
124
-            '5.1' => true,
125
-        ),
126
-        'RecursiveCachingIterator' => array(
127
-            '5.0' => false,
128
-            '5.1' => true,
129
-        ),
130
-        'RecursiveFilterIterator' => array(
131
-            '5.0' => false,
132
-            '5.1' => true,
133
-        ),
134
-        'SimpleXMLIterator' => array(
135
-            '5.0' => false,
136
-            '5.1' => true,
137
-        ),
138
-        'SplFileObject' => array(
139
-            '5.0' => false,
140
-            '5.1' => true,
141
-        ),
142
-        'XMLReader' => array(
143
-            '5.0' => false,
144
-            '5.1' => true,
145
-        ),
146
-
147
-        'SplFileInfo' => array(
148
-            '5.1.1' => false,
149
-            '5.1.2' => true,
150
-        ),
151
-        'SplTempFileObject' => array(
152
-            '5.1.1' => false,
153
-            '5.1.2' => true,
154
-        ),
155
-        'XMLWriter' => array(
156
-            '5.1.1' => false,
157
-            '5.1.2' => true,
158
-        ),
159
-
160
-        'DateTime' => array(
161
-            '5.1' => false,
162
-            '5.2' => true,
163
-        ),
164
-        'DateTimeZone' => array(
165
-            '5.1' => false,
166
-            '5.2' => true,
167
-        ),
168
-        'RegexIterator' => array(
169
-            '5.1' => false,
170
-            '5.2' => true,
171
-        ),
172
-        'RecursiveRegexIterator' => array(
173
-            '5.1' => false,
174
-            '5.2' => true,
175
-        ),
176
-        'ReflectionFunctionAbstract' => array(
177
-            '5.1' => false,
178
-            '5.2' => true,
179
-        ),
180
-        'ZipArchive' => array(
181
-            '5.1' => false,
182
-            '5.2' => true,
183
-        ),
184
-
185
-        'Closure' => array(
186
-            '5.2' => false,
187
-            '5.3' => true,
188
-        ),
189
-        'DateInterval' => array(
190
-            '5.2' => false,
191
-            '5.3' => true,
192
-        ),
193
-        'DatePeriod' => array(
194
-            '5.2' => false,
195
-            '5.3' => true,
196
-        ),
197
-        'finfo' => array(
198
-            '5.2' => false,
199
-            '5.3' => true,
200
-        ),
201
-        'Collator' => array(
202
-            '5.2' => false,
203
-            '5.3' => true,
204
-        ),
205
-        'NumberFormatter' => array(
206
-            '5.2' => false,
207
-            '5.3' => true,
208
-        ),
209
-        'Locale' => array(
210
-            '5.2' => false,
211
-            '5.3' => true,
212
-        ),
213
-        'Normalizer' => array(
214
-            '5.2' => false,
215
-            '5.3' => true,
216
-        ),
217
-        'MessageFormatter' => array(
218
-            '5.2' => false,
219
-            '5.3' => true,
220
-        ),
221
-        'IntlDateFormatter' => array(
222
-            '5.2' => false,
223
-            '5.3' => true,
224
-        ),
225
-        'Phar' => array(
226
-            '5.2' => false,
227
-            '5.3' => true,
228
-        ),
229
-        'PharData' => array(
230
-            '5.2' => false,
231
-            '5.3' => true,
232
-        ),
233
-        'PharFileInfo' => array(
234
-            '5.2' => false,
235
-            '5.3' => true,
236
-        ),
237
-        'FilesystemIterator' => array(
238
-            '5.2' => false,
239
-            '5.3' => true,
240
-        ),
241
-        'GlobIterator' => array(
242
-            '5.2' => false,
243
-            '5.3' => true,
244
-        ),
245
-        'MultipleIterator' => array(
246
-            '5.2' => false,
247
-            '5.3' => true,
248
-        ),
249
-        'RecursiveTreeIterator' => array(
250
-            '5.2' => false,
251
-            '5.3' => true,
252
-        ),
253
-        'SplDoublyLinkedList' => array(
254
-            '5.2' => false,
255
-            '5.3' => true,
256
-        ),
257
-        'SplFixedArray' => array(
258
-            '5.2' => false,
259
-            '5.3' => true,
260
-        ),
261
-        'SplHeap' => array(
262
-            '5.2' => false,
263
-            '5.3' => true,
264
-        ),
265
-        'SplMaxHeap' => array(
266
-            '5.2' => false,
267
-            '5.3' => true,
268
-        ),
269
-        'SplMinHeap' => array(
270
-            '5.2' => false,
271
-            '5.3' => true,
272
-        ),
273
-        'SplObjectStorage' => array(
274
-            '5.2' => false,
275
-            '5.3' => true,
276
-        ),
277
-        'SplPriorityQueue' => array(
278
-            '5.2' => false,
279
-            '5.3' => true,
280
-        ),
281
-        'SplQueue' => array(
282
-            '5.2' => false,
283
-            '5.3' => true,
284
-        ),
285
-        'SplStack' => array(
286
-            '5.2' => false,
287
-            '5.3' => true,
288
-        ),
289
-
290
-        'ResourceBundle' => array(
291
-            '5.3.1' => false,
292
-            '5.3.2' => true,
293
-        ),
294
-
295
-        'CallbackFilterIterator' => array(
296
-            '5.3' => false,
297
-            '5.4' => true,
298
-        ),
299
-        'RecursiveCallbackFilterIterator' => array(
300
-            '5.3' => false,
301
-            '5.4' => true,
302
-        ),
303
-        'ReflectionZendExtension' => array(
304
-            '5.3' => false,
305
-            '5.4' => true,
306
-        ),
307
-        'SessionHandler' => array(
308
-            '5.3' => false,
309
-            '5.4' => true,
310
-        ),
311
-        'SNMP' => array(
312
-            '5.3' => false,
313
-            '5.4' => true,
314
-        ),
315
-        'Transliterator' => array(
316
-            '5.3' => false,
317
-            '5.4' => true,
318
-        ),
319
-        'Spoofchecker' => array(
320
-            '5.3' => false,
321
-            '5.4' => true,
322
-        ),
323
-
324
-        'Generator' => array(
325
-            '5.4' => false,
326
-            '5.5' => true,
327
-        ),
328
-        'CURLFile' => array(
329
-            '5.4' => false,
330
-            '5.5' => true,
331
-        ),
332
-        'DateTimeImmutable' => array(
333
-            '5.4' => false,
334
-            '5.5' => true,
335
-        ),
336
-        'IntlCalendar' => array(
337
-            '5.4' => false,
338
-            '5.5' => true,
339
-        ),
340
-        'IntlGregorianCalendar' => array(
341
-            '5.4' => false,
342
-            '5.5' => true,
343
-        ),
344
-        'IntlTimeZone' => array(
345
-            '5.4' => false,
346
-            '5.5' => true,
347
-        ),
348
-        'IntlBreakIterator' => array(
349
-            '5.4' => false,
350
-            '5.5' => true,
351
-        ),
352
-        'IntlRuleBasedBreakIterator' => array(
353
-            '5.4' => false,
354
-            '5.5' => true,
355
-        ),
356
-        'IntlCodePointBreakIterator' => array(
357
-            '5.4' => false,
358
-            '5.5' => true,
359
-        ),
360
-        'UConverter' => array(
361
-            '5.4' => false,
362
-            '5.5' => true,
363
-        ),
364
-
365
-        'GMP' => array(
366
-            '5.5' => false,
367
-            '5.6' => true,
368
-        ),
369
-
370
-        'IntlChar' => array(
371
-            '5.6' => false,
372
-            '7.0' => true,
373
-        ),
374
-        'ReflectionType' => array(
375
-            '5.6' => false,
376
-            '7.0' => true,
377
-        ),
378
-        'ReflectionGenerator' => array(
379
-            '5.6' => false,
380
-            '7.0' => true,
381
-        ),
382
-
383
-        'ReflectionClassConstant' => array(
384
-            '7.0' => false,
385
-            '7.1' => true,
386
-        ),
387
-
388
-    );
389
-
390
-    /**
391
-     * A list of new Exception classes, not present in older versions.
392
-     *
393
-     * The array lists : version number with false (not present) or true (present).
394
-     * If's sufficient to list the first version where the class appears.
395
-     *
396
-     * {@internal Classes listed here do not need to be added to the $newClasses
397
-     *            property as well.
398
-     *            This list is automatically added to the $newClasses property
399
-     *            in the `register()` method.}}
400
-     *
401
-     * {@internal Helper to update this list: https://3v4l.org/MhlUp}}
402
-     *
403
-     * @var array(string => array(string => bool))
404
-     */
405
-    protected $newExceptions = array(
406
-        'com_exception' => array(
407
-            '4.4' => false,
408
-            '5.0' => true,
409
-        ),
410
-        'DOMException' => array(
411
-            '4.4' => false,
412
-            '5.0' => true,
413
-        ),
414
-        'Exception' => array(
415
-            // According to the docs introduced in PHP 5.1, but this appears to be.
416
-            // an error.  Class was introduced with try/catch keywords in PHP 5.0.
417
-            '4.4' => false,
418
-            '5.0' => true,
419
-        ),
420
-        'ReflectionException' => array(
421
-            '4.4' => false,
422
-            '5.0' => true,
423
-        ),
424
-        'SoapFault' => array(
425
-            '4.4' => false,
426
-            '5.0' => true,
427
-        ),
428
-        'SQLiteException' => array(
429
-            '4.4' => false,
430
-            '5.0' => true,
431
-        ),
432
-
433
-        'ErrorException' => array(
434
-            '5.0' => false,
435
-            '5.1' => true,
436
-        ),
437
-        'BadFunctionCallException' => array(
438
-            '5.0' => false,
439
-            '5.1' => true,
440
-        ),
441
-        'BadMethodCallException' => array(
442
-            '5.0' => false,
443
-            '5.1' => true,
444
-        ),
445
-        'DomainException' => array(
446
-            '5.0' => false,
447
-            '5.1' => true,
448
-        ),
449
-        'InvalidArgumentException' => array(
450
-            '5.0' => false,
451
-            '5.1' => true,
452
-        ),
453
-        'LengthException' => array(
454
-            '5.0' => false,
455
-            '5.1' => true,
456
-        ),
457
-        'LogicException' => array(
458
-            '5.0' => false,
459
-            '5.1' => true,
460
-        ),
461
-        'mysqli_sql_exception' => array(
462
-            '5.0' => false,
463
-            '5.1' => true,
464
-        ),
465
-        'OutOfBoundsException' => array(
466
-            '5.0' => false,
467
-            '5.1' => true,
468
-        ),
469
-        'OutOfRangeException' => array(
470
-            '5.0' => false,
471
-            '5.1' => true,
472
-        ),
473
-        'OverflowException' => array(
474
-            '5.0' => false,
475
-            '5.1' => true,
476
-        ),
477
-        'PDOException' => array(
478
-            '5.0' => false,
479
-            '5.1' => true,
480
-        ),
481
-        'RangeException' => array(
482
-            '5.0' => false,
483
-            '5.1' => true,
484
-        ),
485
-        'RuntimeException' => array(
486
-            '5.0' => false,
487
-            '5.1' => true,
488
-        ),
489
-        'UnderflowException' => array(
490
-            '5.0' => false,
491
-            '5.1' => true,
492
-        ),
493
-        'UnexpectedValueException' => array(
494
-            '5.0' => false,
495
-            '5.1' => true,
496
-        ),
497
-
498
-        'PharException' => array(
499
-            '5.2' => false,
500
-            '5.3' => true,
501
-        ),
502
-
503
-        'SNMPException' => array(
504
-            '5.3' => false,
505
-            '5.4' => true,
506
-        ),
507
-
508
-        'IntlException' => array(
509
-            '5.4' => false,
510
-            '5.5' => true,
511
-        ),
512
-
513
-        'Error' => array(
514
-            '5.6' => false,
515
-            '7.0' => true,
516
-        ),
517
-        'ArithmeticError' => array(
518
-            '5.6' => false,
519
-            '7.0' => true,
520
-        ),
521
-        'AssertionError' => array(
522
-            '5.6' => false,
523
-            '7.0' => true,
524
-        ),
525
-        'DivisionByZeroError' => array(
526
-            '5.6' => false,
527
-            '7.0' => true,
528
-        ),
529
-        'ParseError' => array(
530
-            '5.6' => false,
531
-            '7.0' => true,
532
-        ),
533
-        'TypeError' => array(
534
-            '5.6' => false,
535
-            '7.0' => true,
536
-        ),
537
-        'ClosedGeneratorException' => array(
538
-            '5.6' => false,
539
-            '7.0' => true,
540
-        ),
541
-        'UI\Exception\InvalidArgumentException' => array(
542
-            '5.6' => false,
543
-            '7.0' => true,
544
-        ),
545
-        'UI\Exception\RuntimeException' => array(
546
-            '5.6' => false,
547
-            '7.0' => true,
548
-        ),
549
-
550
-        'ArgumentCountError' => array(
551
-            '7.0' => false,
552
-            '7.1' => true,
553
-        ),
554
-
555
-        'SodiumException' => array(
556
-            '7.1' => false,
557
-            '7.2' => true,
558
-        ),
559
-
560
-        'CompileError' => array(
561
-            '7.2' => false,
562
-            '7.3' => true,
563
-        ),
564
-        'JsonException' => array(
565
-            '7.2' => false,
566
-            '7.3' => true,
567
-        ),
568
-
569
-        'ReflectionReference' => array(
570
-            '7.3' => false,
571
-            '7.4' => true,
572
-        ),
573
-    );
574
-
575
-
576
-    /**
577
-     * Returns an array of tokens this test wants to listen for.
578
-     *
579
-     * @return array
580
-     */
581
-    public function register()
582
-    {
583
-        // Handle case-insensitivity of class names.
584
-        $this->newClasses    = $this->arrayKeysToLowercase($this->newClasses);
585
-        $this->newExceptions = $this->arrayKeysToLowercase($this->newExceptions);
586
-
587
-        // Add the Exception classes to the Classes list.
588
-        $this->newClasses = array_merge($this->newClasses, $this->newExceptions);
589
-
590
-        $targets = array(
591
-            \T_NEW,
592
-            \T_CLASS,
593
-            \T_DOUBLE_COLON,
594
-            \T_FUNCTION,
595
-            \T_CLOSURE,
596
-            \T_CATCH,
597
-        );
598
-
599
-        if (\defined('T_ANON_CLASS')) {
600
-            $targets[] = \T_ANON_CLASS;
601
-        }
602
-
603
-        if (\defined('T_RETURN_TYPE')) {
604
-            $targets[] = \T_RETURN_TYPE;
605
-        }
606
-
607
-        return $targets;
608
-    }
609
-
610
-
611
-    /**
612
-     * Processes this test, when one of its tokens is encountered.
613
-     *
614
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
615
-     * @param int                   $stackPtr  The position of the current token in
616
-     *                                         the stack passed in $tokens.
617
-     *
618
-     * @return void
619
-     */
620
-    public function process(File $phpcsFile, $stackPtr)
621
-    {
622
-        $tokens = $phpcsFile->getTokens();
623
-
624
-        switch ($tokens[$stackPtr]['type']) {
625
-            case 'T_FUNCTION':
626
-            case 'T_CLOSURE':
627
-                $this->processFunctionToken($phpcsFile, $stackPtr);
628
-
629
-                // Deal with older PHPCS version which don't recognize return type hints
630
-                // as well as newer PHPCS versions (3.3.0+) where the tokenization has changed.
631
-                $returnTypeHint = $this->getReturnTypeHintToken($phpcsFile, $stackPtr);
632
-                if ($returnTypeHint !== false) {
633
-                    $this->processReturnTypeToken($phpcsFile, $returnTypeHint);
634
-                }
635
-                break;
636
-
637
-            case 'T_CATCH':
638
-                $this->processCatchToken($phpcsFile, $stackPtr);
639
-                break;
640
-
641
-            case 'T_RETURN_TYPE':
642
-                $this->processReturnTypeToken($phpcsFile, $stackPtr);
643
-                break;
644
-
645
-            default:
646
-                $this->processSingularToken($phpcsFile, $stackPtr);
647
-                break;
648
-        }
649
-    }
650
-
651
-
652
-    /**
653
-     * Processes this test for when a token resulting in a singular class name is encountered.
654
-     *
655
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
656
-     * @param int                   $stackPtr  The position of the current token in
657
-     *                                         the stack passed in $tokens.
658
-     *
659
-     * @return void
660
-     */
661
-    private function processSingularToken(File $phpcsFile, $stackPtr)
662
-    {
663
-        $tokens      = $phpcsFile->getTokens();
664
-        $FQClassName = '';
665
-
666
-        if ($tokens[$stackPtr]['type'] === 'T_NEW') {
667
-            $FQClassName = $this->getFQClassNameFromNewToken($phpcsFile, $stackPtr);
668
-
669
-        } elseif ($tokens[$stackPtr]['type'] === 'T_CLASS' || $tokens[$stackPtr]['type'] === 'T_ANON_CLASS') {
670
-            $FQClassName = $this->getFQExtendedClassName($phpcsFile, $stackPtr);
671
-
672
-        } elseif ($tokens[$stackPtr]['type'] === 'T_DOUBLE_COLON') {
673
-            $FQClassName = $this->getFQClassNameFromDoubleColonToken($phpcsFile, $stackPtr);
674
-        }
675
-
676
-        if ($FQClassName === '') {
677
-            return;
678
-        }
679
-
680
-        $className   = substr($FQClassName, 1); // Remove global namespace indicator.
681
-        $classNameLc = strtolower($className);
682
-
683
-        if (isset($this->newClasses[$classNameLc]) === false) {
684
-            return;
685
-        }
686
-
687
-        $itemInfo = array(
688
-            'name'   => $className,
689
-            'nameLc' => $classNameLc,
690
-        );
691
-        $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
692
-    }
693
-
694
-
695
-    /**
696
-     * Processes this test for when a function token is encountered.
697
-     *
698
-     * - Detect new classes when used as a type hint.
699
-     *
700
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
701
-     * @param int                   $stackPtr  The position of the current token in
702
-     *                                         the stack passed in $tokens.
703
-     *
704
-     * @return void
705
-     */
706
-    private function processFunctionToken(File $phpcsFile, $stackPtr)
707
-    {
708
-        // Retrieve typehints stripped of global NS indicator and/or nullable indicator.
709
-        $typeHints = $this->getTypeHintsFromFunctionDeclaration($phpcsFile, $stackPtr);
710
-        if (empty($typeHints) || \is_array($typeHints) === false) {
711
-            return;
712
-        }
713
-
714
-        foreach ($typeHints as $hint) {
715
-
716
-            $typeHintLc = strtolower($hint);
717
-
718
-            if (isset($this->newClasses[$typeHintLc]) === true) {
719
-                $itemInfo = array(
720
-                    'name'   => $hint,
721
-                    'nameLc' => $typeHintLc,
722
-                );
723
-                $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
724
-            }
725
-        }
726
-    }
727
-
728
-
729
-    /**
730
-     * Processes this test for when a catch token is encountered.
731
-     *
732
-     * - Detect exceptions when used in a catch statement.
733
-     *
734
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
735
-     * @param int                   $stackPtr  The position of the current token in
736
-     *                                         the stack passed in $tokens.
737
-     *
738
-     * @return void
739
-     */
740
-    private function processCatchToken(File $phpcsFile, $stackPtr)
741
-    {
742
-        $tokens = $phpcsFile->getTokens();
743
-
744
-        // Bow out during live coding.
745
-        if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
746
-            return;
747
-        }
748
-
749
-        $opener = $tokens[$stackPtr]['parenthesis_opener'];
750
-        $closer = ($tokens[$stackPtr]['parenthesis_closer'] + 1);
751
-        $name   = '';
752
-        $listen = array(
753
-            // Parts of a (namespaced) class name.
754
-            \T_STRING              => true,
755
-            \T_NS_SEPARATOR        => true,
756
-            // End/split tokens.
757
-            \T_VARIABLE            => false,
758
-            \T_BITWISE_OR          => false,
759
-            \T_CLOSE_CURLY_BRACKET => false, // Shouldn't be needed as we expect a var before this.
760
-        );
761
-
762
-        for ($i = ($opener + 1); $i < $closer; $i++) {
763
-            if (isset($listen[$tokens[$i]['code']]) === false) {
764
-                continue;
765
-            }
766
-
767
-            if ($listen[$tokens[$i]['code']] === true) {
768
-                $name .= $tokens[$i]['content'];
769
-                continue;
770
-            } else {
771
-                if (empty($name) === true) {
772
-                    // Weird, we should have a name by the time we encounter a variable or |.
773
-                    // So this may be the closer.
774
-                    continue;
775
-                }
776
-
777
-                $name   = ltrim($name, '\\');
778
-                $nameLC = strtolower($name);
779
-
780
-                if (isset($this->newExceptions[$nameLC]) === true) {
781
-                    $itemInfo = array(
782
-                        'name'   => $name,
783
-                        'nameLc' => $nameLC,
784
-                    );
785
-                    $this->handleFeature($phpcsFile, $i, $itemInfo);
786
-                }
787
-
788
-                // Reset for a potential multi-catch.
789
-                $name = '';
790
-            }
791
-        }
792
-    }
793
-
794
-
795
-    /**
796
-     * Processes this test for when a return type token is encountered.
797
-     *
798
-     * - Detect new classes when used as a return type declaration.
799
-     *
800
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
801
-     * @param int                   $stackPtr  The position of the current token in
802
-     *                                         the stack passed in $tokens.
803
-     *
804
-     * @return void
805
-     */
806
-    private function processReturnTypeToken(File $phpcsFile, $stackPtr)
807
-    {
808
-        $returnTypeHint   = $this->getReturnTypeHintName($phpcsFile, $stackPtr);
809
-        $returnTypeHint   = ltrim($returnTypeHint, '\\');
810
-        $returnTypeHintLc = strtolower($returnTypeHint);
811
-
812
-        if (isset($this->newClasses[$returnTypeHintLc]) === false) {
813
-            return;
814
-        }
815
-
816
-        // Still here ? Then this is a return type declaration using a new class.
817
-        $itemInfo = array(
818
-            'name'   => $returnTypeHint,
819
-            'nameLc' => $returnTypeHintLc,
820
-        );
821
-        $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
822
-    }
823
-
824
-
825
-    /**
826
-     * Get the relevant sub-array for a specific item from a multi-dimensional array.
827
-     *
828
-     * @param array $itemInfo Base information about the item.
829
-     *
830
-     * @return array Version and other information about the item.
831
-     */
832
-    public function getItemArray(array $itemInfo)
833
-    {
834
-        return $this->newClasses[$itemInfo['nameLc']];
835
-    }
836
-
837
-
838
-    /**
839
-     * Get the error message template for this sniff.
840
-     *
841
-     * @return string
842
-     */
843
-    protected function getErrorMsgTemplate()
844
-    {
845
-        return 'The built-in class ' . parent::getErrorMsgTemplate();
846
-    }
27
+	/**
28
+	 * A list of new classes, not present in older versions.
29
+	 *
30
+	 * The array lists : version number with false (not present) or true (present).
31
+	 * If's sufficient to list the first version where the class appears.
32
+	 *
33
+	 * @var array(string => array(string => bool))
34
+	 */
35
+	protected $newClasses = array(
36
+		'ArrayObject' => array(
37
+			'4.4' => false,
38
+			'5.0' => true,
39
+		),
40
+		'ArrayIterator' => array(
41
+			'4.4' => false,
42
+			'5.0' => true,
43
+		),
44
+		'CachingIterator' => array(
45
+			'4.4' => false,
46
+			'5.0' => true,
47
+		),
48
+		'DirectoryIterator' => array(
49
+			'4.4' => false,
50
+			'5.0' => true,
51
+		),
52
+		'RecursiveDirectoryIterator' => array(
53
+			'4.4' => false,
54
+			'5.0' => true,
55
+		),
56
+		'RecursiveIteratorIterator' => array(
57
+			'4.4' => false,
58
+			'5.0' => true,
59
+		),
60
+		'php_user_filter' => array(
61
+			'4.4' => false,
62
+			'5.0' => true,
63
+		),
64
+		'tidy' => array(
65
+			'4.4' => false,
66
+			'5.0' => true,
67
+		),
68
+
69
+		'SimpleXMLElement' => array(
70
+			'5.0.0' => false,
71
+			'5.0.1' => true,
72
+		),
73
+		'tidyNode' => array(
74
+			'5.0.0' => false,
75
+			'5.0.1' => true,
76
+		),
77
+
78
+		'libXMLError' => array(
79
+			'5.0' => false,
80
+			'5.1' => true,
81
+		),
82
+		'PDO' => array(
83
+			'5.0' => false,
84
+			'5.1' => true,
85
+		),
86
+		'PDOStatement' => array(
87
+			'5.0' => false,
88
+			'5.1' => true,
89
+		),
90
+		'AppendIterator' => array(
91
+			'5.0' => false,
92
+			'5.1' => true,
93
+		),
94
+		'EmptyIterator' => array(
95
+			'5.0' => false,
96
+			'5.1' => true,
97
+		),
98
+		'FilterIterator' => array(
99
+			'5.0' => false,
100
+			'5.1' => true,
101
+		),
102
+		'InfiniteIterator' => array(
103
+			'5.0' => false,
104
+			'5.1' => true,
105
+		),
106
+		'IteratorIterator' => array(
107
+			'5.0' => false,
108
+			'5.1' => true,
109
+		),
110
+		'LimitIterator' => array(
111
+			'5.0' => false,
112
+			'5.1' => true,
113
+		),
114
+		'NoRewindIterator' => array(
115
+			'5.0' => false,
116
+			'5.1' => true,
117
+		),
118
+		'ParentIterator' => array(
119
+			'5.0' => false,
120
+			'5.1' => true,
121
+		),
122
+		'RecursiveArrayIterator' => array(
123
+			'5.0' => false,
124
+			'5.1' => true,
125
+		),
126
+		'RecursiveCachingIterator' => array(
127
+			'5.0' => false,
128
+			'5.1' => true,
129
+		),
130
+		'RecursiveFilterIterator' => array(
131
+			'5.0' => false,
132
+			'5.1' => true,
133
+		),
134
+		'SimpleXMLIterator' => array(
135
+			'5.0' => false,
136
+			'5.1' => true,
137
+		),
138
+		'SplFileObject' => array(
139
+			'5.0' => false,
140
+			'5.1' => true,
141
+		),
142
+		'XMLReader' => array(
143
+			'5.0' => false,
144
+			'5.1' => true,
145
+		),
146
+
147
+		'SplFileInfo' => array(
148
+			'5.1.1' => false,
149
+			'5.1.2' => true,
150
+		),
151
+		'SplTempFileObject' => array(
152
+			'5.1.1' => false,
153
+			'5.1.2' => true,
154
+		),
155
+		'XMLWriter' => array(
156
+			'5.1.1' => false,
157
+			'5.1.2' => true,
158
+		),
159
+
160
+		'DateTime' => array(
161
+			'5.1' => false,
162
+			'5.2' => true,
163
+		),
164
+		'DateTimeZone' => array(
165
+			'5.1' => false,
166
+			'5.2' => true,
167
+		),
168
+		'RegexIterator' => array(
169
+			'5.1' => false,
170
+			'5.2' => true,
171
+		),
172
+		'RecursiveRegexIterator' => array(
173
+			'5.1' => false,
174
+			'5.2' => true,
175
+		),
176
+		'ReflectionFunctionAbstract' => array(
177
+			'5.1' => false,
178
+			'5.2' => true,
179
+		),
180
+		'ZipArchive' => array(
181
+			'5.1' => false,
182
+			'5.2' => true,
183
+		),
184
+
185
+		'Closure' => array(
186
+			'5.2' => false,
187
+			'5.3' => true,
188
+		),
189
+		'DateInterval' => array(
190
+			'5.2' => false,
191
+			'5.3' => true,
192
+		),
193
+		'DatePeriod' => array(
194
+			'5.2' => false,
195
+			'5.3' => true,
196
+		),
197
+		'finfo' => array(
198
+			'5.2' => false,
199
+			'5.3' => true,
200
+		),
201
+		'Collator' => array(
202
+			'5.2' => false,
203
+			'5.3' => true,
204
+		),
205
+		'NumberFormatter' => array(
206
+			'5.2' => false,
207
+			'5.3' => true,
208
+		),
209
+		'Locale' => array(
210
+			'5.2' => false,
211
+			'5.3' => true,
212
+		),
213
+		'Normalizer' => array(
214
+			'5.2' => false,
215
+			'5.3' => true,
216
+		),
217
+		'MessageFormatter' => array(
218
+			'5.2' => false,
219
+			'5.3' => true,
220
+		),
221
+		'IntlDateFormatter' => array(
222
+			'5.2' => false,
223
+			'5.3' => true,
224
+		),
225
+		'Phar' => array(
226
+			'5.2' => false,
227
+			'5.3' => true,
228
+		),
229
+		'PharData' => array(
230
+			'5.2' => false,
231
+			'5.3' => true,
232
+		),
233
+		'PharFileInfo' => array(
234
+			'5.2' => false,
235
+			'5.3' => true,
236
+		),
237
+		'FilesystemIterator' => array(
238
+			'5.2' => false,
239
+			'5.3' => true,
240
+		),
241
+		'GlobIterator' => array(
242
+			'5.2' => false,
243
+			'5.3' => true,
244
+		),
245
+		'MultipleIterator' => array(
246
+			'5.2' => false,
247
+			'5.3' => true,
248
+		),
249
+		'RecursiveTreeIterator' => array(
250
+			'5.2' => false,
251
+			'5.3' => true,
252
+		),
253
+		'SplDoublyLinkedList' => array(
254
+			'5.2' => false,
255
+			'5.3' => true,
256
+		),
257
+		'SplFixedArray' => array(
258
+			'5.2' => false,
259
+			'5.3' => true,
260
+		),
261
+		'SplHeap' => array(
262
+			'5.2' => false,
263
+			'5.3' => true,
264
+		),
265
+		'SplMaxHeap' => array(
266
+			'5.2' => false,
267
+			'5.3' => true,
268
+		),
269
+		'SplMinHeap' => array(
270
+			'5.2' => false,
271
+			'5.3' => true,
272
+		),
273
+		'SplObjectStorage' => array(
274
+			'5.2' => false,
275
+			'5.3' => true,
276
+		),
277
+		'SplPriorityQueue' => array(
278
+			'5.2' => false,
279
+			'5.3' => true,
280
+		),
281
+		'SplQueue' => array(
282
+			'5.2' => false,
283
+			'5.3' => true,
284
+		),
285
+		'SplStack' => array(
286
+			'5.2' => false,
287
+			'5.3' => true,
288
+		),
289
+
290
+		'ResourceBundle' => array(
291
+			'5.3.1' => false,
292
+			'5.3.2' => true,
293
+		),
294
+
295
+		'CallbackFilterIterator' => array(
296
+			'5.3' => false,
297
+			'5.4' => true,
298
+		),
299
+		'RecursiveCallbackFilterIterator' => array(
300
+			'5.3' => false,
301
+			'5.4' => true,
302
+		),
303
+		'ReflectionZendExtension' => array(
304
+			'5.3' => false,
305
+			'5.4' => true,
306
+		),
307
+		'SessionHandler' => array(
308
+			'5.3' => false,
309
+			'5.4' => true,
310
+		),
311
+		'SNMP' => array(
312
+			'5.3' => false,
313
+			'5.4' => true,
314
+		),
315
+		'Transliterator' => array(
316
+			'5.3' => false,
317
+			'5.4' => true,
318
+		),
319
+		'Spoofchecker' => array(
320
+			'5.3' => false,
321
+			'5.4' => true,
322
+		),
323
+
324
+		'Generator' => array(
325
+			'5.4' => false,
326
+			'5.5' => true,
327
+		),
328
+		'CURLFile' => array(
329
+			'5.4' => false,
330
+			'5.5' => true,
331
+		),
332
+		'DateTimeImmutable' => array(
333
+			'5.4' => false,
334
+			'5.5' => true,
335
+		),
336
+		'IntlCalendar' => array(
337
+			'5.4' => false,
338
+			'5.5' => true,
339
+		),
340
+		'IntlGregorianCalendar' => array(
341
+			'5.4' => false,
342
+			'5.5' => true,
343
+		),
344
+		'IntlTimeZone' => array(
345
+			'5.4' => false,
346
+			'5.5' => true,
347
+		),
348
+		'IntlBreakIterator' => array(
349
+			'5.4' => false,
350
+			'5.5' => true,
351
+		),
352
+		'IntlRuleBasedBreakIterator' => array(
353
+			'5.4' => false,
354
+			'5.5' => true,
355
+		),
356
+		'IntlCodePointBreakIterator' => array(
357
+			'5.4' => false,
358
+			'5.5' => true,
359
+		),
360
+		'UConverter' => array(
361
+			'5.4' => false,
362
+			'5.5' => true,
363
+		),
364
+
365
+		'GMP' => array(
366
+			'5.5' => false,
367
+			'5.6' => true,
368
+		),
369
+
370
+		'IntlChar' => array(
371
+			'5.6' => false,
372
+			'7.0' => true,
373
+		),
374
+		'ReflectionType' => array(
375
+			'5.6' => false,
376
+			'7.0' => true,
377
+		),
378
+		'ReflectionGenerator' => array(
379
+			'5.6' => false,
380
+			'7.0' => true,
381
+		),
382
+
383
+		'ReflectionClassConstant' => array(
384
+			'7.0' => false,
385
+			'7.1' => true,
386
+		),
387
+
388
+	);
389
+
390
+	/**
391
+	 * A list of new Exception classes, not present in older versions.
392
+	 *
393
+	 * The array lists : version number with false (not present) or true (present).
394
+	 * If's sufficient to list the first version where the class appears.
395
+	 *
396
+	 * {@internal Classes listed here do not need to be added to the $newClasses
397
+	 *            property as well.
398
+	 *            This list is automatically added to the $newClasses property
399
+	 *            in the `register()` method.}}
400
+	 *
401
+	 * {@internal Helper to update this list: https://3v4l.org/MhlUp}}
402
+	 *
403
+	 * @var array(string => array(string => bool))
404
+	 */
405
+	protected $newExceptions = array(
406
+		'com_exception' => array(
407
+			'4.4' => false,
408
+			'5.0' => true,
409
+		),
410
+		'DOMException' => array(
411
+			'4.4' => false,
412
+			'5.0' => true,
413
+		),
414
+		'Exception' => array(
415
+			// According to the docs introduced in PHP 5.1, but this appears to be.
416
+			// an error.  Class was introduced with try/catch keywords in PHP 5.0.
417
+			'4.4' => false,
418
+			'5.0' => true,
419
+		),
420
+		'ReflectionException' => array(
421
+			'4.4' => false,
422
+			'5.0' => true,
423
+		),
424
+		'SoapFault' => array(
425
+			'4.4' => false,
426
+			'5.0' => true,
427
+		),
428
+		'SQLiteException' => array(
429
+			'4.4' => false,
430
+			'5.0' => true,
431
+		),
432
+
433
+		'ErrorException' => array(
434
+			'5.0' => false,
435
+			'5.1' => true,
436
+		),
437
+		'BadFunctionCallException' => array(
438
+			'5.0' => false,
439
+			'5.1' => true,
440
+		),
441
+		'BadMethodCallException' => array(
442
+			'5.0' => false,
443
+			'5.1' => true,
444
+		),
445
+		'DomainException' => array(
446
+			'5.0' => false,
447
+			'5.1' => true,
448
+		),
449
+		'InvalidArgumentException' => array(
450
+			'5.0' => false,
451
+			'5.1' => true,
452
+		),
453
+		'LengthException' => array(
454
+			'5.0' => false,
455
+			'5.1' => true,
456
+		),
457
+		'LogicException' => array(
458
+			'5.0' => false,
459
+			'5.1' => true,
460
+		),
461
+		'mysqli_sql_exception' => array(
462
+			'5.0' => false,
463
+			'5.1' => true,
464
+		),
465
+		'OutOfBoundsException' => array(
466
+			'5.0' => false,
467
+			'5.1' => true,
468
+		),
469
+		'OutOfRangeException' => array(
470
+			'5.0' => false,
471
+			'5.1' => true,
472
+		),
473
+		'OverflowException' => array(
474
+			'5.0' => false,
475
+			'5.1' => true,
476
+		),
477
+		'PDOException' => array(
478
+			'5.0' => false,
479
+			'5.1' => true,
480
+		),
481
+		'RangeException' => array(
482
+			'5.0' => false,
483
+			'5.1' => true,
484
+		),
485
+		'RuntimeException' => array(
486
+			'5.0' => false,
487
+			'5.1' => true,
488
+		),
489
+		'UnderflowException' => array(
490
+			'5.0' => false,
491
+			'5.1' => true,
492
+		),
493
+		'UnexpectedValueException' => array(
494
+			'5.0' => false,
495
+			'5.1' => true,
496
+		),
497
+
498
+		'PharException' => array(
499
+			'5.2' => false,
500
+			'5.3' => true,
501
+		),
502
+
503
+		'SNMPException' => array(
504
+			'5.3' => false,
505
+			'5.4' => true,
506
+		),
507
+
508
+		'IntlException' => array(
509
+			'5.4' => false,
510
+			'5.5' => true,
511
+		),
512
+
513
+		'Error' => array(
514
+			'5.6' => false,
515
+			'7.0' => true,
516
+		),
517
+		'ArithmeticError' => array(
518
+			'5.6' => false,
519
+			'7.0' => true,
520
+		),
521
+		'AssertionError' => array(
522
+			'5.6' => false,
523
+			'7.0' => true,
524
+		),
525
+		'DivisionByZeroError' => array(
526
+			'5.6' => false,
527
+			'7.0' => true,
528
+		),
529
+		'ParseError' => array(
530
+			'5.6' => false,
531
+			'7.0' => true,
532
+		),
533
+		'TypeError' => array(
534
+			'5.6' => false,
535
+			'7.0' => true,
536
+		),
537
+		'ClosedGeneratorException' => array(
538
+			'5.6' => false,
539
+			'7.0' => true,
540
+		),
541
+		'UI\Exception\InvalidArgumentException' => array(
542
+			'5.6' => false,
543
+			'7.0' => true,
544
+		),
545
+		'UI\Exception\RuntimeException' => array(
546
+			'5.6' => false,
547
+			'7.0' => true,
548
+		),
549
+
550
+		'ArgumentCountError' => array(
551
+			'7.0' => false,
552
+			'7.1' => true,
553
+		),
554
+
555
+		'SodiumException' => array(
556
+			'7.1' => false,
557
+			'7.2' => true,
558
+		),
559
+
560
+		'CompileError' => array(
561
+			'7.2' => false,
562
+			'7.3' => true,
563
+		),
564
+		'JsonException' => array(
565
+			'7.2' => false,
566
+			'7.3' => true,
567
+		),
568
+
569
+		'ReflectionReference' => array(
570
+			'7.3' => false,
571
+			'7.4' => true,
572
+		),
573
+	);
574
+
575
+
576
+	/**
577
+	 * Returns an array of tokens this test wants to listen for.
578
+	 *
579
+	 * @return array
580
+	 */
581
+	public function register()
582
+	{
583
+		// Handle case-insensitivity of class names.
584
+		$this->newClasses    = $this->arrayKeysToLowercase($this->newClasses);
585
+		$this->newExceptions = $this->arrayKeysToLowercase($this->newExceptions);
586
+
587
+		// Add the Exception classes to the Classes list.
588
+		$this->newClasses = array_merge($this->newClasses, $this->newExceptions);
589
+
590
+		$targets = array(
591
+			\T_NEW,
592
+			\T_CLASS,
593
+			\T_DOUBLE_COLON,
594
+			\T_FUNCTION,
595
+			\T_CLOSURE,
596
+			\T_CATCH,
597
+		);
598
+
599
+		if (\defined('T_ANON_CLASS')) {
600
+			$targets[] = \T_ANON_CLASS;
601
+		}
602
+
603
+		if (\defined('T_RETURN_TYPE')) {
604
+			$targets[] = \T_RETURN_TYPE;
605
+		}
606
+
607
+		return $targets;
608
+	}
609
+
610
+
611
+	/**
612
+	 * Processes this test, when one of its tokens is encountered.
613
+	 *
614
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
615
+	 * @param int                   $stackPtr  The position of the current token in
616
+	 *                                         the stack passed in $tokens.
617
+	 *
618
+	 * @return void
619
+	 */
620
+	public function process(File $phpcsFile, $stackPtr)
621
+	{
622
+		$tokens = $phpcsFile->getTokens();
623
+
624
+		switch ($tokens[$stackPtr]['type']) {
625
+			case 'T_FUNCTION':
626
+			case 'T_CLOSURE':
627
+				$this->processFunctionToken($phpcsFile, $stackPtr);
628
+
629
+				// Deal with older PHPCS version which don't recognize return type hints
630
+				// as well as newer PHPCS versions (3.3.0+) where the tokenization has changed.
631
+				$returnTypeHint = $this->getReturnTypeHintToken($phpcsFile, $stackPtr);
632
+				if ($returnTypeHint !== false) {
633
+					$this->processReturnTypeToken($phpcsFile, $returnTypeHint);
634
+				}
635
+				break;
636
+
637
+			case 'T_CATCH':
638
+				$this->processCatchToken($phpcsFile, $stackPtr);
639
+				break;
640
+
641
+			case 'T_RETURN_TYPE':
642
+				$this->processReturnTypeToken($phpcsFile, $stackPtr);
643
+				break;
644
+
645
+			default:
646
+				$this->processSingularToken($phpcsFile, $stackPtr);
647
+				break;
648
+		}
649
+	}
650
+
651
+
652
+	/**
653
+	 * Processes this test for when a token resulting in a singular class name is encountered.
654
+	 *
655
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
656
+	 * @param int                   $stackPtr  The position of the current token in
657
+	 *                                         the stack passed in $tokens.
658
+	 *
659
+	 * @return void
660
+	 */
661
+	private function processSingularToken(File $phpcsFile, $stackPtr)
662
+	{
663
+		$tokens      = $phpcsFile->getTokens();
664
+		$FQClassName = '';
665
+
666
+		if ($tokens[$stackPtr]['type'] === 'T_NEW') {
667
+			$FQClassName = $this->getFQClassNameFromNewToken($phpcsFile, $stackPtr);
668
+
669
+		} elseif ($tokens[$stackPtr]['type'] === 'T_CLASS' || $tokens[$stackPtr]['type'] === 'T_ANON_CLASS') {
670
+			$FQClassName = $this->getFQExtendedClassName($phpcsFile, $stackPtr);
671
+
672
+		} elseif ($tokens[$stackPtr]['type'] === 'T_DOUBLE_COLON') {
673
+			$FQClassName = $this->getFQClassNameFromDoubleColonToken($phpcsFile, $stackPtr);
674
+		}
675
+
676
+		if ($FQClassName === '') {
677
+			return;
678
+		}
679
+
680
+		$className   = substr($FQClassName, 1); // Remove global namespace indicator.
681
+		$classNameLc = strtolower($className);
682
+
683
+		if (isset($this->newClasses[$classNameLc]) === false) {
684
+			return;
685
+		}
686
+
687
+		$itemInfo = array(
688
+			'name'   => $className,
689
+			'nameLc' => $classNameLc,
690
+		);
691
+		$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
692
+	}
693
+
694
+
695
+	/**
696
+	 * Processes this test for when a function token is encountered.
697
+	 *
698
+	 * - Detect new classes when used as a type hint.
699
+	 *
700
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
701
+	 * @param int                   $stackPtr  The position of the current token in
702
+	 *                                         the stack passed in $tokens.
703
+	 *
704
+	 * @return void
705
+	 */
706
+	private function processFunctionToken(File $phpcsFile, $stackPtr)
707
+	{
708
+		// Retrieve typehints stripped of global NS indicator and/or nullable indicator.
709
+		$typeHints = $this->getTypeHintsFromFunctionDeclaration($phpcsFile, $stackPtr);
710
+		if (empty($typeHints) || \is_array($typeHints) === false) {
711
+			return;
712
+		}
713
+
714
+		foreach ($typeHints as $hint) {
715
+
716
+			$typeHintLc = strtolower($hint);
717
+
718
+			if (isset($this->newClasses[$typeHintLc]) === true) {
719
+				$itemInfo = array(
720
+					'name'   => $hint,
721
+					'nameLc' => $typeHintLc,
722
+				);
723
+				$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
724
+			}
725
+		}
726
+	}
727
+
728
+
729
+	/**
730
+	 * Processes this test for when a catch token is encountered.
731
+	 *
732
+	 * - Detect exceptions when used in a catch statement.
733
+	 *
734
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
735
+	 * @param int                   $stackPtr  The position of the current token in
736
+	 *                                         the stack passed in $tokens.
737
+	 *
738
+	 * @return void
739
+	 */
740
+	private function processCatchToken(File $phpcsFile, $stackPtr)
741
+	{
742
+		$tokens = $phpcsFile->getTokens();
743
+
744
+		// Bow out during live coding.
745
+		if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
746
+			return;
747
+		}
748
+
749
+		$opener = $tokens[$stackPtr]['parenthesis_opener'];
750
+		$closer = ($tokens[$stackPtr]['parenthesis_closer'] + 1);
751
+		$name   = '';
752
+		$listen = array(
753
+			// Parts of a (namespaced) class name.
754
+			\T_STRING              => true,
755
+			\T_NS_SEPARATOR        => true,
756
+			// End/split tokens.
757
+			\T_VARIABLE            => false,
758
+			\T_BITWISE_OR          => false,
759
+			\T_CLOSE_CURLY_BRACKET => false, // Shouldn't be needed as we expect a var before this.
760
+		);
761
+
762
+		for ($i = ($opener + 1); $i < $closer; $i++) {
763
+			if (isset($listen[$tokens[$i]['code']]) === false) {
764
+				continue;
765
+			}
766
+
767
+			if ($listen[$tokens[$i]['code']] === true) {
768
+				$name .= $tokens[$i]['content'];
769
+				continue;
770
+			} else {
771
+				if (empty($name) === true) {
772
+					// Weird, we should have a name by the time we encounter a variable or |.
773
+					// So this may be the closer.
774
+					continue;
775
+				}
776
+
777
+				$name   = ltrim($name, '\\');
778
+				$nameLC = strtolower($name);
779
+
780
+				if (isset($this->newExceptions[$nameLC]) === true) {
781
+					$itemInfo = array(
782
+						'name'   => $name,
783
+						'nameLc' => $nameLC,
784
+					);
785
+					$this->handleFeature($phpcsFile, $i, $itemInfo);
786
+				}
787
+
788
+				// Reset for a potential multi-catch.
789
+				$name = '';
790
+			}
791
+		}
792
+	}
793
+
794
+
795
+	/**
796
+	 * Processes this test for when a return type token is encountered.
797
+	 *
798
+	 * - Detect new classes when used as a return type declaration.
799
+	 *
800
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
801
+	 * @param int                   $stackPtr  The position of the current token in
802
+	 *                                         the stack passed in $tokens.
803
+	 *
804
+	 * @return void
805
+	 */
806
+	private function processReturnTypeToken(File $phpcsFile, $stackPtr)
807
+	{
808
+		$returnTypeHint   = $this->getReturnTypeHintName($phpcsFile, $stackPtr);
809
+		$returnTypeHint   = ltrim($returnTypeHint, '\\');
810
+		$returnTypeHintLc = strtolower($returnTypeHint);
811
+
812
+		if (isset($this->newClasses[$returnTypeHintLc]) === false) {
813
+			return;
814
+		}
815
+
816
+		// Still here ? Then this is a return type declaration using a new class.
817
+		$itemInfo = array(
818
+			'name'   => $returnTypeHint,
819
+			'nameLc' => $returnTypeHintLc,
820
+		);
821
+		$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
822
+	}
823
+
824
+
825
+	/**
826
+	 * Get the relevant sub-array for a specific item from a multi-dimensional array.
827
+	 *
828
+	 * @param array $itemInfo Base information about the item.
829
+	 *
830
+	 * @return array Version and other information about the item.
831
+	 */
832
+	public function getItemArray(array $itemInfo)
833
+	{
834
+		return $this->newClasses[$itemInfo['nameLc']];
835
+	}
836
+
837
+
838
+	/**
839
+	 * Get the error message template for this sniff.
840
+	 *
841
+	 * @return string
842
+	 */
843
+	protected function getErrorMsgTemplate()
844
+	{
845
+		return 'The built-in class ' . parent::getErrorMsgTemplate();
846
+	}
847 847
 }
Please login to merge, or discard this patch.
PHPCompatibility/Sniffs/Classes/NewTypedPropertiesSniff.php 1 patch
Indentation   +86 added lines, -86 removed lines patch added patch discarded remove patch
@@ -26,100 +26,100 @@
 block discarded – undo
26 26
 class NewTypedPropertiesSniff extends Sniff
27 27
 {
28 28
 
29
-    /**
30
-     * Valid property modifier keywords.
31
-     *
32
-     * @var array
33
-     */
34
-    private $modifierKeywords = array(
35
-        \T_PRIVATE   => \T_PRIVATE,
36
-        \T_PROTECTED => \T_PROTECTED,
37
-        \T_PUBLIC    => \T_PUBLIC,
38
-        \T_STATIC    => \T_STATIC,
39
-        \T_VAR       => \T_VAR,
40
-    );
29
+	/**
30
+	 * Valid property modifier keywords.
31
+	 *
32
+	 * @var array
33
+	 */
34
+	private $modifierKeywords = array(
35
+		\T_PRIVATE   => \T_PRIVATE,
36
+		\T_PROTECTED => \T_PROTECTED,
37
+		\T_PUBLIC    => \T_PUBLIC,
38
+		\T_STATIC    => \T_STATIC,
39
+		\T_VAR       => \T_VAR,
40
+	);
41 41
 
42 42
 
43
-    /**
44
-     * Returns an array of tokens this test wants to listen for.
45
-     *
46
-     * @return array
47
-     */
48
-    public function register()
49
-    {
50
-        return array(\T_VARIABLE);
51
-    }
43
+	/**
44
+	 * Returns an array of tokens this test wants to listen for.
45
+	 *
46
+	 * @return array
47
+	 */
48
+	public function register()
49
+	{
50
+		return array(\T_VARIABLE);
51
+	}
52 52
 
53
-    /**
54
-     * Processes this test, when one of its tokens is encountered.
55
-     *
56
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
57
-     * @param int                   $stackPtr  The position of the current token in the
58
-     *                                         stack passed in $tokens.
59
-     *
60
-     * @return int|void Integer stack pointer to skip forward or void to continue
61
-     *                  normal file processing.
62
-     */
63
-    public function process(File $phpcsFile, $stackPtr)
64
-    {
65
-        if ($this->isClassProperty($phpcsFile, $stackPtr) === false) {
66
-            return;
67
-        }
53
+	/**
54
+	 * Processes this test, when one of its tokens is encountered.
55
+	 *
56
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
57
+	 * @param int                   $stackPtr  The position of the current token in the
58
+	 *                                         stack passed in $tokens.
59
+	 *
60
+	 * @return int|void Integer stack pointer to skip forward or void to continue
61
+	 *                  normal file processing.
62
+	 */
63
+	public function process(File $phpcsFile, $stackPtr)
64
+	{
65
+		if ($this->isClassProperty($phpcsFile, $stackPtr) === false) {
66
+			return;
67
+		}
68 68
 
69
-        $find  = $this->modifierKeywords;
70
-        $find += array(
71
-            \T_SEMICOLON          => \T_SEMICOLON,
72
-            \T_OPEN_CURLY_BRACKET => \T_OPEN_CURLY_BRACKET,
73
-        );
69
+		$find  = $this->modifierKeywords;
70
+		$find += array(
71
+			\T_SEMICOLON          => \T_SEMICOLON,
72
+			\T_OPEN_CURLY_BRACKET => \T_OPEN_CURLY_BRACKET,
73
+		);
74 74
 
75
-        $tokens   = $phpcsFile->getTokens();
76
-        $modifier = $phpcsFile->findPrevious($find, ($stackPtr - 1));
77
-        if ($modifier === false
78
-            || $tokens[$modifier]['code'] === \T_SEMICOLON
79
-            || $tokens[$modifier]['code'] === \T_OPEN_CURLY_BRACKET
80
-        ) {
81
-            // Parse error. Ignore.
82
-            return;
83
-        }
75
+		$tokens   = $phpcsFile->getTokens();
76
+		$modifier = $phpcsFile->findPrevious($find, ($stackPtr - 1));
77
+		if ($modifier === false
78
+			|| $tokens[$modifier]['code'] === \T_SEMICOLON
79
+			|| $tokens[$modifier]['code'] === \T_OPEN_CURLY_BRACKET
80
+		) {
81
+			// Parse error. Ignore.
82
+			return;
83
+		}
84 84
 
85
-        $type = $phpcsFile->findNext(Tokens::$emptyTokens, ($modifier + 1), null, true);
86
-        if ($tokens[$type]['code'] === \T_VARIABLE) {
87
-            return;
88
-        }
85
+		$type = $phpcsFile->findNext(Tokens::$emptyTokens, ($modifier + 1), null, true);
86
+		if ($tokens[$type]['code'] === \T_VARIABLE) {
87
+			return;
88
+		}
89 89
 
90
-        // Still here ? In that case, this will be a typed property.
91
-        if ($this->supportsBelow('7.3') === true) {
92
-            $phpcsFile->addError(
93
-                'Typed properties are not supported in PHP 7.3 or earlier',
94
-                $type,
95
-                'Found'
96
-            );
97
-        }
90
+		// Still here ? In that case, this will be a typed property.
91
+		if ($this->supportsBelow('7.3') === true) {
92
+			$phpcsFile->addError(
93
+				'Typed properties are not supported in PHP 7.3 or earlier',
94
+				$type,
95
+				'Found'
96
+			);
97
+		}
98 98
 
99
-        if ($this->supportsAbove('7.4') === true) {
100
-            // Examine the type to verify it's valid.
101
-            if ($tokens[$type]['type'] === 'T_NULLABLE'
102
-                // Needed to support PHPCS < 3.5.0 which doesn't correct to the nullable token type yet.
103
-                || $tokens[$type]['code'] === \T_INLINE_THEN
104
-            ) {
105
-                $type = $phpcsFile->findNext(Tokens::$emptyTokens, ($type + 1), null, true);
106
-            }
99
+		if ($this->supportsAbove('7.4') === true) {
100
+			// Examine the type to verify it's valid.
101
+			if ($tokens[$type]['type'] === 'T_NULLABLE'
102
+				// Needed to support PHPCS < 3.5.0 which doesn't correct to the nullable token type yet.
103
+				|| $tokens[$type]['code'] === \T_INLINE_THEN
104
+			) {
105
+				$type = $phpcsFile->findNext(Tokens::$emptyTokens, ($type + 1), null, true);
106
+			}
107 107
 
108
-            $content = $tokens[$type]['content'];
109
-            if ($content === 'void' || $content === 'callable') {
110
-                $phpcsFile->addError(
111
-                    '%s is not supported as a type declaration for properties',
112
-                    $type,
113
-                    'InvalidType',
114
-                    array($content)
115
-                );
116
-            }
117
-        }
108
+			$content = $tokens[$type]['content'];
109
+			if ($content === 'void' || $content === 'callable') {
110
+				$phpcsFile->addError(
111
+					'%s is not supported as a type declaration for properties',
112
+					$type,
113
+					'InvalidType',
114
+					array($content)
115
+				);
116
+			}
117
+		}
118 118
 
119
-        $endOfStatement = $phpcsFile->findNext(\T_SEMICOLON, ($stackPtr + 1));
120
-        if ($endOfStatement !== false) {
121
-            // Don't throw the same error multiple times for multi-property declarations.
122
-            return ($endOfStatement + 1);
123
-        }
124
-    }
119
+		$endOfStatement = $phpcsFile->findNext(\T_SEMICOLON, ($stackPtr + 1));
120
+		if ($endOfStatement !== false) {
121
+			// Don't throw the same error multiple times for multi-property declarations.
122
+			return ($endOfStatement + 1);
123
+		}
124
+	}
125 125
 }
Please login to merge, or discard this patch.
PHPCompatibility/Sniffs/Classes/ForbiddenAbstractPrivateMethodsSniff.php 1 patch
Indentation   +53 added lines, -53 removed lines patch added patch discarded remove patch
@@ -29,62 +29,62 @@
 block discarded – undo
29 29
 class ForbiddenAbstractPrivateMethodsSniff extends Sniff
30 30
 {
31 31
 
32
-    /**
33
-     * Valid scopes to check for abstract private methods.
34
-     *
35
-     * @since 9.2.0
36
-     *
37
-     * @var array
38
-     */
39
-    public $ooScopeTokens = array(
40
-        'T_CLASS'      => true,
41
-        'T_TRAIT'      => true,
42
-        'T_ANON_CLASS' => true,
43
-    );
32
+	/**
33
+	 * Valid scopes to check for abstract private methods.
34
+	 *
35
+	 * @since 9.2.0
36
+	 *
37
+	 * @var array
38
+	 */
39
+	public $ooScopeTokens = array(
40
+		'T_CLASS'      => true,
41
+		'T_TRAIT'      => true,
42
+		'T_ANON_CLASS' => true,
43
+	);
44 44
 
45
-    /**
46
-     * Returns an array of tokens this test wants to listen for.
47
-     *
48
-     * @since 9.2.0
49
-     *
50
-     * @return array
51
-     */
52
-    public function register()
53
-    {
54
-        return array(\T_FUNCTION);
55
-    }
45
+	/**
46
+	 * Returns an array of tokens this test wants to listen for.
47
+	 *
48
+	 * @since 9.2.0
49
+	 *
50
+	 * @return array
51
+	 */
52
+	public function register()
53
+	{
54
+		return array(\T_FUNCTION);
55
+	}
56 56
 
57
-    /**
58
-     * Processes this test, when one of its tokens is encountered.
59
-     *
60
-     * @since 9.2.0
61
-     *
62
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
63
-     * @param int                   $stackPtr  The position of the current token
64
-     *                                         in the stack passed in $tokens.
65
-     *
66
-     * @return void
67
-     */
68
-    public function process(File $phpcsFile, $stackPtr)
69
-    {
70
-        if ($this->supportsAbove('5.1') === false) {
71
-            return;
72
-        }
57
+	/**
58
+	 * Processes this test, when one of its tokens is encountered.
59
+	 *
60
+	 * @since 9.2.0
61
+	 *
62
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
63
+	 * @param int                   $stackPtr  The position of the current token
64
+	 *                                         in the stack passed in $tokens.
65
+	 *
66
+	 * @return void
67
+	 */
68
+	public function process(File $phpcsFile, $stackPtr)
69
+	{
70
+		if ($this->supportsAbove('5.1') === false) {
71
+			return;
72
+		}
73 73
 
74
-        if ($this->validDirectScope($phpcsFile, $stackPtr, $this->ooScopeTokens) === false) {
75
-            // Function, not method.
76
-            return;
77
-        }
74
+		if ($this->validDirectScope($phpcsFile, $stackPtr, $this->ooScopeTokens) === false) {
75
+			// Function, not method.
76
+			return;
77
+		}
78 78
 
79
-        $properties = $phpcsFile->getMethodProperties($stackPtr);
80
-        if ($properties['scope'] !== 'private' || $properties['is_abstract'] !== true) {
81
-            return;
82
-        }
79
+		$properties = $phpcsFile->getMethodProperties($stackPtr);
80
+		if ($properties['scope'] !== 'private' || $properties['is_abstract'] !== true) {
81
+			return;
82
+		}
83 83
 
84
-        $phpcsFile->addError(
85
-            'Abstract methods cannot be declared as private since PHP 5.1',
86
-            $stackPtr,
87
-            'Found'
88
-        );
89
-    }
84
+		$phpcsFile->addError(
85
+			'Abstract methods cannot be declared as private since PHP 5.1',
86
+			$stackPtr,
87
+			'Found'
88
+		);
89
+	}
90 90
 }
Please login to merge, or discard this patch.
PHPCompatibility/Sniffs/Classes/NewLateStaticBindingSniff.php 1 patch
Indentation   +44 added lines, -44 removed lines patch added patch discarded remove patch
@@ -26,54 +26,54 @@
 block discarded – undo
26 26
  */
27 27
 class NewLateStaticBindingSniff extends Sniff
28 28
 {
29
-    /**
30
-     * Returns an array of tokens this test wants to listen for.
31
-     *
32
-     * @return array
33
-     */
34
-    public function register()
35
-    {
36
-        return array(\T_STATIC);
37
-    }
29
+	/**
30
+	 * Returns an array of tokens this test wants to listen for.
31
+	 *
32
+	 * @return array
33
+	 */
34
+	public function register()
35
+	{
36
+		return array(\T_STATIC);
37
+	}
38 38
 
39 39
 
40
-    /**
41
-     * Processes this test, when one of its tokens is encountered.
42
-     *
43
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
44
-     * @param int                   $stackPtr  The position of the current token in the
45
-     *                                         stack passed in $tokens.
46
-     *
47
-     * @return void
48
-     */
49
-    public function process(File $phpcsFile, $stackPtr)
50
-    {
51
-        $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
52
-        if ($nextNonEmpty === false) {
53
-            return;
54
-        }
40
+	/**
41
+	 * Processes this test, when one of its tokens is encountered.
42
+	 *
43
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
44
+	 * @param int                   $stackPtr  The position of the current token in the
45
+	 *                                         stack passed in $tokens.
46
+	 *
47
+	 * @return void
48
+	 */
49
+	public function process(File $phpcsFile, $stackPtr)
50
+	{
51
+		$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
52
+		if ($nextNonEmpty === false) {
53
+			return;
54
+		}
55 55
 
56
-        $tokens = $phpcsFile->getTokens();
57
-        if ($tokens[$nextNonEmpty]['code'] !== \T_DOUBLE_COLON) {
58
-            return;
59
-        }
56
+		$tokens = $phpcsFile->getTokens();
57
+		if ($tokens[$nextNonEmpty]['code'] !== \T_DOUBLE_COLON) {
58
+			return;
59
+		}
60 60
 
61
-        $inClass = $this->inClassScope($phpcsFile, $stackPtr, false);
61
+		$inClass = $this->inClassScope($phpcsFile, $stackPtr, false);
62 62
 
63
-        if ($inClass === true && $this->supportsBelow('5.2') === true) {
64
-            $phpcsFile->addError(
65
-                'Late static binding is not supported in PHP 5.2 or earlier.',
66
-                $stackPtr,
67
-                'Found'
68
-            );
69
-        }
63
+		if ($inClass === true && $this->supportsBelow('5.2') === true) {
64
+			$phpcsFile->addError(
65
+				'Late static binding is not supported in PHP 5.2 or earlier.',
66
+				$stackPtr,
67
+				'Found'
68
+			);
69
+		}
70 70
 
71
-        if ($inClass === false) {
72
-            $phpcsFile->addError(
73
-                'Late static binding is not supported outside of class scope.',
74
-                $stackPtr,
75
-                'OutsideClassScope'
76
-            );
77
-        }
78
-    }
71
+		if ($inClass === false) {
72
+			$phpcsFile->addError(
73
+				'Late static binding is not supported outside of class scope.',
74
+				$stackPtr,
75
+				'OutsideClassScope'
76
+			);
77
+		}
78
+	}
79 79
 }
Please login to merge, or discard this patch.
PHPCompatibility/Sniffs/Lists/ForbiddenEmptyListAssignmentSniff.php 1 patch
Indentation   +70 added lines, -70 removed lines patch added patch discarded remove patch
@@ -29,83 +29,83 @@
 block discarded – undo
29 29
 class ForbiddenEmptyListAssignmentSniff extends Sniff
30 30
 {
31 31
 
32
-    /**
33
-     * List of tokens to disregard when determining whether the list() is empty.
34
-     *
35
-     * @var array
36
-     */
37
-    protected $ignoreTokens = array();
32
+	/**
33
+	 * List of tokens to disregard when determining whether the list() is empty.
34
+	 *
35
+	 * @var array
36
+	 */
37
+	protected $ignoreTokens = array();
38 38
 
39
-    /**
40
-     * Returns an array of tokens this test wants to listen for.
41
-     *
42
-     * @return array
43
-     */
44
-    public function register()
45
-    {
46
-        // Set up a list of tokens to disregard when determining whether the list() is empty.
47
-        // Only needs to be set up once.
48
-        $this->ignoreTokens                       = Tokens::$emptyTokens;
49
-        $this->ignoreTokens[\T_COMMA]             = \T_COMMA;
50
-        $this->ignoreTokens[\T_OPEN_PARENTHESIS]  = \T_OPEN_PARENTHESIS;
51
-        $this->ignoreTokens[\T_CLOSE_PARENTHESIS] = \T_CLOSE_PARENTHESIS;
39
+	/**
40
+	 * Returns an array of tokens this test wants to listen for.
41
+	 *
42
+	 * @return array
43
+	 */
44
+	public function register()
45
+	{
46
+		// Set up a list of tokens to disregard when determining whether the list() is empty.
47
+		// Only needs to be set up once.
48
+		$this->ignoreTokens                       = Tokens::$emptyTokens;
49
+		$this->ignoreTokens[\T_COMMA]             = \T_COMMA;
50
+		$this->ignoreTokens[\T_OPEN_PARENTHESIS]  = \T_OPEN_PARENTHESIS;
51
+		$this->ignoreTokens[\T_CLOSE_PARENTHESIS] = \T_CLOSE_PARENTHESIS;
52 52
 
53
-        return array(
54
-            \T_LIST,
55
-            \T_OPEN_SHORT_ARRAY,
56
-        );
57
-    }
53
+		return array(
54
+			\T_LIST,
55
+			\T_OPEN_SHORT_ARRAY,
56
+		);
57
+	}
58 58
 
59
-    /**
60
-     * Processes this test, when one of its tokens is encountered.
61
-     *
62
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
63
-     * @param int                   $stackPtr  The position of the current token in the
64
-     *                                         stack passed in $tokens.
65
-     *
66
-     * @return void
67
-     */
68
-    public function process(File $phpcsFile, $stackPtr)
69
-    {
70
-        if ($this->supportsAbove('7.0') === false) {
71
-            return;
72
-        }
59
+	/**
60
+	 * Processes this test, when one of its tokens is encountered.
61
+	 *
62
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
63
+	 * @param int                   $stackPtr  The position of the current token in the
64
+	 *                                         stack passed in $tokens.
65
+	 *
66
+	 * @return void
67
+	 */
68
+	public function process(File $phpcsFile, $stackPtr)
69
+	{
70
+		if ($this->supportsAbove('7.0') === false) {
71
+			return;
72
+		}
73 73
 
74
-        $tokens = $phpcsFile->getTokens();
74
+		$tokens = $phpcsFile->getTokens();
75 75
 
76
-        if ($tokens[$stackPtr]['code'] === \T_OPEN_SHORT_ARRAY) {
77
-            if ($this->isShortList($phpcsFile, $stackPtr) === false) {
78
-                return;
79
-            }
76
+		if ($tokens[$stackPtr]['code'] === \T_OPEN_SHORT_ARRAY) {
77
+			if ($this->isShortList($phpcsFile, $stackPtr) === false) {
78
+				return;
79
+			}
80 80
 
81
-            $open  = $stackPtr;
82
-            $close = $tokens[$stackPtr]['bracket_closer'];
83
-        } else {
84
-            // T_LIST.
85
-            $open = $phpcsFile->findNext(\T_OPEN_PARENTHESIS, $stackPtr, null, false, null, true);
86
-            if ($open === false || isset($tokens[$open]['parenthesis_closer']) === false) {
87
-                return;
88
-            }
81
+			$open  = $stackPtr;
82
+			$close = $tokens[$stackPtr]['bracket_closer'];
83
+		} else {
84
+			// T_LIST.
85
+			$open = $phpcsFile->findNext(\T_OPEN_PARENTHESIS, $stackPtr, null, false, null, true);
86
+			if ($open === false || isset($tokens[$open]['parenthesis_closer']) === false) {
87
+				return;
88
+			}
89 89
 
90
-            $close = $tokens[$open]['parenthesis_closer'];
91
-        }
90
+			$close = $tokens[$open]['parenthesis_closer'];
91
+		}
92 92
 
93
-        $error = true;
94
-        if (($close - $open) > 1) {
95
-            for ($cnt = $open + 1; $cnt < $close; $cnt++) {
96
-                if (isset($this->ignoreTokens[$tokens[$cnt]['code']]) === false) {
97
-                    $error = false;
98
-                    break;
99
-                }
100
-            }
101
-        }
93
+		$error = true;
94
+		if (($close - $open) > 1) {
95
+			for ($cnt = $open + 1; $cnt < $close; $cnt++) {
96
+				if (isset($this->ignoreTokens[$tokens[$cnt]['code']]) === false) {
97
+					$error = false;
98
+					break;
99
+				}
100
+			}
101
+		}
102 102
 
103
-        if ($error === true) {
104
-            $phpcsFile->addError(
105
-                'Empty list() assignments are not allowed since PHP 7.0',
106
-                $stackPtr,
107
-                'Found'
108
-            );
109
-        }
110
-    }
103
+		if ($error === true) {
104
+			$phpcsFile->addError(
105
+				'Empty list() assignments are not allowed since PHP 7.0',
106
+				$stackPtr,
107
+				'Found'
108
+			);
109
+		}
110
+	}
111 111
 }
Please login to merge, or discard this patch.
php-compatibility/PHPCompatibility/Sniffs/Lists/NewKeyedListSniff.php 1 patch
Indentation   +180 added lines, -180 removed lines patch added patch discarded remove patch
@@ -28,184 +28,184 @@
 block discarded – undo
28 28
  */
29 29
 class NewKeyedListSniff extends Sniff
30 30
 {
31
-    /**
32
-     * Tokens which represent the start of a list construct.
33
-     *
34
-     * @var array
35
-     */
36
-    protected $sniffTargets =  array(
37
-        \T_LIST             => \T_LIST,
38
-        \T_OPEN_SHORT_ARRAY => \T_OPEN_SHORT_ARRAY,
39
-    );
40
-
41
-    /**
42
-     * The token(s) within the list construct which is being targeted.
43
-     *
44
-     * @var array
45
-     */
46
-    protected $targetsInList = array(
47
-        \T_DOUBLE_ARROW => \T_DOUBLE_ARROW,
48
-    );
49
-
50
-    /**
51
-     * All tokens needed to walk through the list construct and
52
-     * determine whether the target token is contained within.
53
-     *
54
-     * Set by the setUpAllTargets() method which is called from within register().
55
-     *
56
-     * @var array
57
-     */
58
-    protected $allTargets;
59
-
60
-
61
-    /**
62
-     * Returns an array of tokens this test wants to listen for.
63
-     *
64
-     * @return array
65
-     */
66
-    public function register()
67
-    {
68
-        $this->setUpAllTargets();
69
-
70
-        return $this->sniffTargets;
71
-    }
72
-
73
-    /**
74
-     * Prepare the $allTargets array only once.
75
-     *
76
-     * @return array
77
-     */
78
-    public function setUpAllTargets()
79
-    {
80
-        $this->allTargets = $this->sniffTargets + $this->targetsInList;
81
-    }
82
-
83
-    /**
84
-     * Do a version check to determine if this sniff needs to run at all.
85
-     *
86
-     * @return bool
87
-     */
88
-    protected function bowOutEarly()
89
-    {
90
-        return ($this->supportsBelow('7.0') === false);
91
-    }
92
-
93
-    /**
94
-     * Processes this test, when one of its tokens is encountered.
95
-     *
96
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
97
-     * @param int                   $stackPtr  The position of the current token in the
98
-     *                                         stack passed in $tokens.
99
-     *
100
-     * @return void
101
-     */
102
-    public function process(File $phpcsFile, $stackPtr)
103
-    {
104
-        if ($this->bowOutEarly() === true) {
105
-            return;
106
-        }
107
-
108
-        $tokens = $phpcsFile->getTokens();
109
-
110
-        if ($tokens[$stackPtr]['code'] === \T_OPEN_SHORT_ARRAY
111
-            && $this->isShortList($phpcsFile, $stackPtr) === false
112
-        ) {
113
-            // Short array, not short list.
114
-            return;
115
-        }
116
-
117
-        if ($tokens[$stackPtr]['code'] === \T_LIST) {
118
-            $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
119
-            if ($nextNonEmpty === false
120
-                || $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS
121
-                || isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false
122
-            ) {
123
-                // Parse error or live coding.
124
-                return;
125
-            }
126
-
127
-            $opener = $nextNonEmpty;
128
-            $closer = $tokens[$nextNonEmpty]['parenthesis_closer'];
129
-        } else {
130
-            // Short list syntax.
131
-            $opener = $stackPtr;
132
-
133
-            if (isset($tokens[$stackPtr]['bracket_closer'])) {
134
-                $closer = $tokens[$stackPtr]['bracket_closer'];
135
-            }
136
-        }
137
-
138
-        if (isset($opener, $closer) === false) {
139
-            return;
140
-        }
141
-
142
-        $this->examineList($phpcsFile, $opener, $closer);
143
-    }
144
-
145
-
146
-    /**
147
-     * Examine the contents of a list construct to determine whether an error needs to be thrown.
148
-     *
149
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
150
-     * @param int                   $opener    The position of the list open token.
151
-     * @param int                   $closer    The position of the list close token.
152
-     *
153
-     * @return void
154
-     */
155
-    protected function examineList(File $phpcsFile, $opener, $closer)
156
-    {
157
-        $start = $opener;
158
-        while (($start = $this->hasTargetInList($phpcsFile, $start, $closer)) !== false) {
159
-            $phpcsFile->addError(
160
-                'Specifying keys in list constructs is not supported in PHP 7.0 or earlier.',
161
-                $start,
162
-                'Found'
163
-            );
164
-        }
165
-    }
166
-
167
-
168
-    /**
169
-     * Check whether a certain target token exists within a list construct.
170
-     *
171
-     * Skips past nested list constructs, so these can be examined based on their own token.
172
-     *
173
-     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
174
-     * @param int                   $start     The position of the list open token or a token
175
-     *                                         within the list to start (resume) the examination from.
176
-     * @param int                   $closer    The position of the list close token.
177
-     *
178
-     * @return int|bool Stack pointer to the target token if encountered. False otherwise.
179
-     */
180
-    protected function hasTargetInList(File $phpcsFile, $start, $closer)
181
-    {
182
-        $tokens = $phpcsFile->getTokens();
183
-
184
-        for ($i = ($start + 1); $i < $closer; $i++) {
185
-            if (isset($this->allTargets[$tokens[$i]['code']]) === false) {
186
-                continue;
187
-            }
188
-
189
-            if (isset($this->targetsInList[$tokens[$i]['code']]) === true) {
190
-                return $i;
191
-            }
192
-
193
-            // Skip past nested list constructs.
194
-            if ($tokens[$i]['code'] === \T_LIST) {
195
-                $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true);
196
-                if ($nextNonEmpty !== false
197
-                    && $tokens[$nextNonEmpty]['code'] === \T_OPEN_PARENTHESIS
198
-                    && isset($tokens[$nextNonEmpty]['parenthesis_closer']) === true
199
-                ) {
200
-                    $i = $tokens[$nextNonEmpty]['parenthesis_closer'];
201
-                }
202
-            } elseif ($tokens[$i]['code'] === \T_OPEN_SHORT_ARRAY
203
-                && isset($tokens[$i]['bracket_closer'])
204
-            ) {
205
-                $i = $tokens[$i]['bracket_closer'];
206
-            }
207
-        }
208
-
209
-        return false;
210
-    }
31
+	/**
32
+	 * Tokens which represent the start of a list construct.
33
+	 *
34
+	 * @var array
35
+	 */
36
+	protected $sniffTargets =  array(
37
+		\T_LIST             => \T_LIST,
38
+		\T_OPEN_SHORT_ARRAY => \T_OPEN_SHORT_ARRAY,
39
+	);
40
+
41
+	/**
42
+	 * The token(s) within the list construct which is being targeted.
43
+	 *
44
+	 * @var array
45
+	 */
46
+	protected $targetsInList = array(
47
+		\T_DOUBLE_ARROW => \T_DOUBLE_ARROW,
48
+	);
49
+
50
+	/**
51
+	 * All tokens needed to walk through the list construct and
52
+	 * determine whether the target token is contained within.
53
+	 *
54
+	 * Set by the setUpAllTargets() method which is called from within register().
55
+	 *
56
+	 * @var array
57
+	 */
58
+	protected $allTargets;
59
+
60
+
61
+	/**
62
+	 * Returns an array of tokens this test wants to listen for.
63
+	 *
64
+	 * @return array
65
+	 */
66
+	public function register()
67
+	{
68
+		$this->setUpAllTargets();
69
+
70
+		return $this->sniffTargets;
71
+	}
72
+
73
+	/**
74
+	 * Prepare the $allTargets array only once.
75
+	 *
76
+	 * @return array
77
+	 */
78
+	public function setUpAllTargets()
79
+	{
80
+		$this->allTargets = $this->sniffTargets + $this->targetsInList;
81
+	}
82
+
83
+	/**
84
+	 * Do a version check to determine if this sniff needs to run at all.
85
+	 *
86
+	 * @return bool
87
+	 */
88
+	protected function bowOutEarly()
89
+	{
90
+		return ($this->supportsBelow('7.0') === false);
91
+	}
92
+
93
+	/**
94
+	 * Processes this test, when one of its tokens is encountered.
95
+	 *
96
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
97
+	 * @param int                   $stackPtr  The position of the current token in the
98
+	 *                                         stack passed in $tokens.
99
+	 *
100
+	 * @return void
101
+	 */
102
+	public function process(File $phpcsFile, $stackPtr)
103
+	{
104
+		if ($this->bowOutEarly() === true) {
105
+			return;
106
+		}
107
+
108
+		$tokens = $phpcsFile->getTokens();
109
+
110
+		if ($tokens[$stackPtr]['code'] === \T_OPEN_SHORT_ARRAY
111
+			&& $this->isShortList($phpcsFile, $stackPtr) === false
112
+		) {
113
+			// Short array, not short list.
114
+			return;
115
+		}
116
+
117
+		if ($tokens[$stackPtr]['code'] === \T_LIST) {
118
+			$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
119
+			if ($nextNonEmpty === false
120
+				|| $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS
121
+				|| isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false
122
+			) {
123
+				// Parse error or live coding.
124
+				return;
125
+			}
126
+
127
+			$opener = $nextNonEmpty;
128
+			$closer = $tokens[$nextNonEmpty]['parenthesis_closer'];
129
+		} else {
130
+			// Short list syntax.
131
+			$opener = $stackPtr;
132
+
133
+			if (isset($tokens[$stackPtr]['bracket_closer'])) {
134
+				$closer = $tokens[$stackPtr]['bracket_closer'];
135
+			}
136
+		}
137
+
138
+		if (isset($opener, $closer) === false) {
139
+			return;
140
+		}
141
+
142
+		$this->examineList($phpcsFile, $opener, $closer);
143
+	}
144
+
145
+
146
+	/**
147
+	 * Examine the contents of a list construct to determine whether an error needs to be thrown.
148
+	 *
149
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
150
+	 * @param int                   $opener    The position of the list open token.
151
+	 * @param int                   $closer    The position of the list close token.
152
+	 *
153
+	 * @return void
154
+	 */
155
+	protected function examineList(File $phpcsFile, $opener, $closer)
156
+	{
157
+		$start = $opener;
158
+		while (($start = $this->hasTargetInList($phpcsFile, $start, $closer)) !== false) {
159
+			$phpcsFile->addError(
160
+				'Specifying keys in list constructs is not supported in PHP 7.0 or earlier.',
161
+				$start,
162
+				'Found'
163
+			);
164
+		}
165
+	}
166
+
167
+
168
+	/**
169
+	 * Check whether a certain target token exists within a list construct.
170
+	 *
171
+	 * Skips past nested list constructs, so these can be examined based on their own token.
172
+	 *
173
+	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
174
+	 * @param int                   $start     The position of the list open token or a token
175
+	 *                                         within the list to start (resume) the examination from.
176
+	 * @param int                   $closer    The position of the list close token.
177
+	 *
178
+	 * @return int|bool Stack pointer to the target token if encountered. False otherwise.
179
+	 */
180
+	protected function hasTargetInList(File $phpcsFile, $start, $closer)
181
+	{
182
+		$tokens = $phpcsFile->getTokens();
183
+
184
+		for ($i = ($start + 1); $i < $closer; $i++) {
185
+			if (isset($this->allTargets[$tokens[$i]['code']]) === false) {
186
+				continue;
187
+			}
188
+
189
+			if (isset($this->targetsInList[$tokens[$i]['code']]) === true) {
190
+				return $i;
191
+			}
192
+
193
+			// Skip past nested list constructs.
194
+			if ($tokens[$i]['code'] === \T_LIST) {
195
+				$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true);
196
+				if ($nextNonEmpty !== false
197
+					&& $tokens[$nextNonEmpty]['code'] === \T_OPEN_PARENTHESIS
198
+					&& isset($tokens[$nextNonEmpty]['parenthesis_closer']) === true
199
+				) {
200
+					$i = $tokens[$nextNonEmpty]['parenthesis_closer'];
201
+				}
202
+			} elseif ($tokens[$i]['code'] === \T_OPEN_SHORT_ARRAY
203
+				&& isset($tokens[$i]['bracket_closer'])
204
+			) {
205
+				$i = $tokens[$i]['bracket_closer'];
206
+			}
207
+		}
208
+
209
+		return false;
210
+	}
211 211
 }
Please login to merge, or discard this patch.