Completed
Pull Request — develop (#1492)
by Zack
33:18 queued 12:17
created
vendor/squizlabs/php_codesniffer/src/Filters/ExactMatch.php 1 patch
Indentation   +87 added lines, -87 removed lines patch added patch discarded remove patch
@@ -16,93 +16,93 @@
 block discarded – undo
16 16
 abstract class ExactMatch extends Filter
17 17
 {
18 18
 
19
-    /**
20
-     * A list of files to exclude.
21
-     *
22
-     * @var array
23
-     */
24
-    private $blacklist = null;
25
-
26
-    /**
27
-     * A list of files to include.
28
-     *
29
-     * If the whitelist is empty, only files in the blacklist will be excluded.
30
-     *
31
-     * @var array
32
-     */
33
-    private $whitelist = null;
34
-
35
-
36
-    /**
37
-     * Check whether the current element of the iterator is acceptable.
38
-     *
39
-     * If a file is both blacklisted and whitelisted, it will be deemed unacceptable.
40
-     *
41
-     * @return bool
42
-     */
43
-    public function accept()
44
-    {
45
-        if (parent::accept() === false) {
46
-            return false;
47
-        }
48
-
49
-        if ($this->blacklist === null) {
50
-            $this->blacklist = $this->getblacklist();
51
-        }
52
-
53
-        if ($this->whitelist === null) {
54
-            $this->whitelist = $this->getwhitelist();
55
-        }
56
-
57
-        $filePath = Util\Common::realpath($this->current());
58
-
59
-        // If file is both blacklisted and whitelisted, the blacklist takes precedence.
60
-        if (isset($this->blacklist[$filePath]) === true) {
61
-            return false;
62
-        }
63
-
64
-        if (empty($this->whitelist) === true && empty($this->blacklist) === false) {
65
-            // We are only checking a blacklist, so everything else should be whitelisted.
66
-            return true;
67
-        }
68
-
69
-        return isset($this->whitelist[$filePath]);
70
-
71
-    }//end accept()
72
-
73
-
74
-    /**
75
-     * Returns an iterator for the current entry.
76
-     *
77
-     * Ensures that the blacklist and whitelist are preserved so they don't have
78
-     * to be generated each time.
79
-     *
80
-     * @return \RecursiveIterator
81
-     */
82
-    public function getChildren()
83
-    {
84
-        $children            = parent::getChildren();
85
-        $children->blacklist = $this->blacklist;
86
-        $children->whitelist = $this->whitelist;
87
-        return $children;
88
-
89
-    }//end getChildren()
90
-
91
-
92
-    /**
93
-     * Get a list of blacklisted file paths.
94
-     *
95
-     * @return array
96
-     */
97
-    abstract protected function getBlacklist();
98
-
99
-
100
-    /**
101
-     * Get a list of whitelisted file paths.
102
-     *
103
-     * @return array
104
-     */
105
-    abstract protected function getWhitelist();
19
+	/**
20
+	 * A list of files to exclude.
21
+	 *
22
+	 * @var array
23
+	 */
24
+	private $blacklist = null;
25
+
26
+	/**
27
+	 * A list of files to include.
28
+	 *
29
+	 * If the whitelist is empty, only files in the blacklist will be excluded.
30
+	 *
31
+	 * @var array
32
+	 */
33
+	private $whitelist = null;
34
+
35
+
36
+	/**
37
+	 * Check whether the current element of the iterator is acceptable.
38
+	 *
39
+	 * If a file is both blacklisted and whitelisted, it will be deemed unacceptable.
40
+	 *
41
+	 * @return bool
42
+	 */
43
+	public function accept()
44
+	{
45
+		if (parent::accept() === false) {
46
+			return false;
47
+		}
48
+
49
+		if ($this->blacklist === null) {
50
+			$this->blacklist = $this->getblacklist();
51
+		}
52
+
53
+		if ($this->whitelist === null) {
54
+			$this->whitelist = $this->getwhitelist();
55
+		}
56
+
57
+		$filePath = Util\Common::realpath($this->current());
58
+
59
+		// If file is both blacklisted and whitelisted, the blacklist takes precedence.
60
+		if (isset($this->blacklist[$filePath]) === true) {
61
+			return false;
62
+		}
63
+
64
+		if (empty($this->whitelist) === true && empty($this->blacklist) === false) {
65
+			// We are only checking a blacklist, so everything else should be whitelisted.
66
+			return true;
67
+		}
68
+
69
+		return isset($this->whitelist[$filePath]);
70
+
71
+	}//end accept()
72
+
73
+
74
+	/**
75
+	 * Returns an iterator for the current entry.
76
+	 *
77
+	 * Ensures that the blacklist and whitelist are preserved so they don't have
78
+	 * to be generated each time.
79
+	 *
80
+	 * @return \RecursiveIterator
81
+	 */
82
+	public function getChildren()
83
+	{
84
+		$children            = parent::getChildren();
85
+		$children->blacklist = $this->blacklist;
86
+		$children->whitelist = $this->whitelist;
87
+		return $children;
88
+
89
+	}//end getChildren()
90
+
91
+
92
+	/**
93
+	 * Get a list of blacklisted file paths.
94
+	 *
95
+	 * @return array
96
+	 */
97
+	abstract protected function getBlacklist();
98
+
99
+
100
+	/**
101
+	 * Get a list of whitelisted file paths.
102
+	 *
103
+	 * @return array
104
+	 */
105
+	abstract protected function getWhitelist();
106 106
 
107 107
 
108 108
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Filters/GitModified.php 1 patch
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -15,52 +15,52 @@
 block discarded – undo
15 15
 {
16 16
 
17 17
 
18
-    /**
19
-     * Get a list of blacklisted file paths.
20
-     *
21
-     * @return array
22
-     */
23
-    protected function getBlacklist()
24
-    {
25
-        return [];
26
-
27
-    }//end getBlacklist()
28
-
29
-
30
-    /**
31
-     * Get a list of whitelisted file paths.
32
-     *
33
-     * @return array
34
-     */
35
-    protected function getWhitelist()
36
-    {
37
-        $modified = [];
38
-
39
-        $cmd    = 'git ls-files -o -m --exclude-standard -- '.escapeshellarg($this->basedir);
40
-        $output = [];
41
-        exec($cmd, $output);
42
-
43
-        $basedir = $this->basedir;
44
-        if (is_dir($basedir) === false) {
45
-            $basedir = dirname($basedir);
46
-        }
47
-
48
-        foreach ($output as $path) {
49
-            $path = Util\Common::realpath($path);
50
-
51
-            if ($path === false) {
52
-                continue;
53
-            }
54
-
55
-            do {
56
-                $modified[$path] = true;
57
-                $path            = dirname($path);
58
-            } while ($path !== $basedir);
59
-        }
60
-
61
-        return $modified;
62
-
63
-    }//end getWhitelist()
18
+	/**
19
+	 * Get a list of blacklisted file paths.
20
+	 *
21
+	 * @return array
22
+	 */
23
+	protected function getBlacklist()
24
+	{
25
+		return [];
26
+
27
+	}//end getBlacklist()
28
+
29
+
30
+	/**
31
+	 * Get a list of whitelisted file paths.
32
+	 *
33
+	 * @return array
34
+	 */
35
+	protected function getWhitelist()
36
+	{
37
+		$modified = [];
38
+
39
+		$cmd    = 'git ls-files -o -m --exclude-standard -- '.escapeshellarg($this->basedir);
40
+		$output = [];
41
+		exec($cmd, $output);
42
+
43
+		$basedir = $this->basedir;
44
+		if (is_dir($basedir) === false) {
45
+			$basedir = dirname($basedir);
46
+		}
47
+
48
+		foreach ($output as $path) {
49
+			$path = Util\Common::realpath($path);
50
+
51
+			if ($path === false) {
52
+				continue;
53
+			}
54
+
55
+			do {
56
+				$modified[$path] = true;
57
+				$path            = dirname($path);
58
+			} while ($path !== $basedir);
59
+		}
60
+
61
+		return $modified;
62
+
63
+	}//end getWhitelist()
64 64
 
65 65
 
66 66
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Reporter.php 1 patch
Indentation   +399 added lines, -399 removed lines patch added patch discarded remove patch
@@ -18,405 +18,405 @@
 block discarded – undo
18 18
 class Reporter
19 19
 {
20 20
 
21
-    /**
22
-     * The config data for the run.
23
-     *
24
-     * @var \PHP_CodeSniffer\Config
25
-     */
26
-    public $config = null;
27
-
28
-    /**
29
-     * Total number of files that contain errors or warnings.
30
-     *
31
-     * @var integer
32
-     */
33
-    public $totalFiles = 0;
34
-
35
-    /**
36
-     * Total number of errors found during the run.
37
-     *
38
-     * @var integer
39
-     */
40
-    public $totalErrors = 0;
41
-
42
-    /**
43
-     * Total number of warnings found during the run.
44
-     *
45
-     * @var integer
46
-     */
47
-    public $totalWarnings = 0;
48
-
49
-    /**
50
-     * Total number of errors/warnings that can be fixed.
51
-     *
52
-     * @var integer
53
-     */
54
-    public $totalFixable = 0;
55
-
56
-    /**
57
-     * Total number of errors/warnings that were fixed.
58
-     *
59
-     * @var integer
60
-     */
61
-    public $totalFixed = 0;
62
-
63
-    /**
64
-     * When the PHPCS run started.
65
-     *
66
-     * @var float
67
-     */
68
-    public static $startTime = 0;
69
-
70
-    /**
71
-     * A cache of report objects.
72
-     *
73
-     * @var array
74
-     */
75
-    private $reports = [];
76
-
77
-    /**
78
-     * A cache of opened temporary files.
79
-     *
80
-     * @var array
81
-     */
82
-    private $tmpFiles = [];
83
-
84
-
85
-    /**
86
-     * Initialise the reporter.
87
-     *
88
-     * All reports specified in the config will be created and their
89
-     * output file (or a temp file if none is specified) initialised by
90
-     * clearing the current contents.
91
-     *
92
-     * @param \PHP_CodeSniffer\Config $config The config data for the run.
93
-     *
94
-     * @return void
95
-     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If a report is not available.
96
-     */
97
-    public function __construct(Config $config)
98
-    {
99
-        $this->config = $config;
100
-
101
-        foreach ($config->reports as $type => $output) {
102
-            if ($output === null) {
103
-                $output = $config->reportFile;
104
-            }
105
-
106
-            $reportClassName = '';
107
-            if (strpos($type, '.') !== false) {
108
-                // This is a path to a custom report class.
109
-                $filename = realpath($type);
110
-                if ($filename === false) {
111
-                    $error = "ERROR: Custom report \"$type\" not found".PHP_EOL;
112
-                    throw new DeepExitException($error, 3);
113
-                }
114
-
115
-                $reportClassName = Autoload::loadFile($filename);
116
-            } else if (class_exists('PHP_CodeSniffer\Reports\\'.ucfirst($type)) === true) {
117
-                // PHPCS native report.
118
-                $reportClassName = 'PHP_CodeSniffer\Reports\\'.ucfirst($type);
119
-            } else if (class_exists($type) === true) {
120
-                // FQN of a custom report.
121
-                $reportClassName = $type;
122
-            } else {
123
-                // OK, so not a FQN, try and find the report using the registered namespaces.
124
-                $registeredNamespaces = Autoload::getSearchPaths();
125
-                $trimmedType          = ltrim($type, '\\');
126
-
127
-                foreach ($registeredNamespaces as $nsPrefix) {
128
-                    if ($nsPrefix === '') {
129
-                        continue;
130
-                    }
131
-
132
-                    if (class_exists($nsPrefix.'\\'.$trimmedType) === true) {
133
-                        $reportClassName = $nsPrefix.'\\'.$trimmedType;
134
-                        break;
135
-                    }
136
-                }
137
-            }//end if
138
-
139
-            if ($reportClassName === '') {
140
-                $error = "ERROR: Class file for report \"$type\" not found".PHP_EOL;
141
-                throw new DeepExitException($error, 3);
142
-            }
143
-
144
-            $reportClass = new $reportClassName();
145
-            if (false === ($reportClass instanceof Report)) {
146
-                throw new RuntimeException('Class "'.$reportClassName.'" must implement the "PHP_CodeSniffer\Report" interface.');
147
-            }
148
-
149
-            $this->reports[$type] = [
150
-                'output' => $output,
151
-                'class'  => $reportClass,
152
-            ];
153
-
154
-            if ($output === null) {
155
-                // Using a temp file.
156
-                // This needs to be set in the constructor so that all
157
-                // child procs use the same report file when running in parallel.
158
-                $this->tmpFiles[$type] = tempnam(sys_get_temp_dir(), 'phpcs');
159
-                file_put_contents($this->tmpFiles[$type], '');
160
-            } else {
161
-                file_put_contents($output, '');
162
-            }
163
-        }//end foreach
164
-
165
-    }//end __construct()
166
-
167
-
168
-    /**
169
-     * Generates and prints final versions of all reports.
170
-     *
171
-     * Returns TRUE if any of the reports output content to the screen
172
-     * or FALSE if all reports were silently printed to a file.
173
-     *
174
-     * @return bool
175
-     */
176
-    public function printReports()
177
-    {
178
-        $toScreen = false;
179
-        foreach ($this->reports as $type => $report) {
180
-            if ($report['output'] === null) {
181
-                $toScreen = true;
182
-            }
183
-
184
-            $this->printReport($type);
185
-        }
186
-
187
-        return $toScreen;
188
-
189
-    }//end printReports()
190
-
191
-
192
-    /**
193
-     * Generates and prints a single final report.
194
-     *
195
-     * @param string $report The report type to print.
196
-     *
197
-     * @return void
198
-     */
199
-    public function printReport($report)
200
-    {
201
-        $reportClass = $this->reports[$report]['class'];
202
-        $reportFile  = $this->reports[$report]['output'];
203
-
204
-        if ($reportFile !== null) {
205
-            $filename = $reportFile;
206
-            $toScreen = false;
207
-        } else {
208
-            if (isset($this->tmpFiles[$report]) === true) {
209
-                $filename = $this->tmpFiles[$report];
210
-            } else {
211
-                $filename = null;
212
-            }
213
-
214
-            $toScreen = true;
215
-        }
216
-
217
-        $reportCache = '';
218
-        if ($filename !== null) {
219
-            $reportCache = file_get_contents($filename);
220
-        }
221
-
222
-        ob_start();
223
-        $reportClass->generate(
224
-            $reportCache,
225
-            $this->totalFiles,
226
-            $this->totalErrors,
227
-            $this->totalWarnings,
228
-            $this->totalFixable,
229
-            $this->config->showSources,
230
-            $this->config->reportWidth,
231
-            $this->config->interactive,
232
-            $toScreen
233
-        );
234
-        $generatedReport = ob_get_contents();
235
-        ob_end_clean();
236
-
237
-        if ($this->config->colors !== true || $reportFile !== null) {
238
-            $generatedReport = preg_replace('`\033\[[0-9;]+m`', '', $generatedReport);
239
-        }
240
-
241
-        if ($reportFile !== null) {
242
-            if (PHP_CODESNIFFER_VERBOSITY > 0) {
243
-                echo $generatedReport;
244
-            }
245
-
246
-            file_put_contents($reportFile, $generatedReport.PHP_EOL);
247
-        } else {
248
-            echo $generatedReport;
249
-            if ($filename !== null && file_exists($filename) === true) {
250
-                unlink($filename);
251
-                unset($this->tmpFiles[$report]);
252
-            }
253
-        }
254
-
255
-    }//end printReport()
256
-
257
-
258
-    /**
259
-     * Caches the result of a single processed file for all reports.
260
-     *
261
-     * The report content that is generated is appended to the output file
262
-     * assigned to each report. This content may be an intermediate report format
263
-     * and not reflect the final report output.
264
-     *
265
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file that has been processed.
266
-     *
267
-     * @return void
268
-     */
269
-    public function cacheFileReport(File $phpcsFile)
270
-    {
271
-        if (isset($this->config->reports) === false) {
272
-            // This happens during unit testing, or any time someone just wants
273
-            // the error data and not the printed report.
274
-            return;
275
-        }
276
-
277
-        $reportData  = $this->prepareFileReport($phpcsFile);
278
-        $errorsShown = false;
279
-
280
-        foreach ($this->reports as $type => $report) {
281
-            $reportClass = $report['class'];
282
-
283
-            ob_start();
284
-            $result = $reportClass->generateFileReport($reportData, $phpcsFile, $this->config->showSources, $this->config->reportWidth);
285
-            if ($result === true) {
286
-                $errorsShown = true;
287
-            }
288
-
289
-            $generatedReport = ob_get_contents();
290
-            ob_end_clean();
291
-
292
-            if ($report['output'] === null) {
293
-                // Using a temp file.
294
-                if (isset($this->tmpFiles[$type]) === false) {
295
-                    // When running in interactive mode, the reporter prints the full
296
-                    // report many times, which will unlink the temp file. So we need
297
-                    // to create a new one if it doesn't exist.
298
-                    $this->tmpFiles[$type] = tempnam(sys_get_temp_dir(), 'phpcs');
299
-                    file_put_contents($this->tmpFiles[$type], '');
300
-                }
301
-
302
-                file_put_contents($this->tmpFiles[$type], $generatedReport, (FILE_APPEND | LOCK_EX));
303
-            } else {
304
-                file_put_contents($report['output'], $generatedReport, (FILE_APPEND | LOCK_EX));
305
-            }//end if
306
-        }//end foreach
307
-
308
-        if ($errorsShown === true || PHP_CODESNIFFER_CBF === true) {
309
-            $this->totalFiles++;
310
-            $this->totalErrors   += $reportData['errors'];
311
-            $this->totalWarnings += $reportData['warnings'];
312
-
313
-            // When PHPCBF is running, we need to use the fixable error values
314
-            // after the report has run and fixed what it can.
315
-            if (PHP_CODESNIFFER_CBF === true) {
316
-                $this->totalFixable += $phpcsFile->getFixableCount();
317
-                $this->totalFixed   += $phpcsFile->getFixedCount();
318
-            } else {
319
-                $this->totalFixable += $reportData['fixable'];
320
-            }
321
-        }
322
-
323
-    }//end cacheFileReport()
324
-
325
-
326
-    /**
327
-     * Generate summary information to be used during report generation.
328
-     *
329
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file that has been processed.
330
-     *
331
-     * @return array
332
-     */
333
-    public function prepareFileReport(File $phpcsFile)
334
-    {
335
-        $report = [
336
-            'filename' => Common::stripBasepath($phpcsFile->getFilename(), $this->config->basepath),
337
-            'errors'   => $phpcsFile->getErrorCount(),
338
-            'warnings' => $phpcsFile->getWarningCount(),
339
-            'fixable'  => $phpcsFile->getFixableCount(),
340
-            'messages' => [],
341
-        ];
342
-
343
-        if ($report['errors'] === 0 && $report['warnings'] === 0) {
344
-            // Prefect score!
345
-            return $report;
346
-        }
347
-
348
-        if ($this->config->recordErrors === false) {
349
-            $message  = 'Errors are not being recorded but this report requires error messages. ';
350
-            $message .= 'This report will not show the correct information.';
351
-            $report['messages'][1][1] = [
352
-                [
353
-                    'message'  => $message,
354
-                    'source'   => 'Internal.RecordErrors',
355
-                    'severity' => 5,
356
-                    'fixable'  => false,
357
-                    'type'     => 'ERROR',
358
-                ],
359
-            ];
360
-            return $report;
361
-        }
362
-
363
-        $errors = [];
364
-
365
-        // Merge errors and warnings.
366
-        foreach ($phpcsFile->getErrors() as $line => $lineErrors) {
367
-            foreach ($lineErrors as $column => $colErrors) {
368
-                $newErrors = [];
369
-                foreach ($colErrors as $data) {
370
-                    $newErrors[] = [
371
-                        'message'  => $data['message'],
372
-                        'source'   => $data['source'],
373
-                        'severity' => $data['severity'],
374
-                        'fixable'  => $data['fixable'],
375
-                        'type'     => 'ERROR',
376
-                    ];
377
-                }
378
-
379
-                $errors[$line][$column] = $newErrors;
380
-            }
381
-
382
-            ksort($errors[$line]);
383
-        }//end foreach
384
-
385
-        foreach ($phpcsFile->getWarnings() as $line => $lineWarnings) {
386
-            foreach ($lineWarnings as $column => $colWarnings) {
387
-                $newWarnings = [];
388
-                foreach ($colWarnings as $data) {
389
-                    $newWarnings[] = [
390
-                        'message'  => $data['message'],
391
-                        'source'   => $data['source'],
392
-                        'severity' => $data['severity'],
393
-                        'fixable'  => $data['fixable'],
394
-                        'type'     => 'WARNING',
395
-                    ];
396
-                }
397
-
398
-                if (isset($errors[$line]) === false) {
399
-                    $errors[$line] = [];
400
-                }
401
-
402
-                if (isset($errors[$line][$column]) === true) {
403
-                    $errors[$line][$column] = array_merge(
404
-                        $newWarnings,
405
-                        $errors[$line][$column]
406
-                    );
407
-                } else {
408
-                    $errors[$line][$column] = $newWarnings;
409
-                }
410
-            }//end foreach
411
-
412
-            ksort($errors[$line]);
413
-        }//end foreach
414
-
415
-        ksort($errors);
416
-        $report['messages'] = $errors;
417
-        return $report;
418
-
419
-    }//end prepareFileReport()
21
+	/**
22
+	 * The config data for the run.
23
+	 *
24
+	 * @var \PHP_CodeSniffer\Config
25
+	 */
26
+	public $config = null;
27
+
28
+	/**
29
+	 * Total number of files that contain errors or warnings.
30
+	 *
31
+	 * @var integer
32
+	 */
33
+	public $totalFiles = 0;
34
+
35
+	/**
36
+	 * Total number of errors found during the run.
37
+	 *
38
+	 * @var integer
39
+	 */
40
+	public $totalErrors = 0;
41
+
42
+	/**
43
+	 * Total number of warnings found during the run.
44
+	 *
45
+	 * @var integer
46
+	 */
47
+	public $totalWarnings = 0;
48
+
49
+	/**
50
+	 * Total number of errors/warnings that can be fixed.
51
+	 *
52
+	 * @var integer
53
+	 */
54
+	public $totalFixable = 0;
55
+
56
+	/**
57
+	 * Total number of errors/warnings that were fixed.
58
+	 *
59
+	 * @var integer
60
+	 */
61
+	public $totalFixed = 0;
62
+
63
+	/**
64
+	 * When the PHPCS run started.
65
+	 *
66
+	 * @var float
67
+	 */
68
+	public static $startTime = 0;
69
+
70
+	/**
71
+	 * A cache of report objects.
72
+	 *
73
+	 * @var array
74
+	 */
75
+	private $reports = [];
76
+
77
+	/**
78
+	 * A cache of opened temporary files.
79
+	 *
80
+	 * @var array
81
+	 */
82
+	private $tmpFiles = [];
83
+
84
+
85
+	/**
86
+	 * Initialise the reporter.
87
+	 *
88
+	 * All reports specified in the config will be created and their
89
+	 * output file (or a temp file if none is specified) initialised by
90
+	 * clearing the current contents.
91
+	 *
92
+	 * @param \PHP_CodeSniffer\Config $config The config data for the run.
93
+	 *
94
+	 * @return void
95
+	 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If a report is not available.
96
+	 */
97
+	public function __construct(Config $config)
98
+	{
99
+		$this->config = $config;
100
+
101
+		foreach ($config->reports as $type => $output) {
102
+			if ($output === null) {
103
+				$output = $config->reportFile;
104
+			}
105
+
106
+			$reportClassName = '';
107
+			if (strpos($type, '.') !== false) {
108
+				// This is a path to a custom report class.
109
+				$filename = realpath($type);
110
+				if ($filename === false) {
111
+					$error = "ERROR: Custom report \"$type\" not found".PHP_EOL;
112
+					throw new DeepExitException($error, 3);
113
+				}
114
+
115
+				$reportClassName = Autoload::loadFile($filename);
116
+			} else if (class_exists('PHP_CodeSniffer\Reports\\'.ucfirst($type)) === true) {
117
+				// PHPCS native report.
118
+				$reportClassName = 'PHP_CodeSniffer\Reports\\'.ucfirst($type);
119
+			} else if (class_exists($type) === true) {
120
+				// FQN of a custom report.
121
+				$reportClassName = $type;
122
+			} else {
123
+				// OK, so not a FQN, try and find the report using the registered namespaces.
124
+				$registeredNamespaces = Autoload::getSearchPaths();
125
+				$trimmedType          = ltrim($type, '\\');
126
+
127
+				foreach ($registeredNamespaces as $nsPrefix) {
128
+					if ($nsPrefix === '') {
129
+						continue;
130
+					}
131
+
132
+					if (class_exists($nsPrefix.'\\'.$trimmedType) === true) {
133
+						$reportClassName = $nsPrefix.'\\'.$trimmedType;
134
+						break;
135
+					}
136
+				}
137
+			}//end if
138
+
139
+			if ($reportClassName === '') {
140
+				$error = "ERROR: Class file for report \"$type\" not found".PHP_EOL;
141
+				throw new DeepExitException($error, 3);
142
+			}
143
+
144
+			$reportClass = new $reportClassName();
145
+			if (false === ($reportClass instanceof Report)) {
146
+				throw new RuntimeException('Class "'.$reportClassName.'" must implement the "PHP_CodeSniffer\Report" interface.');
147
+			}
148
+
149
+			$this->reports[$type] = [
150
+				'output' => $output,
151
+				'class'  => $reportClass,
152
+			];
153
+
154
+			if ($output === null) {
155
+				// Using a temp file.
156
+				// This needs to be set in the constructor so that all
157
+				// child procs use the same report file when running in parallel.
158
+				$this->tmpFiles[$type] = tempnam(sys_get_temp_dir(), 'phpcs');
159
+				file_put_contents($this->tmpFiles[$type], '');
160
+			} else {
161
+				file_put_contents($output, '');
162
+			}
163
+		}//end foreach
164
+
165
+	}//end __construct()
166
+
167
+
168
+	/**
169
+	 * Generates and prints final versions of all reports.
170
+	 *
171
+	 * Returns TRUE if any of the reports output content to the screen
172
+	 * or FALSE if all reports were silently printed to a file.
173
+	 *
174
+	 * @return bool
175
+	 */
176
+	public function printReports()
177
+	{
178
+		$toScreen = false;
179
+		foreach ($this->reports as $type => $report) {
180
+			if ($report['output'] === null) {
181
+				$toScreen = true;
182
+			}
183
+
184
+			$this->printReport($type);
185
+		}
186
+
187
+		return $toScreen;
188
+
189
+	}//end printReports()
190
+
191
+
192
+	/**
193
+	 * Generates and prints a single final report.
194
+	 *
195
+	 * @param string $report The report type to print.
196
+	 *
197
+	 * @return void
198
+	 */
199
+	public function printReport($report)
200
+	{
201
+		$reportClass = $this->reports[$report]['class'];
202
+		$reportFile  = $this->reports[$report]['output'];
203
+
204
+		if ($reportFile !== null) {
205
+			$filename = $reportFile;
206
+			$toScreen = false;
207
+		} else {
208
+			if (isset($this->tmpFiles[$report]) === true) {
209
+				$filename = $this->tmpFiles[$report];
210
+			} else {
211
+				$filename = null;
212
+			}
213
+
214
+			$toScreen = true;
215
+		}
216
+
217
+		$reportCache = '';
218
+		if ($filename !== null) {
219
+			$reportCache = file_get_contents($filename);
220
+		}
221
+
222
+		ob_start();
223
+		$reportClass->generate(
224
+			$reportCache,
225
+			$this->totalFiles,
226
+			$this->totalErrors,
227
+			$this->totalWarnings,
228
+			$this->totalFixable,
229
+			$this->config->showSources,
230
+			$this->config->reportWidth,
231
+			$this->config->interactive,
232
+			$toScreen
233
+		);
234
+		$generatedReport = ob_get_contents();
235
+		ob_end_clean();
236
+
237
+		if ($this->config->colors !== true || $reportFile !== null) {
238
+			$generatedReport = preg_replace('`\033\[[0-9;]+m`', '', $generatedReport);
239
+		}
240
+
241
+		if ($reportFile !== null) {
242
+			if (PHP_CODESNIFFER_VERBOSITY > 0) {
243
+				echo $generatedReport;
244
+			}
245
+
246
+			file_put_contents($reportFile, $generatedReport.PHP_EOL);
247
+		} else {
248
+			echo $generatedReport;
249
+			if ($filename !== null && file_exists($filename) === true) {
250
+				unlink($filename);
251
+				unset($this->tmpFiles[$report]);
252
+			}
253
+		}
254
+
255
+	}//end printReport()
256
+
257
+
258
+	/**
259
+	 * Caches the result of a single processed file for all reports.
260
+	 *
261
+	 * The report content that is generated is appended to the output file
262
+	 * assigned to each report. This content may be an intermediate report format
263
+	 * and not reflect the final report output.
264
+	 *
265
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file that has been processed.
266
+	 *
267
+	 * @return void
268
+	 */
269
+	public function cacheFileReport(File $phpcsFile)
270
+	{
271
+		if (isset($this->config->reports) === false) {
272
+			// This happens during unit testing, or any time someone just wants
273
+			// the error data and not the printed report.
274
+			return;
275
+		}
276
+
277
+		$reportData  = $this->prepareFileReport($phpcsFile);
278
+		$errorsShown = false;
279
+
280
+		foreach ($this->reports as $type => $report) {
281
+			$reportClass = $report['class'];
282
+
283
+			ob_start();
284
+			$result = $reportClass->generateFileReport($reportData, $phpcsFile, $this->config->showSources, $this->config->reportWidth);
285
+			if ($result === true) {
286
+				$errorsShown = true;
287
+			}
288
+
289
+			$generatedReport = ob_get_contents();
290
+			ob_end_clean();
291
+
292
+			if ($report['output'] === null) {
293
+				// Using a temp file.
294
+				if (isset($this->tmpFiles[$type]) === false) {
295
+					// When running in interactive mode, the reporter prints the full
296
+					// report many times, which will unlink the temp file. So we need
297
+					// to create a new one if it doesn't exist.
298
+					$this->tmpFiles[$type] = tempnam(sys_get_temp_dir(), 'phpcs');
299
+					file_put_contents($this->tmpFiles[$type], '');
300
+				}
301
+
302
+				file_put_contents($this->tmpFiles[$type], $generatedReport, (FILE_APPEND | LOCK_EX));
303
+			} else {
304
+				file_put_contents($report['output'], $generatedReport, (FILE_APPEND | LOCK_EX));
305
+			}//end if
306
+		}//end foreach
307
+
308
+		if ($errorsShown === true || PHP_CODESNIFFER_CBF === true) {
309
+			$this->totalFiles++;
310
+			$this->totalErrors   += $reportData['errors'];
311
+			$this->totalWarnings += $reportData['warnings'];
312
+
313
+			// When PHPCBF is running, we need to use the fixable error values
314
+			// after the report has run and fixed what it can.
315
+			if (PHP_CODESNIFFER_CBF === true) {
316
+				$this->totalFixable += $phpcsFile->getFixableCount();
317
+				$this->totalFixed   += $phpcsFile->getFixedCount();
318
+			} else {
319
+				$this->totalFixable += $reportData['fixable'];
320
+			}
321
+		}
322
+
323
+	}//end cacheFileReport()
324
+
325
+
326
+	/**
327
+	 * Generate summary information to be used during report generation.
328
+	 *
329
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file that has been processed.
330
+	 *
331
+	 * @return array
332
+	 */
333
+	public function prepareFileReport(File $phpcsFile)
334
+	{
335
+		$report = [
336
+			'filename' => Common::stripBasepath($phpcsFile->getFilename(), $this->config->basepath),
337
+			'errors'   => $phpcsFile->getErrorCount(),
338
+			'warnings' => $phpcsFile->getWarningCount(),
339
+			'fixable'  => $phpcsFile->getFixableCount(),
340
+			'messages' => [],
341
+		];
342
+
343
+		if ($report['errors'] === 0 && $report['warnings'] === 0) {
344
+			// Prefect score!
345
+			return $report;
346
+		}
347
+
348
+		if ($this->config->recordErrors === false) {
349
+			$message  = 'Errors are not being recorded but this report requires error messages. ';
350
+			$message .= 'This report will not show the correct information.';
351
+			$report['messages'][1][1] = [
352
+				[
353
+					'message'  => $message,
354
+					'source'   => 'Internal.RecordErrors',
355
+					'severity' => 5,
356
+					'fixable'  => false,
357
+					'type'     => 'ERROR',
358
+				],
359
+			];
360
+			return $report;
361
+		}
362
+
363
+		$errors = [];
364
+
365
+		// Merge errors and warnings.
366
+		foreach ($phpcsFile->getErrors() as $line => $lineErrors) {
367
+			foreach ($lineErrors as $column => $colErrors) {
368
+				$newErrors = [];
369
+				foreach ($colErrors as $data) {
370
+					$newErrors[] = [
371
+						'message'  => $data['message'],
372
+						'source'   => $data['source'],
373
+						'severity' => $data['severity'],
374
+						'fixable'  => $data['fixable'],
375
+						'type'     => 'ERROR',
376
+					];
377
+				}
378
+
379
+				$errors[$line][$column] = $newErrors;
380
+			}
381
+
382
+			ksort($errors[$line]);
383
+		}//end foreach
384
+
385
+		foreach ($phpcsFile->getWarnings() as $line => $lineWarnings) {
386
+			foreach ($lineWarnings as $column => $colWarnings) {
387
+				$newWarnings = [];
388
+				foreach ($colWarnings as $data) {
389
+					$newWarnings[] = [
390
+						'message'  => $data['message'],
391
+						'source'   => $data['source'],
392
+						'severity' => $data['severity'],
393
+						'fixable'  => $data['fixable'],
394
+						'type'     => 'WARNING',
395
+					];
396
+				}
397
+
398
+				if (isset($errors[$line]) === false) {
399
+					$errors[$line] = [];
400
+				}
401
+
402
+				if (isset($errors[$line][$column]) === true) {
403
+					$errors[$line][$column] = array_merge(
404
+						$newWarnings,
405
+						$errors[$line][$column]
406
+					);
407
+				} else {
408
+					$errors[$line][$column] = $newWarnings;
409
+				}
410
+			}//end foreach
411
+
412
+			ksort($errors[$line]);
413
+		}//end foreach
414
+
415
+		ksort($errors);
416
+		$report['messages'] = $errors;
417
+		return $report;
418
+
419
+	}//end prepareFileReport()
420 420
 
421 421
 
422 422
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Tokenizers/PHP.php 1 patch
Indentation   +1888 added lines, -1888 removed lines patch added patch discarded remove patch
@@ -15,660 +15,660 @@  discard block
 block discarded – undo
15 15
 {
16 16
 
17 17
 
18
-    /**
19
-     * A list of tokens that are allowed to open a scope.
20
-     *
21
-     * This array also contains information about what kind of token the scope
22
-     * opener uses to open and close the scope, if the token strictly requires
23
-     * an opener, if the token can share a scope closer, and who it can be shared
24
-     * with. An example of a token that shares a scope closer is a CASE scope.
25
-     *
26
-     * @var array
27
-     */
28
-    public $scopeOpeners = [
29
-        T_IF            => [
30
-            'start'  => [
31
-                T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
32
-                T_COLON              => T_COLON,
33
-            ],
34
-            'end'    => [
35
-                T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
36
-                T_ENDIF               => T_ENDIF,
37
-                T_ELSE                => T_ELSE,
38
-                T_ELSEIF              => T_ELSEIF,
39
-            ],
40
-            'strict' => false,
41
-            'shared' => false,
42
-            'with'   => [
43
-                T_ELSE   => T_ELSE,
44
-                T_ELSEIF => T_ELSEIF,
45
-            ],
46
-        ],
47
-        T_TRY           => [
48
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
49
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
50
-            'strict' => true,
51
-            'shared' => false,
52
-            'with'   => [],
53
-        ],
54
-        T_CATCH         => [
55
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
56
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
57
-            'strict' => true,
58
-            'shared' => false,
59
-            'with'   => [],
60
-        ],
61
-        T_FINALLY       => [
62
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
63
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
64
-            'strict' => true,
65
-            'shared' => false,
66
-            'with'   => [],
67
-        ],
68
-        T_ELSE          => [
69
-            'start'  => [
70
-                T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
71
-                T_COLON              => T_COLON,
72
-            ],
73
-            'end'    => [
74
-                T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
75
-                T_ENDIF               => T_ENDIF,
76
-            ],
77
-            'strict' => false,
78
-            'shared' => false,
79
-            'with'   => [
80
-                T_IF     => T_IF,
81
-                T_ELSEIF => T_ELSEIF,
82
-            ],
83
-        ],
84
-        T_ELSEIF        => [
85
-            'start'  => [
86
-                T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
87
-                T_COLON              => T_COLON,
88
-            ],
89
-            'end'    => [
90
-                T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
91
-                T_ENDIF               => T_ENDIF,
92
-                T_ELSE                => T_ELSE,
93
-                T_ELSEIF              => T_ELSEIF,
94
-            ],
95
-            'strict' => false,
96
-            'shared' => false,
97
-            'with'   => [
98
-                T_IF   => T_IF,
99
-                T_ELSE => T_ELSE,
100
-            ],
101
-        ],
102
-        T_FOR           => [
103
-            'start'  => [
104
-                T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
105
-                T_COLON              => T_COLON,
106
-            ],
107
-            'end'    => [
108
-                T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
109
-                T_ENDFOR              => T_ENDFOR,
110
-            ],
111
-            'strict' => false,
112
-            'shared' => false,
113
-            'with'   => [],
114
-        ],
115
-        T_FOREACH       => [
116
-            'start'  => [
117
-                T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
118
-                T_COLON              => T_COLON,
119
-            ],
120
-            'end'    => [
121
-                T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
122
-                T_ENDFOREACH          => T_ENDFOREACH,
123
-            ],
124
-            'strict' => false,
125
-            'shared' => false,
126
-            'with'   => [],
127
-        ],
128
-        T_INTERFACE     => [
129
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
130
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
131
-            'strict' => true,
132
-            'shared' => false,
133
-            'with'   => [],
134
-        ],
135
-        T_FUNCTION      => [
136
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
137
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
138
-            'strict' => true,
139
-            'shared' => false,
140
-            'with'   => [],
141
-        ],
142
-        T_CLASS         => [
143
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
144
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
145
-            'strict' => true,
146
-            'shared' => false,
147
-            'with'   => [],
148
-        ],
149
-        T_TRAIT         => [
150
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
151
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
152
-            'strict' => true,
153
-            'shared' => false,
154
-            'with'   => [],
155
-        ],
156
-        T_USE           => [
157
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
158
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
159
-            'strict' => false,
160
-            'shared' => false,
161
-            'with'   => [],
162
-        ],
163
-        T_DECLARE       => [
164
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
165
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
166
-            'strict' => false,
167
-            'shared' => false,
168
-            'with'   => [],
169
-        ],
170
-        T_NAMESPACE     => [
171
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
172
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
173
-            'strict' => false,
174
-            'shared' => false,
175
-            'with'   => [],
176
-        ],
177
-        T_WHILE         => [
178
-            'start'  => [
179
-                T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
180
-                T_COLON              => T_COLON,
181
-            ],
182
-            'end'    => [
183
-                T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
184
-                T_ENDWHILE            => T_ENDWHILE,
185
-            ],
186
-            'strict' => false,
187
-            'shared' => false,
188
-            'with'   => [],
189
-        ],
190
-        T_DO            => [
191
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
192
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
193
-            'strict' => true,
194
-            'shared' => false,
195
-            'with'   => [],
196
-        ],
197
-        T_SWITCH        => [
198
-            'start'  => [
199
-                T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
200
-                T_COLON              => T_COLON,
201
-            ],
202
-            'end'    => [
203
-                T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
204
-                T_ENDSWITCH           => T_ENDSWITCH,
205
-            ],
206
-            'strict' => true,
207
-            'shared' => false,
208
-            'with'   => [],
209
-        ],
210
-        T_CASE          => [
211
-            'start'  => [
212
-                T_COLON     => T_COLON,
213
-                T_SEMICOLON => T_SEMICOLON,
214
-            ],
215
-            'end'    => [
216
-                T_BREAK    => T_BREAK,
217
-                T_RETURN   => T_RETURN,
218
-                T_CONTINUE => T_CONTINUE,
219
-                T_THROW    => T_THROW,
220
-                T_EXIT     => T_EXIT,
221
-            ],
222
-            'strict' => true,
223
-            'shared' => true,
224
-            'with'   => [
225
-                T_DEFAULT => T_DEFAULT,
226
-                T_CASE    => T_CASE,
227
-                T_SWITCH  => T_SWITCH,
228
-            ],
229
-        ],
230
-        T_DEFAULT       => [
231
-            'start'  => [
232
-                T_COLON     => T_COLON,
233
-                T_SEMICOLON => T_SEMICOLON,
234
-            ],
235
-            'end'    => [
236
-                T_BREAK    => T_BREAK,
237
-                T_RETURN   => T_RETURN,
238
-                T_CONTINUE => T_CONTINUE,
239
-                T_THROW    => T_THROW,
240
-                T_EXIT     => T_EXIT,
241
-            ],
242
-            'strict' => true,
243
-            'shared' => true,
244
-            'with'   => [
245
-                T_CASE   => T_CASE,
246
-                T_SWITCH => T_SWITCH,
247
-            ],
248
-        ],
249
-        T_START_HEREDOC => [
250
-            'start'  => [T_START_HEREDOC => T_START_HEREDOC],
251
-            'end'    => [T_END_HEREDOC => T_END_HEREDOC],
252
-            'strict' => true,
253
-            'shared' => false,
254
-            'with'   => [],
255
-        ],
256
-        T_START_NOWDOC  => [
257
-            'start'  => [T_START_NOWDOC => T_START_NOWDOC],
258
-            'end'    => [T_END_NOWDOC => T_END_NOWDOC],
259
-            'strict' => true,
260
-            'shared' => false,
261
-            'with'   => [],
262
-        ],
263
-    ];
264
-
265
-    /**
266
-     * A list of tokens that end the scope.
267
-     *
268
-     * This array is just a unique collection of the end tokens
269
-     * from the scopeOpeners array. The data is duplicated here to
270
-     * save time during parsing of the file.
271
-     *
272
-     * @var array
273
-     */
274
-    public $endScopeTokens = [
275
-        T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
276
-        T_ENDIF               => T_ENDIF,
277
-        T_ENDFOR              => T_ENDFOR,
278
-        T_ENDFOREACH          => T_ENDFOREACH,
279
-        T_ENDWHILE            => T_ENDWHILE,
280
-        T_ENDSWITCH           => T_ENDSWITCH,
281
-        T_BREAK               => T_BREAK,
282
-        T_END_HEREDOC         => T_END_HEREDOC,
283
-    ];
284
-
285
-    /**
286
-     * Known lengths of tokens.
287
-     *
288
-     * @var array<int, int>
289
-     */
290
-    public $knownLengths = [
291
-        T_ABSTRACT                 => 8,
292
-        T_AND_EQUAL                => 2,
293
-        T_ARRAY                    => 5,
294
-        T_AS                       => 2,
295
-        T_BOOLEAN_AND              => 2,
296
-        T_BOOLEAN_OR               => 2,
297
-        T_BREAK                    => 5,
298
-        T_CALLABLE                 => 8,
299
-        T_CASE                     => 4,
300
-        T_CATCH                    => 5,
301
-        T_CLASS                    => 5,
302
-        T_CLASS_C                  => 9,
303
-        T_CLONE                    => 5,
304
-        T_CONCAT_EQUAL             => 2,
305
-        T_CONST                    => 5,
306
-        T_CONTINUE                 => 8,
307
-        T_CURLY_OPEN               => 2,
308
-        T_DEC                      => 2,
309
-        T_DECLARE                  => 7,
310
-        T_DEFAULT                  => 7,
311
-        T_DIR                      => 7,
312
-        T_DIV_EQUAL                => 2,
313
-        T_DO                       => 2,
314
-        T_DOLLAR_OPEN_CURLY_BRACES => 2,
315
-        T_DOUBLE_ARROW             => 2,
316
-        T_DOUBLE_COLON             => 2,
317
-        T_ECHO                     => 4,
318
-        T_ELSE                     => 4,
319
-        T_ELSEIF                   => 6,
320
-        T_EMPTY                    => 5,
321
-        T_ENDDECLARE               => 10,
322
-        T_ENDFOR                   => 6,
323
-        T_ENDFOREACH               => 10,
324
-        T_ENDIF                    => 5,
325
-        T_ENDSWITCH                => 9,
326
-        T_ENDWHILE                 => 8,
327
-        T_EVAL                     => 4,
328
-        T_EXTENDS                  => 7,
329
-        T_FILE                     => 8,
330
-        T_FINAL                    => 5,
331
-        T_FINALLY                  => 7,
332
-        T_FOR                      => 3,
333
-        T_FOREACH                  => 7,
334
-        T_FUNCTION                 => 8,
335
-        T_FUNC_C                   => 12,
336
-        T_GLOBAL                   => 6,
337
-        T_GOTO                     => 4,
338
-        T_HALT_COMPILER            => 15,
339
-        T_IF                       => 2,
340
-        T_IMPLEMENTS               => 10,
341
-        T_INC                      => 2,
342
-        T_INCLUDE                  => 7,
343
-        T_INCLUDE_ONCE             => 12,
344
-        T_INSTANCEOF               => 10,
345
-        T_INSTEADOF                => 9,
346
-        T_INTERFACE                => 9,
347
-        T_ISSET                    => 5,
348
-        T_IS_EQUAL                 => 2,
349
-        T_IS_GREATER_OR_EQUAL      => 2,
350
-        T_IS_IDENTICAL             => 3,
351
-        T_IS_NOT_EQUAL             => 2,
352
-        T_IS_NOT_IDENTICAL         => 3,
353
-        T_IS_SMALLER_OR_EQUAL      => 2,
354
-        T_LINE                     => 8,
355
-        T_LIST                     => 4,
356
-        T_LOGICAL_AND              => 3,
357
-        T_LOGICAL_OR               => 2,
358
-        T_LOGICAL_XOR              => 3,
359
-        T_METHOD_C                 => 10,
360
-        T_MINUS_EQUAL              => 2,
361
-        T_POW_EQUAL                => 3,
362
-        T_MOD_EQUAL                => 2,
363
-        T_MUL_EQUAL                => 2,
364
-        T_NAMESPACE                => 9,
365
-        T_NS_C                     => 13,
366
-        T_NS_SEPARATOR             => 1,
367
-        T_NEW                      => 3,
368
-        T_OBJECT_OPERATOR          => 2,
369
-        T_OPEN_TAG_WITH_ECHO       => 3,
370
-        T_OR_EQUAL                 => 2,
371
-        T_PLUS_EQUAL               => 2,
372
-        T_PRINT                    => 5,
373
-        T_PRIVATE                  => 7,
374
-        T_PUBLIC                   => 6,
375
-        T_PROTECTED                => 9,
376
-        T_REQUIRE                  => 7,
377
-        T_REQUIRE_ONCE             => 12,
378
-        T_RETURN                   => 6,
379
-        T_STATIC                   => 6,
380
-        T_SWITCH                   => 6,
381
-        T_THROW                    => 5,
382
-        T_TRAIT                    => 5,
383
-        T_TRAIT_C                  => 9,
384
-        T_TRY                      => 3,
385
-        T_UNSET                    => 5,
386
-        T_USE                      => 3,
387
-        T_VAR                      => 3,
388
-        T_WHILE                    => 5,
389
-        T_XOR_EQUAL                => 2,
390
-        T_YIELD                    => 5,
391
-        T_OPEN_CURLY_BRACKET       => 1,
392
-        T_CLOSE_CURLY_BRACKET      => 1,
393
-        T_OPEN_SQUARE_BRACKET      => 1,
394
-        T_CLOSE_SQUARE_BRACKET     => 1,
395
-        T_OPEN_PARENTHESIS         => 1,
396
-        T_CLOSE_PARENTHESIS        => 1,
397
-        T_COLON                    => 1,
398
-        T_STRING_CONCAT            => 1,
399
-        T_INLINE_THEN              => 1,
400
-        T_INLINE_ELSE              => 1,
401
-        T_NULLABLE                 => 1,
402
-        T_NULL                     => 4,
403
-        T_FALSE                    => 5,
404
-        T_TRUE                     => 4,
405
-        T_SEMICOLON                => 1,
406
-        T_EQUAL                    => 1,
407
-        T_MULTIPLY                 => 1,
408
-        T_DIVIDE                   => 1,
409
-        T_PLUS                     => 1,
410
-        T_MINUS                    => 1,
411
-        T_MODULUS                  => 1,
412
-        T_POW                      => 2,
413
-        T_SPACESHIP                => 3,
414
-        T_COALESCE                 => 2,
415
-        T_COALESCE_EQUAL           => 3,
416
-        T_BITWISE_AND              => 1,
417
-        T_BITWISE_OR               => 1,
418
-        T_BITWISE_XOR              => 1,
419
-        T_SL                       => 2,
420
-        T_SR                       => 2,
421
-        T_SL_EQUAL                 => 3,
422
-        T_SR_EQUAL                 => 3,
423
-        T_GREATER_THAN             => 1,
424
-        T_LESS_THAN                => 1,
425
-        T_BOOLEAN_NOT              => 1,
426
-        T_SELF                     => 4,
427
-        T_PARENT                   => 6,
428
-        T_COMMA                    => 1,
429
-        T_THIS                     => 4,
430
-        T_CLOSURE                  => 8,
431
-        T_BACKTICK                 => 1,
432
-        T_OPEN_SHORT_ARRAY         => 1,
433
-        T_CLOSE_SHORT_ARRAY        => 1,
434
-    ];
435
-
436
-
437
-    /**
438
-     * A cache of different token types, resolved into arrays.
439
-     *
440
-     * @var array
441
-     * @see standardiseToken()
442
-     */
443
-    private static $resolveTokenCache = [];
444
-
445
-
446
-    /**
447
-     * Creates an array of tokens when given some PHP code.
448
-     *
449
-     * Starts by using token_get_all() but does a lot of extra processing
450
-     * to insert information about the context of the token.
451
-     *
452
-     * @param string $string The string to tokenize.
453
-     *
454
-     * @return array
455
-     */
456
-    protected function tokenize($string)
457
-    {
458
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
459
-            echo "\t*** START PHP TOKENIZING ***".PHP_EOL;
460
-            $isWin = false;
461
-            if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
462
-                $isWin = true;
463
-            }
464
-        }
465
-
466
-        $tokens      = @token_get_all($string);
467
-        $finalTokens = [];
468
-
469
-        $newStackPtr       = 0;
470
-        $numTokens         = count($tokens);
471
-        $lastNotEmptyToken = 0;
472
-
473
-        $insideInlineIf = [];
474
-        $insideUseGroup = false;
475
-
476
-        $commentTokenizer = new Comment();
477
-
478
-        for ($stackPtr = 0; $stackPtr < $numTokens; $stackPtr++) {
479
-            // Special case for tokens we have needed to blank out.
480
-            if ($tokens[$stackPtr] === null) {
481
-                continue;
482
-            }
483
-
484
-            $token        = (array) $tokens[$stackPtr];
485
-            $tokenIsArray = isset($token[1]);
486
-
487
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
488
-                if ($tokenIsArray === true) {
489
-                    $type    = Util\Tokens::tokenName($token[0]);
490
-                    $content = Util\Common::prepareForOutput($token[1]);
491
-                } else {
492
-                    $newToken = self::resolveSimpleToken($token[0]);
493
-                    $type     = $newToken['type'];
494
-                    $content  = Util\Common::prepareForOutput($token[0]);
495
-                }
496
-
497
-                echo "\tProcess token ";
498
-                if ($tokenIsArray === true) {
499
-                    echo "[$stackPtr]";
500
-                } else {
501
-                    echo " $stackPtr ";
502
-                }
503
-
504
-                echo ": $type => $content";
505
-            }//end if
506
-
507
-            if ($newStackPtr > 0
508
-                && isset(Util\Tokens::$emptyTokens[$finalTokens[($newStackPtr - 1)]['code']]) === false
509
-            ) {
510
-                $lastNotEmptyToken = ($newStackPtr - 1);
511
-            }
512
-
513
-            /*
18
+	/**
19
+	 * A list of tokens that are allowed to open a scope.
20
+	 *
21
+	 * This array also contains information about what kind of token the scope
22
+	 * opener uses to open and close the scope, if the token strictly requires
23
+	 * an opener, if the token can share a scope closer, and who it can be shared
24
+	 * with. An example of a token that shares a scope closer is a CASE scope.
25
+	 *
26
+	 * @var array
27
+	 */
28
+	public $scopeOpeners = [
29
+		T_IF            => [
30
+			'start'  => [
31
+				T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
32
+				T_COLON              => T_COLON,
33
+			],
34
+			'end'    => [
35
+				T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
36
+				T_ENDIF               => T_ENDIF,
37
+				T_ELSE                => T_ELSE,
38
+				T_ELSEIF              => T_ELSEIF,
39
+			],
40
+			'strict' => false,
41
+			'shared' => false,
42
+			'with'   => [
43
+				T_ELSE   => T_ELSE,
44
+				T_ELSEIF => T_ELSEIF,
45
+			],
46
+		],
47
+		T_TRY           => [
48
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
49
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
50
+			'strict' => true,
51
+			'shared' => false,
52
+			'with'   => [],
53
+		],
54
+		T_CATCH         => [
55
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
56
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
57
+			'strict' => true,
58
+			'shared' => false,
59
+			'with'   => [],
60
+		],
61
+		T_FINALLY       => [
62
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
63
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
64
+			'strict' => true,
65
+			'shared' => false,
66
+			'with'   => [],
67
+		],
68
+		T_ELSE          => [
69
+			'start'  => [
70
+				T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
71
+				T_COLON              => T_COLON,
72
+			],
73
+			'end'    => [
74
+				T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
75
+				T_ENDIF               => T_ENDIF,
76
+			],
77
+			'strict' => false,
78
+			'shared' => false,
79
+			'with'   => [
80
+				T_IF     => T_IF,
81
+				T_ELSEIF => T_ELSEIF,
82
+			],
83
+		],
84
+		T_ELSEIF        => [
85
+			'start'  => [
86
+				T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
87
+				T_COLON              => T_COLON,
88
+			],
89
+			'end'    => [
90
+				T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
91
+				T_ENDIF               => T_ENDIF,
92
+				T_ELSE                => T_ELSE,
93
+				T_ELSEIF              => T_ELSEIF,
94
+			],
95
+			'strict' => false,
96
+			'shared' => false,
97
+			'with'   => [
98
+				T_IF   => T_IF,
99
+				T_ELSE => T_ELSE,
100
+			],
101
+		],
102
+		T_FOR           => [
103
+			'start'  => [
104
+				T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
105
+				T_COLON              => T_COLON,
106
+			],
107
+			'end'    => [
108
+				T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
109
+				T_ENDFOR              => T_ENDFOR,
110
+			],
111
+			'strict' => false,
112
+			'shared' => false,
113
+			'with'   => [],
114
+		],
115
+		T_FOREACH       => [
116
+			'start'  => [
117
+				T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
118
+				T_COLON              => T_COLON,
119
+			],
120
+			'end'    => [
121
+				T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
122
+				T_ENDFOREACH          => T_ENDFOREACH,
123
+			],
124
+			'strict' => false,
125
+			'shared' => false,
126
+			'with'   => [],
127
+		],
128
+		T_INTERFACE     => [
129
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
130
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
131
+			'strict' => true,
132
+			'shared' => false,
133
+			'with'   => [],
134
+		],
135
+		T_FUNCTION      => [
136
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
137
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
138
+			'strict' => true,
139
+			'shared' => false,
140
+			'with'   => [],
141
+		],
142
+		T_CLASS         => [
143
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
144
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
145
+			'strict' => true,
146
+			'shared' => false,
147
+			'with'   => [],
148
+		],
149
+		T_TRAIT         => [
150
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
151
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
152
+			'strict' => true,
153
+			'shared' => false,
154
+			'with'   => [],
155
+		],
156
+		T_USE           => [
157
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
158
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
159
+			'strict' => false,
160
+			'shared' => false,
161
+			'with'   => [],
162
+		],
163
+		T_DECLARE       => [
164
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
165
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
166
+			'strict' => false,
167
+			'shared' => false,
168
+			'with'   => [],
169
+		],
170
+		T_NAMESPACE     => [
171
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
172
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
173
+			'strict' => false,
174
+			'shared' => false,
175
+			'with'   => [],
176
+		],
177
+		T_WHILE         => [
178
+			'start'  => [
179
+				T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
180
+				T_COLON              => T_COLON,
181
+			],
182
+			'end'    => [
183
+				T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
184
+				T_ENDWHILE            => T_ENDWHILE,
185
+			],
186
+			'strict' => false,
187
+			'shared' => false,
188
+			'with'   => [],
189
+		],
190
+		T_DO            => [
191
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
192
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
193
+			'strict' => true,
194
+			'shared' => false,
195
+			'with'   => [],
196
+		],
197
+		T_SWITCH        => [
198
+			'start'  => [
199
+				T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
200
+				T_COLON              => T_COLON,
201
+			],
202
+			'end'    => [
203
+				T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
204
+				T_ENDSWITCH           => T_ENDSWITCH,
205
+			],
206
+			'strict' => true,
207
+			'shared' => false,
208
+			'with'   => [],
209
+		],
210
+		T_CASE          => [
211
+			'start'  => [
212
+				T_COLON     => T_COLON,
213
+				T_SEMICOLON => T_SEMICOLON,
214
+			],
215
+			'end'    => [
216
+				T_BREAK    => T_BREAK,
217
+				T_RETURN   => T_RETURN,
218
+				T_CONTINUE => T_CONTINUE,
219
+				T_THROW    => T_THROW,
220
+				T_EXIT     => T_EXIT,
221
+			],
222
+			'strict' => true,
223
+			'shared' => true,
224
+			'with'   => [
225
+				T_DEFAULT => T_DEFAULT,
226
+				T_CASE    => T_CASE,
227
+				T_SWITCH  => T_SWITCH,
228
+			],
229
+		],
230
+		T_DEFAULT       => [
231
+			'start'  => [
232
+				T_COLON     => T_COLON,
233
+				T_SEMICOLON => T_SEMICOLON,
234
+			],
235
+			'end'    => [
236
+				T_BREAK    => T_BREAK,
237
+				T_RETURN   => T_RETURN,
238
+				T_CONTINUE => T_CONTINUE,
239
+				T_THROW    => T_THROW,
240
+				T_EXIT     => T_EXIT,
241
+			],
242
+			'strict' => true,
243
+			'shared' => true,
244
+			'with'   => [
245
+				T_CASE   => T_CASE,
246
+				T_SWITCH => T_SWITCH,
247
+			],
248
+		],
249
+		T_START_HEREDOC => [
250
+			'start'  => [T_START_HEREDOC => T_START_HEREDOC],
251
+			'end'    => [T_END_HEREDOC => T_END_HEREDOC],
252
+			'strict' => true,
253
+			'shared' => false,
254
+			'with'   => [],
255
+		],
256
+		T_START_NOWDOC  => [
257
+			'start'  => [T_START_NOWDOC => T_START_NOWDOC],
258
+			'end'    => [T_END_NOWDOC => T_END_NOWDOC],
259
+			'strict' => true,
260
+			'shared' => false,
261
+			'with'   => [],
262
+		],
263
+	];
264
+
265
+	/**
266
+	 * A list of tokens that end the scope.
267
+	 *
268
+	 * This array is just a unique collection of the end tokens
269
+	 * from the scopeOpeners array. The data is duplicated here to
270
+	 * save time during parsing of the file.
271
+	 *
272
+	 * @var array
273
+	 */
274
+	public $endScopeTokens = [
275
+		T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
276
+		T_ENDIF               => T_ENDIF,
277
+		T_ENDFOR              => T_ENDFOR,
278
+		T_ENDFOREACH          => T_ENDFOREACH,
279
+		T_ENDWHILE            => T_ENDWHILE,
280
+		T_ENDSWITCH           => T_ENDSWITCH,
281
+		T_BREAK               => T_BREAK,
282
+		T_END_HEREDOC         => T_END_HEREDOC,
283
+	];
284
+
285
+	/**
286
+	 * Known lengths of tokens.
287
+	 *
288
+	 * @var array<int, int>
289
+	 */
290
+	public $knownLengths = [
291
+		T_ABSTRACT                 => 8,
292
+		T_AND_EQUAL                => 2,
293
+		T_ARRAY                    => 5,
294
+		T_AS                       => 2,
295
+		T_BOOLEAN_AND              => 2,
296
+		T_BOOLEAN_OR               => 2,
297
+		T_BREAK                    => 5,
298
+		T_CALLABLE                 => 8,
299
+		T_CASE                     => 4,
300
+		T_CATCH                    => 5,
301
+		T_CLASS                    => 5,
302
+		T_CLASS_C                  => 9,
303
+		T_CLONE                    => 5,
304
+		T_CONCAT_EQUAL             => 2,
305
+		T_CONST                    => 5,
306
+		T_CONTINUE                 => 8,
307
+		T_CURLY_OPEN               => 2,
308
+		T_DEC                      => 2,
309
+		T_DECLARE                  => 7,
310
+		T_DEFAULT                  => 7,
311
+		T_DIR                      => 7,
312
+		T_DIV_EQUAL                => 2,
313
+		T_DO                       => 2,
314
+		T_DOLLAR_OPEN_CURLY_BRACES => 2,
315
+		T_DOUBLE_ARROW             => 2,
316
+		T_DOUBLE_COLON             => 2,
317
+		T_ECHO                     => 4,
318
+		T_ELSE                     => 4,
319
+		T_ELSEIF                   => 6,
320
+		T_EMPTY                    => 5,
321
+		T_ENDDECLARE               => 10,
322
+		T_ENDFOR                   => 6,
323
+		T_ENDFOREACH               => 10,
324
+		T_ENDIF                    => 5,
325
+		T_ENDSWITCH                => 9,
326
+		T_ENDWHILE                 => 8,
327
+		T_EVAL                     => 4,
328
+		T_EXTENDS                  => 7,
329
+		T_FILE                     => 8,
330
+		T_FINAL                    => 5,
331
+		T_FINALLY                  => 7,
332
+		T_FOR                      => 3,
333
+		T_FOREACH                  => 7,
334
+		T_FUNCTION                 => 8,
335
+		T_FUNC_C                   => 12,
336
+		T_GLOBAL                   => 6,
337
+		T_GOTO                     => 4,
338
+		T_HALT_COMPILER            => 15,
339
+		T_IF                       => 2,
340
+		T_IMPLEMENTS               => 10,
341
+		T_INC                      => 2,
342
+		T_INCLUDE                  => 7,
343
+		T_INCLUDE_ONCE             => 12,
344
+		T_INSTANCEOF               => 10,
345
+		T_INSTEADOF                => 9,
346
+		T_INTERFACE                => 9,
347
+		T_ISSET                    => 5,
348
+		T_IS_EQUAL                 => 2,
349
+		T_IS_GREATER_OR_EQUAL      => 2,
350
+		T_IS_IDENTICAL             => 3,
351
+		T_IS_NOT_EQUAL             => 2,
352
+		T_IS_NOT_IDENTICAL         => 3,
353
+		T_IS_SMALLER_OR_EQUAL      => 2,
354
+		T_LINE                     => 8,
355
+		T_LIST                     => 4,
356
+		T_LOGICAL_AND              => 3,
357
+		T_LOGICAL_OR               => 2,
358
+		T_LOGICAL_XOR              => 3,
359
+		T_METHOD_C                 => 10,
360
+		T_MINUS_EQUAL              => 2,
361
+		T_POW_EQUAL                => 3,
362
+		T_MOD_EQUAL                => 2,
363
+		T_MUL_EQUAL                => 2,
364
+		T_NAMESPACE                => 9,
365
+		T_NS_C                     => 13,
366
+		T_NS_SEPARATOR             => 1,
367
+		T_NEW                      => 3,
368
+		T_OBJECT_OPERATOR          => 2,
369
+		T_OPEN_TAG_WITH_ECHO       => 3,
370
+		T_OR_EQUAL                 => 2,
371
+		T_PLUS_EQUAL               => 2,
372
+		T_PRINT                    => 5,
373
+		T_PRIVATE                  => 7,
374
+		T_PUBLIC                   => 6,
375
+		T_PROTECTED                => 9,
376
+		T_REQUIRE                  => 7,
377
+		T_REQUIRE_ONCE             => 12,
378
+		T_RETURN                   => 6,
379
+		T_STATIC                   => 6,
380
+		T_SWITCH                   => 6,
381
+		T_THROW                    => 5,
382
+		T_TRAIT                    => 5,
383
+		T_TRAIT_C                  => 9,
384
+		T_TRY                      => 3,
385
+		T_UNSET                    => 5,
386
+		T_USE                      => 3,
387
+		T_VAR                      => 3,
388
+		T_WHILE                    => 5,
389
+		T_XOR_EQUAL                => 2,
390
+		T_YIELD                    => 5,
391
+		T_OPEN_CURLY_BRACKET       => 1,
392
+		T_CLOSE_CURLY_BRACKET      => 1,
393
+		T_OPEN_SQUARE_BRACKET      => 1,
394
+		T_CLOSE_SQUARE_BRACKET     => 1,
395
+		T_OPEN_PARENTHESIS         => 1,
396
+		T_CLOSE_PARENTHESIS        => 1,
397
+		T_COLON                    => 1,
398
+		T_STRING_CONCAT            => 1,
399
+		T_INLINE_THEN              => 1,
400
+		T_INLINE_ELSE              => 1,
401
+		T_NULLABLE                 => 1,
402
+		T_NULL                     => 4,
403
+		T_FALSE                    => 5,
404
+		T_TRUE                     => 4,
405
+		T_SEMICOLON                => 1,
406
+		T_EQUAL                    => 1,
407
+		T_MULTIPLY                 => 1,
408
+		T_DIVIDE                   => 1,
409
+		T_PLUS                     => 1,
410
+		T_MINUS                    => 1,
411
+		T_MODULUS                  => 1,
412
+		T_POW                      => 2,
413
+		T_SPACESHIP                => 3,
414
+		T_COALESCE                 => 2,
415
+		T_COALESCE_EQUAL           => 3,
416
+		T_BITWISE_AND              => 1,
417
+		T_BITWISE_OR               => 1,
418
+		T_BITWISE_XOR              => 1,
419
+		T_SL                       => 2,
420
+		T_SR                       => 2,
421
+		T_SL_EQUAL                 => 3,
422
+		T_SR_EQUAL                 => 3,
423
+		T_GREATER_THAN             => 1,
424
+		T_LESS_THAN                => 1,
425
+		T_BOOLEAN_NOT              => 1,
426
+		T_SELF                     => 4,
427
+		T_PARENT                   => 6,
428
+		T_COMMA                    => 1,
429
+		T_THIS                     => 4,
430
+		T_CLOSURE                  => 8,
431
+		T_BACKTICK                 => 1,
432
+		T_OPEN_SHORT_ARRAY         => 1,
433
+		T_CLOSE_SHORT_ARRAY        => 1,
434
+	];
435
+
436
+
437
+	/**
438
+	 * A cache of different token types, resolved into arrays.
439
+	 *
440
+	 * @var array
441
+	 * @see standardiseToken()
442
+	 */
443
+	private static $resolveTokenCache = [];
444
+
445
+
446
+	/**
447
+	 * Creates an array of tokens when given some PHP code.
448
+	 *
449
+	 * Starts by using token_get_all() but does a lot of extra processing
450
+	 * to insert information about the context of the token.
451
+	 *
452
+	 * @param string $string The string to tokenize.
453
+	 *
454
+	 * @return array
455
+	 */
456
+	protected function tokenize($string)
457
+	{
458
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
459
+			echo "\t*** START PHP TOKENIZING ***".PHP_EOL;
460
+			$isWin = false;
461
+			if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
462
+				$isWin = true;
463
+			}
464
+		}
465
+
466
+		$tokens      = @token_get_all($string);
467
+		$finalTokens = [];
468
+
469
+		$newStackPtr       = 0;
470
+		$numTokens         = count($tokens);
471
+		$lastNotEmptyToken = 0;
472
+
473
+		$insideInlineIf = [];
474
+		$insideUseGroup = false;
475
+
476
+		$commentTokenizer = new Comment();
477
+
478
+		for ($stackPtr = 0; $stackPtr < $numTokens; $stackPtr++) {
479
+			// Special case for tokens we have needed to blank out.
480
+			if ($tokens[$stackPtr] === null) {
481
+				continue;
482
+			}
483
+
484
+			$token        = (array) $tokens[$stackPtr];
485
+			$tokenIsArray = isset($token[1]);
486
+
487
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
488
+				if ($tokenIsArray === true) {
489
+					$type    = Util\Tokens::tokenName($token[0]);
490
+					$content = Util\Common::prepareForOutput($token[1]);
491
+				} else {
492
+					$newToken = self::resolveSimpleToken($token[0]);
493
+					$type     = $newToken['type'];
494
+					$content  = Util\Common::prepareForOutput($token[0]);
495
+				}
496
+
497
+				echo "\tProcess token ";
498
+				if ($tokenIsArray === true) {
499
+					echo "[$stackPtr]";
500
+				} else {
501
+					echo " $stackPtr ";
502
+				}
503
+
504
+				echo ": $type => $content";
505
+			}//end if
506
+
507
+			if ($newStackPtr > 0
508
+				&& isset(Util\Tokens::$emptyTokens[$finalTokens[($newStackPtr - 1)]['code']]) === false
509
+			) {
510
+				$lastNotEmptyToken = ($newStackPtr - 1);
511
+			}
512
+
513
+			/*
514 514
                 If we are using \r\n newline characters, the \r and \n are sometimes
515 515
                 split over two tokens. This normally occurs after comments. We need
516 516
                 to merge these two characters together so that our line endings are
517 517
                 consistent for all lines.
518 518
             */
519 519
 
520
-            if ($tokenIsArray === true && substr($token[1], -1) === "\r") {
521
-                if (isset($tokens[($stackPtr + 1)]) === true
522
-                    && is_array($tokens[($stackPtr + 1)]) === true
523
-                    && $tokens[($stackPtr + 1)][1][0] === "\n"
524
-                ) {
525
-                    $token[1] .= "\n";
526
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
527
-                        if ($isWin === true) {
528
-                            echo '\n';
529
-                        } else {
530
-                            echo "\033[30;1m\\n\033[0m";
531
-                        }
532
-                    }
533
-
534
-                    if ($tokens[($stackPtr + 1)][1] === "\n") {
535
-                        // This token's content has been merged into the previous,
536
-                        // so we can skip it.
537
-                        $tokens[($stackPtr + 1)] = '';
538
-                    } else {
539
-                        $tokens[($stackPtr + 1)][1] = substr($tokens[($stackPtr + 1)][1], 1);
540
-                    }
541
-                }
542
-            }//end if
543
-
544
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
545
-                echo PHP_EOL;
546
-            }
547
-
548
-            /*
520
+			if ($tokenIsArray === true && substr($token[1], -1) === "\r") {
521
+				if (isset($tokens[($stackPtr + 1)]) === true
522
+					&& is_array($tokens[($stackPtr + 1)]) === true
523
+					&& $tokens[($stackPtr + 1)][1][0] === "\n"
524
+				) {
525
+					$token[1] .= "\n";
526
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
527
+						if ($isWin === true) {
528
+							echo '\n';
529
+						} else {
530
+							echo "\033[30;1m\\n\033[0m";
531
+						}
532
+					}
533
+
534
+					if ($tokens[($stackPtr + 1)][1] === "\n") {
535
+						// This token's content has been merged into the previous,
536
+						// so we can skip it.
537
+						$tokens[($stackPtr + 1)] = '';
538
+					} else {
539
+						$tokens[($stackPtr + 1)][1] = substr($tokens[($stackPtr + 1)][1], 1);
540
+					}
541
+				}
542
+			}//end if
543
+
544
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
545
+				echo PHP_EOL;
546
+			}
547
+
548
+			/*
549 549
                 Parse doc blocks into something that can be easily iterated over.
550 550
             */
551 551
 
552
-            if ($tokenIsArray === true
553
-                && ($token[0] === T_DOC_COMMENT
554
-                || ($token[0] === T_COMMENT && strpos($token[1], '/**') === 0))
555
-            ) {
556
-                $commentTokens = $commentTokenizer->tokenizeString($token[1], $this->eolChar, $newStackPtr);
557
-                foreach ($commentTokens as $commentToken) {
558
-                    $finalTokens[$newStackPtr] = $commentToken;
559
-                    $newStackPtr++;
560
-                }
552
+			if ($tokenIsArray === true
553
+				&& ($token[0] === T_DOC_COMMENT
554
+				|| ($token[0] === T_COMMENT && strpos($token[1], '/**') === 0))
555
+			) {
556
+				$commentTokens = $commentTokenizer->tokenizeString($token[1], $this->eolChar, $newStackPtr);
557
+				foreach ($commentTokens as $commentToken) {
558
+					$finalTokens[$newStackPtr] = $commentToken;
559
+					$newStackPtr++;
560
+				}
561 561
 
562
-                continue;
563
-            }
562
+				continue;
563
+			}
564 564
 
565
-            /*
565
+			/*
566 566
                 If this is a double quoted string, PHP will tokenize the whole
567 567
                 thing which causes problems with the scope map when braces are
568 568
                 within the string. So we need to merge the tokens together to
569 569
                 provide a single string.
570 570
             */
571 571
 
572
-            if ($tokenIsArray === false && ($token[0] === '"' || $token[0] === 'b"')) {
573
-                // Binary casts need a special token.
574
-                if ($token[0] === 'b"') {
575
-                    $finalTokens[$newStackPtr] = [
576
-                        'code'    => T_BINARY_CAST,
577
-                        'type'    => 'T_BINARY_CAST',
578
-                        'content' => 'b',
579
-                    ];
580
-                    $newStackPtr++;
581
-                }
582
-
583
-                $tokenContent = '"';
584
-                $nestedVars   = [];
585
-                for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
586
-                    $subToken        = (array) $tokens[$i];
587
-                    $subTokenIsArray = isset($subToken[1]);
588
-
589
-                    if ($subTokenIsArray === true) {
590
-                        $tokenContent .= $subToken[1];
591
-                        if ($subToken[1] === '{'
592
-                            && $subToken[0] !== T_ENCAPSED_AND_WHITESPACE
593
-                        ) {
594
-                            $nestedVars[] = $i;
595
-                        }
596
-                    } else {
597
-                        $tokenContent .= $subToken[0];
598
-                        if ($subToken[0] === '}') {
599
-                            array_pop($nestedVars);
600
-                        }
601
-                    }
602
-
603
-                    if ($subTokenIsArray === false
604
-                        && $subToken[0] === '"'
605
-                        && empty($nestedVars) === true
606
-                    ) {
607
-                        // We found the other end of the double quoted string.
608
-                        break;
609
-                    }
610
-                }//end for
611
-
612
-                $stackPtr = $i;
613
-
614
-                // Convert each line within the double quoted string to a
615
-                // new token, so it conforms with other multiple line tokens.
616
-                $tokenLines = explode($this->eolChar, $tokenContent);
617
-                $numLines   = count($tokenLines);
618
-                $newToken   = [];
619
-
620
-                for ($j = 0; $j < $numLines; $j++) {
621
-                    $newToken['content'] = $tokenLines[$j];
622
-                    if ($j === ($numLines - 1)) {
623
-                        if ($tokenLines[$j] === '') {
624
-                            break;
625
-                        }
626
-                    } else {
627
-                        $newToken['content'] .= $this->eolChar;
628
-                    }
629
-
630
-                    $newToken['code']          = T_DOUBLE_QUOTED_STRING;
631
-                    $newToken['type']          = 'T_DOUBLE_QUOTED_STRING';
632
-                    $finalTokens[$newStackPtr] = $newToken;
633
-                    $newStackPtr++;
634
-                }
635
-
636
-                // Continue, as we're done with this token.
637
-                continue;
638
-            }//end if
639
-
640
-            /*
572
+			if ($tokenIsArray === false && ($token[0] === '"' || $token[0] === 'b"')) {
573
+				// Binary casts need a special token.
574
+				if ($token[0] === 'b"') {
575
+					$finalTokens[$newStackPtr] = [
576
+						'code'    => T_BINARY_CAST,
577
+						'type'    => 'T_BINARY_CAST',
578
+						'content' => 'b',
579
+					];
580
+					$newStackPtr++;
581
+				}
582
+
583
+				$tokenContent = '"';
584
+				$nestedVars   = [];
585
+				for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
586
+					$subToken        = (array) $tokens[$i];
587
+					$subTokenIsArray = isset($subToken[1]);
588
+
589
+					if ($subTokenIsArray === true) {
590
+						$tokenContent .= $subToken[1];
591
+						if ($subToken[1] === '{'
592
+							&& $subToken[0] !== T_ENCAPSED_AND_WHITESPACE
593
+						) {
594
+							$nestedVars[] = $i;
595
+						}
596
+					} else {
597
+						$tokenContent .= $subToken[0];
598
+						if ($subToken[0] === '}') {
599
+							array_pop($nestedVars);
600
+						}
601
+					}
602
+
603
+					if ($subTokenIsArray === false
604
+						&& $subToken[0] === '"'
605
+						&& empty($nestedVars) === true
606
+					) {
607
+						// We found the other end of the double quoted string.
608
+						break;
609
+					}
610
+				}//end for
611
+
612
+				$stackPtr = $i;
613
+
614
+				// Convert each line within the double quoted string to a
615
+				// new token, so it conforms with other multiple line tokens.
616
+				$tokenLines = explode($this->eolChar, $tokenContent);
617
+				$numLines   = count($tokenLines);
618
+				$newToken   = [];
619
+
620
+				for ($j = 0; $j < $numLines; $j++) {
621
+					$newToken['content'] = $tokenLines[$j];
622
+					if ($j === ($numLines - 1)) {
623
+						if ($tokenLines[$j] === '') {
624
+							break;
625
+						}
626
+					} else {
627
+						$newToken['content'] .= $this->eolChar;
628
+					}
629
+
630
+					$newToken['code']          = T_DOUBLE_QUOTED_STRING;
631
+					$newToken['type']          = 'T_DOUBLE_QUOTED_STRING';
632
+					$finalTokens[$newStackPtr] = $newToken;
633
+					$newStackPtr++;
634
+				}
635
+
636
+				// Continue, as we're done with this token.
637
+				continue;
638
+			}//end if
639
+
640
+			/*
641 641
                 Detect binary casting and assign the casts their own token.
642 642
             */
643 643
 
644
-            if ($tokenIsArray === true
645
-                && $token[0] === T_CONSTANT_ENCAPSED_STRING
646
-                && (substr($token[1], 0, 2) === 'b"'
647
-                || substr($token[1], 0, 2) === "b'")
648
-            ) {
649
-                $finalTokens[$newStackPtr] = [
650
-                    'code'    => T_BINARY_CAST,
651
-                    'type'    => 'T_BINARY_CAST',
652
-                    'content' => 'b',
653
-                ];
654
-                $newStackPtr++;
655
-                $token[1] = substr($token[1], 1);
656
-            }
657
-
658
-            if ($tokenIsArray === true
659
-                && $token[0] === T_STRING_CAST
660
-                && preg_match('`^\(\s*binary\s*\)$`i', $token[1]) === 1
661
-            ) {
662
-                $finalTokens[$newStackPtr] = [
663
-                    'code'    => T_BINARY_CAST,
664
-                    'type'    => 'T_BINARY_CAST',
665
-                    'content' => $token[1],
666
-                ];
667
-                $newStackPtr++;
668
-                continue;
669
-            }
670
-
671
-            /*
644
+			if ($tokenIsArray === true
645
+				&& $token[0] === T_CONSTANT_ENCAPSED_STRING
646
+				&& (substr($token[1], 0, 2) === 'b"'
647
+				|| substr($token[1], 0, 2) === "b'")
648
+			) {
649
+				$finalTokens[$newStackPtr] = [
650
+					'code'    => T_BINARY_CAST,
651
+					'type'    => 'T_BINARY_CAST',
652
+					'content' => 'b',
653
+				];
654
+				$newStackPtr++;
655
+				$token[1] = substr($token[1], 1);
656
+			}
657
+
658
+			if ($tokenIsArray === true
659
+				&& $token[0] === T_STRING_CAST
660
+				&& preg_match('`^\(\s*binary\s*\)$`i', $token[1]) === 1
661
+			) {
662
+				$finalTokens[$newStackPtr] = [
663
+					'code'    => T_BINARY_CAST,
664
+					'type'    => 'T_BINARY_CAST',
665
+					'content' => $token[1],
666
+				];
667
+				$newStackPtr++;
668
+				continue;
669
+			}
670
+
671
+			/*
672 672
                 If this is a heredoc, PHP will tokenize the whole
673 673
                 thing which causes problems when heredocs don't
674 674
                 contain real PHP code, which is almost never.
@@ -676,243 +676,243 @@  discard block
 block discarded – undo
676 676
                 alone though.
677 677
             */
678 678
 
679
-            if ($tokenIsArray === true && $token[0] === T_START_HEREDOC) {
680
-                // Add the start heredoc token to the final array.
681
-                $finalTokens[$newStackPtr] = self::standardiseToken($token);
682
-
683
-                // Check if this is actually a nowdoc and use a different token
684
-                // to help the sniffs.
685
-                $nowdoc = false;
686
-                if (strpos($token[1], "'") !== false) {
687
-                    $finalTokens[$newStackPtr]['code'] = T_START_NOWDOC;
688
-                    $finalTokens[$newStackPtr]['type'] = 'T_START_NOWDOC';
689
-                    $nowdoc = true;
690
-                }
691
-
692
-                $tokenContent = '';
693
-                for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
694
-                    $subTokenIsArray = is_array($tokens[$i]);
695
-                    if ($subTokenIsArray === true
696
-                        && $tokens[$i][0] === T_END_HEREDOC
697
-                    ) {
698
-                        // We found the other end of the heredoc.
699
-                        break;
700
-                    }
701
-
702
-                    if ($subTokenIsArray === true) {
703
-                        $tokenContent .= $tokens[$i][1];
704
-                    } else {
705
-                        $tokenContent .= $tokens[$i];
706
-                    }
707
-                }
708
-
709
-                if ($i === $numTokens) {
710
-                    // We got to the end of the file and never
711
-                    // found the closing token, so this probably wasn't
712
-                    // a heredoc.
713
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
714
-                        $type = $finalTokens[$newStackPtr]['type'];
715
-                        echo "\t\t* failed to find the end of the here/nowdoc".PHP_EOL;
716
-                        echo "\t\t* token $stackPtr changed from $type to T_STRING".PHP_EOL;
717
-                    }
718
-
719
-                    $finalTokens[$newStackPtr]['code'] = T_STRING;
720
-                    $finalTokens[$newStackPtr]['type'] = 'T_STRING';
721
-                    $newStackPtr++;
722
-                    continue;
723
-                }
724
-
725
-                $stackPtr = $i;
726
-                $newStackPtr++;
727
-
728
-                // Convert each line within the heredoc to a
729
-                // new token, so it conforms with other multiple line tokens.
730
-                $tokenLines = explode($this->eolChar, $tokenContent);
731
-                $numLines   = count($tokenLines);
732
-                $newToken   = [];
733
-
734
-                for ($j = 0; $j < $numLines; $j++) {
735
-                    $newToken['content'] = $tokenLines[$j];
736
-                    if ($j === ($numLines - 1)) {
737
-                        if ($tokenLines[$j] === '') {
738
-                            break;
739
-                        }
740
-                    } else {
741
-                        $newToken['content'] .= $this->eolChar;
742
-                    }
743
-
744
-                    if ($nowdoc === true) {
745
-                        $newToken['code'] = T_NOWDOC;
746
-                        $newToken['type'] = 'T_NOWDOC';
747
-                    } else {
748
-                        $newToken['code'] = T_HEREDOC;
749
-                        $newToken['type'] = 'T_HEREDOC';
750
-                    }
751
-
752
-                    $finalTokens[$newStackPtr] = $newToken;
753
-                    $newStackPtr++;
754
-                }//end for
755
-
756
-                // Add the end heredoc token to the final array.
757
-                $finalTokens[$newStackPtr] = self::standardiseToken($tokens[$stackPtr]);
758
-
759
-                if ($nowdoc === true) {
760
-                    $finalTokens[$newStackPtr]['code'] = T_END_NOWDOC;
761
-                    $finalTokens[$newStackPtr]['type'] = 'T_END_NOWDOC';
762
-                }
763
-
764
-                $newStackPtr++;
765
-
766
-                // Continue, as we're done with this token.
767
-                continue;
768
-            }//end if
769
-
770
-            /*
679
+			if ($tokenIsArray === true && $token[0] === T_START_HEREDOC) {
680
+				// Add the start heredoc token to the final array.
681
+				$finalTokens[$newStackPtr] = self::standardiseToken($token);
682
+
683
+				// Check if this is actually a nowdoc and use a different token
684
+				// to help the sniffs.
685
+				$nowdoc = false;
686
+				if (strpos($token[1], "'") !== false) {
687
+					$finalTokens[$newStackPtr]['code'] = T_START_NOWDOC;
688
+					$finalTokens[$newStackPtr]['type'] = 'T_START_NOWDOC';
689
+					$nowdoc = true;
690
+				}
691
+
692
+				$tokenContent = '';
693
+				for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
694
+					$subTokenIsArray = is_array($tokens[$i]);
695
+					if ($subTokenIsArray === true
696
+						&& $tokens[$i][0] === T_END_HEREDOC
697
+					) {
698
+						// We found the other end of the heredoc.
699
+						break;
700
+					}
701
+
702
+					if ($subTokenIsArray === true) {
703
+						$tokenContent .= $tokens[$i][1];
704
+					} else {
705
+						$tokenContent .= $tokens[$i];
706
+					}
707
+				}
708
+
709
+				if ($i === $numTokens) {
710
+					// We got to the end of the file and never
711
+					// found the closing token, so this probably wasn't
712
+					// a heredoc.
713
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
714
+						$type = $finalTokens[$newStackPtr]['type'];
715
+						echo "\t\t* failed to find the end of the here/nowdoc".PHP_EOL;
716
+						echo "\t\t* token $stackPtr changed from $type to T_STRING".PHP_EOL;
717
+					}
718
+
719
+					$finalTokens[$newStackPtr]['code'] = T_STRING;
720
+					$finalTokens[$newStackPtr]['type'] = 'T_STRING';
721
+					$newStackPtr++;
722
+					continue;
723
+				}
724
+
725
+				$stackPtr = $i;
726
+				$newStackPtr++;
727
+
728
+				// Convert each line within the heredoc to a
729
+				// new token, so it conforms with other multiple line tokens.
730
+				$tokenLines = explode($this->eolChar, $tokenContent);
731
+				$numLines   = count($tokenLines);
732
+				$newToken   = [];
733
+
734
+				for ($j = 0; $j < $numLines; $j++) {
735
+					$newToken['content'] = $tokenLines[$j];
736
+					if ($j === ($numLines - 1)) {
737
+						if ($tokenLines[$j] === '') {
738
+							break;
739
+						}
740
+					} else {
741
+						$newToken['content'] .= $this->eolChar;
742
+					}
743
+
744
+					if ($nowdoc === true) {
745
+						$newToken['code'] = T_NOWDOC;
746
+						$newToken['type'] = 'T_NOWDOC';
747
+					} else {
748
+						$newToken['code'] = T_HEREDOC;
749
+						$newToken['type'] = 'T_HEREDOC';
750
+					}
751
+
752
+					$finalTokens[$newStackPtr] = $newToken;
753
+					$newStackPtr++;
754
+				}//end for
755
+
756
+				// Add the end heredoc token to the final array.
757
+				$finalTokens[$newStackPtr] = self::standardiseToken($tokens[$stackPtr]);
758
+
759
+				if ($nowdoc === true) {
760
+					$finalTokens[$newStackPtr]['code'] = T_END_NOWDOC;
761
+					$finalTokens[$newStackPtr]['type'] = 'T_END_NOWDOC';
762
+				}
763
+
764
+				$newStackPtr++;
765
+
766
+				// Continue, as we're done with this token.
767
+				continue;
768
+			}//end if
769
+
770
+			/*
771 771
                 Before PHP 7.0, the "yield from" was tokenized as
772 772
                 T_YIELD, T_WHITESPACE and T_STRING. So look for
773 773
                 and change this token in earlier versions.
774 774
             */
775 775
 
776
-            if (PHP_VERSION_ID < 70000
777
-                && PHP_VERSION_ID >= 50500
778
-                && $tokenIsArray === true
779
-                && $token[0] === T_YIELD
780
-                && isset($tokens[($stackPtr + 1)]) === true
781
-                && isset($tokens[($stackPtr + 2)]) === true
782
-                && $tokens[($stackPtr + 1)][0] === T_WHITESPACE
783
-                && $tokens[($stackPtr + 2)][0] === T_STRING
784
-                && strtolower($tokens[($stackPtr + 2)][1]) === 'from'
785
-            ) {
786
-                // Could be multi-line, so just the token stack.
787
-                $token[0]  = T_YIELD_FROM;
788
-                $token[1] .= $tokens[($stackPtr + 1)][1].$tokens[($stackPtr + 2)][1];
789
-
790
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
791
-                    for ($i = ($stackPtr + 1); $i <= ($stackPtr + 2); $i++) {
792
-                        $type    = Util\Tokens::tokenName($tokens[$i][0]);
793
-                        $content = Util\Common::prepareForOutput($tokens[$i][1]);
794
-                        echo "\t\t* token $i merged into T_YIELD_FROM; was: $type => $content".PHP_EOL;
795
-                    }
796
-                }
797
-
798
-                $tokens[($stackPtr + 1)] = null;
799
-                $tokens[($stackPtr + 2)] = null;
800
-            }
801
-
802
-            /*
776
+			if (PHP_VERSION_ID < 70000
777
+				&& PHP_VERSION_ID >= 50500
778
+				&& $tokenIsArray === true
779
+				&& $token[0] === T_YIELD
780
+				&& isset($tokens[($stackPtr + 1)]) === true
781
+				&& isset($tokens[($stackPtr + 2)]) === true
782
+				&& $tokens[($stackPtr + 1)][0] === T_WHITESPACE
783
+				&& $tokens[($stackPtr + 2)][0] === T_STRING
784
+				&& strtolower($tokens[($stackPtr + 2)][1]) === 'from'
785
+			) {
786
+				// Could be multi-line, so just the token stack.
787
+				$token[0]  = T_YIELD_FROM;
788
+				$token[1] .= $tokens[($stackPtr + 1)][1].$tokens[($stackPtr + 2)][1];
789
+
790
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
791
+					for ($i = ($stackPtr + 1); $i <= ($stackPtr + 2); $i++) {
792
+						$type    = Util\Tokens::tokenName($tokens[$i][0]);
793
+						$content = Util\Common::prepareForOutput($tokens[$i][1]);
794
+						echo "\t\t* token $i merged into T_YIELD_FROM; was: $type => $content".PHP_EOL;
795
+					}
796
+				}
797
+
798
+				$tokens[($stackPtr + 1)] = null;
799
+				$tokens[($stackPtr + 2)] = null;
800
+			}
801
+
802
+			/*
803 803
                 Before PHP 5.5, the yield keyword was tokenized as
804 804
                 T_STRING. So look for and change this token in
805 805
                 earlier versions.
806 806
                 Checks also if it is just "yield" or "yield from".
807 807
             */
808 808
 
809
-            if (PHP_VERSION_ID < 50500
810
-                && $tokenIsArray === true
811
-                && $token[0] === T_STRING
812
-                && strtolower($token[1]) === 'yield'
813
-            ) {
814
-                if (isset($tokens[($stackPtr + 1)]) === true
815
-                    && isset($tokens[($stackPtr + 2)]) === true
816
-                    && $tokens[($stackPtr + 1)][0] === T_WHITESPACE
817
-                    && $tokens[($stackPtr + 2)][0] === T_STRING
818
-                    && strtolower($tokens[($stackPtr + 2)][1]) === 'from'
819
-                ) {
820
-                    // Could be multi-line, so just just the token stack.
821
-                    $token[0]  = T_YIELD_FROM;
822
-                    $token[1] .= $tokens[($stackPtr + 1)][1].$tokens[($stackPtr + 2)][1];
823
-
824
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
825
-                        for ($i = ($stackPtr + 1); $i <= ($stackPtr + 2); $i++) {
826
-                            $type    = Util\Tokens::tokenName($tokens[$i][0]);
827
-                            $content = Util\Common::prepareForOutput($tokens[$i][1]);
828
-                            echo "\t\t* token $i merged into T_YIELD_FROM; was: $type => $content".PHP_EOL;
829
-                        }
830
-                    }
831
-
832
-                    $tokens[($stackPtr + 1)] = null;
833
-                    $tokens[($stackPtr + 2)] = null;
834
-                } else {
835
-                    $newToken            = [];
836
-                    $newToken['code']    = T_YIELD;
837
-                    $newToken['type']    = 'T_YIELD';
838
-                    $newToken['content'] = $token[1];
839
-                    $finalTokens[$newStackPtr] = $newToken;
840
-
841
-                    $newStackPtr++;
842
-                    continue;
843
-                }//end if
844
-            }//end if
845
-
846
-            /*
809
+			if (PHP_VERSION_ID < 50500
810
+				&& $tokenIsArray === true
811
+				&& $token[0] === T_STRING
812
+				&& strtolower($token[1]) === 'yield'
813
+			) {
814
+				if (isset($tokens[($stackPtr + 1)]) === true
815
+					&& isset($tokens[($stackPtr + 2)]) === true
816
+					&& $tokens[($stackPtr + 1)][0] === T_WHITESPACE
817
+					&& $tokens[($stackPtr + 2)][0] === T_STRING
818
+					&& strtolower($tokens[($stackPtr + 2)][1]) === 'from'
819
+				) {
820
+					// Could be multi-line, so just just the token stack.
821
+					$token[0]  = T_YIELD_FROM;
822
+					$token[1] .= $tokens[($stackPtr + 1)][1].$tokens[($stackPtr + 2)][1];
823
+
824
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
825
+						for ($i = ($stackPtr + 1); $i <= ($stackPtr + 2); $i++) {
826
+							$type    = Util\Tokens::tokenName($tokens[$i][0]);
827
+							$content = Util\Common::prepareForOutput($tokens[$i][1]);
828
+							echo "\t\t* token $i merged into T_YIELD_FROM; was: $type => $content".PHP_EOL;
829
+						}
830
+					}
831
+
832
+					$tokens[($stackPtr + 1)] = null;
833
+					$tokens[($stackPtr + 2)] = null;
834
+				} else {
835
+					$newToken            = [];
836
+					$newToken['code']    = T_YIELD;
837
+					$newToken['type']    = 'T_YIELD';
838
+					$newToken['content'] = $token[1];
839
+					$finalTokens[$newStackPtr] = $newToken;
840
+
841
+					$newStackPtr++;
842
+					continue;
843
+				}//end if
844
+			}//end if
845
+
846
+			/*
847 847
                 Before PHP 5.6, the ... operator was tokenized as three
848 848
                 T_STRING_CONCAT tokens in a row. So look for and combine
849 849
                 these tokens in earlier versions.
850 850
             */
851 851
 
852
-            if ($tokenIsArray === false
853
-                && $token[0] === '.'
854
-                && isset($tokens[($stackPtr + 1)]) === true
855
-                && isset($tokens[($stackPtr + 2)]) === true
856
-                && $tokens[($stackPtr + 1)] === '.'
857
-                && $tokens[($stackPtr + 2)] === '.'
858
-            ) {
859
-                $newToken            = [];
860
-                $newToken['code']    = T_ELLIPSIS;
861
-                $newToken['type']    = 'T_ELLIPSIS';
862
-                $newToken['content'] = '...';
863
-                $finalTokens[$newStackPtr] = $newToken;
864
-
865
-                $newStackPtr++;
866
-                $stackPtr += 2;
867
-                continue;
868
-            }
869
-
870
-            /*
852
+			if ($tokenIsArray === false
853
+				&& $token[0] === '.'
854
+				&& isset($tokens[($stackPtr + 1)]) === true
855
+				&& isset($tokens[($stackPtr + 2)]) === true
856
+				&& $tokens[($stackPtr + 1)] === '.'
857
+				&& $tokens[($stackPtr + 2)] === '.'
858
+			) {
859
+				$newToken            = [];
860
+				$newToken['code']    = T_ELLIPSIS;
861
+				$newToken['type']    = 'T_ELLIPSIS';
862
+				$newToken['content'] = '...';
863
+				$finalTokens[$newStackPtr] = $newToken;
864
+
865
+				$newStackPtr++;
866
+				$stackPtr += 2;
867
+				continue;
868
+			}
869
+
870
+			/*
871 871
                 Before PHP 5.6, the ** operator was tokenized as two
872 872
                 T_MULTIPLY tokens in a row. So look for and combine
873 873
                 these tokens in earlier versions.
874 874
             */
875 875
 
876
-            if ($tokenIsArray === false
877
-                && $token[0] === '*'
878
-                && isset($tokens[($stackPtr + 1)]) === true
879
-                && $tokens[($stackPtr + 1)] === '*'
880
-            ) {
881
-                $newToken            = [];
882
-                $newToken['code']    = T_POW;
883
-                $newToken['type']    = 'T_POW';
884
-                $newToken['content'] = '**';
885
-                $finalTokens[$newStackPtr] = $newToken;
886
-
887
-                $newStackPtr++;
888
-                $stackPtr++;
889
-                continue;
890
-            }
891
-
892
-            /*
876
+			if ($tokenIsArray === false
877
+				&& $token[0] === '*'
878
+				&& isset($tokens[($stackPtr + 1)]) === true
879
+				&& $tokens[($stackPtr + 1)] === '*'
880
+			) {
881
+				$newToken            = [];
882
+				$newToken['code']    = T_POW;
883
+				$newToken['type']    = 'T_POW';
884
+				$newToken['content'] = '**';
885
+				$finalTokens[$newStackPtr] = $newToken;
886
+
887
+				$newStackPtr++;
888
+				$stackPtr++;
889
+				continue;
890
+			}
891
+
892
+			/*
893 893
                 Before PHP 5.6, the **= operator was tokenized as
894 894
                 T_MULTIPLY followed by T_MUL_EQUAL. So look for and combine
895 895
                 these tokens in earlier versions.
896 896
             */
897 897
 
898
-            if ($tokenIsArray === false
899
-                && $token[0] === '*'
900
-                && isset($tokens[($stackPtr + 1)]) === true
901
-                && is_array($tokens[($stackPtr + 1)]) === true
902
-                && $tokens[($stackPtr + 1)][1] === '*='
903
-            ) {
904
-                $newToken            = [];
905
-                $newToken['code']    = T_POW_EQUAL;
906
-                $newToken['type']    = 'T_POW_EQUAL';
907
-                $newToken['content'] = '**=';
908
-                $finalTokens[$newStackPtr] = $newToken;
909
-
910
-                $newStackPtr++;
911
-                $stackPtr++;
912
-                continue;
913
-            }
914
-
915
-            /*
898
+			if ($tokenIsArray === false
899
+				&& $token[0] === '*'
900
+				&& isset($tokens[($stackPtr + 1)]) === true
901
+				&& is_array($tokens[($stackPtr + 1)]) === true
902
+				&& $tokens[($stackPtr + 1)][1] === '*='
903
+			) {
904
+				$newToken            = [];
905
+				$newToken['code']    = T_POW_EQUAL;
906
+				$newToken['type']    = 'T_POW_EQUAL';
907
+				$newToken['content'] = '**=';
908
+				$finalTokens[$newStackPtr] = $newToken;
909
+
910
+				$newStackPtr++;
911
+				$stackPtr++;
912
+				continue;
913
+			}
914
+
915
+			/*
916 916
                 Before PHP 7, the ??= operator was tokenized as
917 917
                 T_INLINE_THEN, T_INLINE_THEN, T_EQUAL.
918 918
                 Between PHP 7.0 and 7.2, the ??= operator was tokenized as
@@ -920,277 +920,277 @@  discard block
 block discarded – undo
920 920
                 So look for and combine these tokens in earlier versions.
921 921
             */
922 922
 
923
-            if (($tokenIsArray === false
924
-                && $token[0] === '?'
925
-                && isset($tokens[($stackPtr + 1)]) === true
926
-                && $tokens[($stackPtr + 1)][0] === '?'
927
-                && isset($tokens[($stackPtr + 2)]) === true
928
-                && $tokens[($stackPtr + 2)][0] === '=')
929
-                || ($tokenIsArray === true
930
-                && $token[0] === T_COALESCE
931
-                && isset($tokens[($stackPtr + 1)]) === true
932
-                && $tokens[($stackPtr + 1)][0] === '=')
933
-            ) {
934
-                $newToken            = [];
935
-                $newToken['code']    = T_COALESCE_EQUAL;
936
-                $newToken['type']    = 'T_COALESCE_EQUAL';
937
-                $newToken['content'] = '??=';
938
-                $finalTokens[$newStackPtr] = $newToken;
939
-
940
-                $newStackPtr++;
941
-                $stackPtr++;
942
-
943
-                if ($tokenIsArray === false) {
944
-                    // Pre PHP 7.
945
-                    $stackPtr++;
946
-                }
947
-
948
-                continue;
949
-            }
950
-
951
-            /*
923
+			if (($tokenIsArray === false
924
+				&& $token[0] === '?'
925
+				&& isset($tokens[($stackPtr + 1)]) === true
926
+				&& $tokens[($stackPtr + 1)][0] === '?'
927
+				&& isset($tokens[($stackPtr + 2)]) === true
928
+				&& $tokens[($stackPtr + 2)][0] === '=')
929
+				|| ($tokenIsArray === true
930
+				&& $token[0] === T_COALESCE
931
+				&& isset($tokens[($stackPtr + 1)]) === true
932
+				&& $tokens[($stackPtr + 1)][0] === '=')
933
+			) {
934
+				$newToken            = [];
935
+				$newToken['code']    = T_COALESCE_EQUAL;
936
+				$newToken['type']    = 'T_COALESCE_EQUAL';
937
+				$newToken['content'] = '??=';
938
+				$finalTokens[$newStackPtr] = $newToken;
939
+
940
+				$newStackPtr++;
941
+				$stackPtr++;
942
+
943
+				if ($tokenIsArray === false) {
944
+					// Pre PHP 7.
945
+					$stackPtr++;
946
+				}
947
+
948
+				continue;
949
+			}
950
+
951
+			/*
952 952
                 Before PHP 7, the ?? operator was tokenized as
953 953
                 T_INLINE_THEN followed by T_INLINE_THEN.
954 954
                 So look for and combine these tokens in earlier versions.
955 955
             */
956 956
 
957
-            if ($tokenIsArray === false
958
-                && $token[0] === '?'
959
-                && isset($tokens[($stackPtr + 1)]) === true
960
-                && $tokens[($stackPtr + 1)][0] === '?'
961
-            ) {
962
-                $newToken            = [];
963
-                $newToken['code']    = T_COALESCE;
964
-                $newToken['type']    = 'T_COALESCE';
965
-                $newToken['content'] = '??';
966
-                $finalTokens[$newStackPtr] = $newToken;
967
-
968
-                $newStackPtr++;
969
-                $stackPtr++;
970
-                continue;
971
-            }
972
-
973
-            /*
957
+			if ($tokenIsArray === false
958
+				&& $token[0] === '?'
959
+				&& isset($tokens[($stackPtr + 1)]) === true
960
+				&& $tokens[($stackPtr + 1)][0] === '?'
961
+			) {
962
+				$newToken            = [];
963
+				$newToken['code']    = T_COALESCE;
964
+				$newToken['type']    = 'T_COALESCE';
965
+				$newToken['content'] = '??';
966
+				$finalTokens[$newStackPtr] = $newToken;
967
+
968
+				$newStackPtr++;
969
+				$stackPtr++;
970
+				continue;
971
+			}
972
+
973
+			/*
974 974
                 Convert ? to T_NULLABLE OR T_INLINE_THEN
975 975
             */
976 976
 
977
-            if ($tokenIsArray === false && $token[0] === '?') {
978
-                $newToken            = [];
979
-                $newToken['content'] = '?';
980
-
981
-                $prevNonEmpty = null;
982
-                for ($i = ($stackPtr - 1); $i >= 0; $i--) {
983
-                    if (is_array($tokens[$i]) === true) {
984
-                        $tokenType = $tokens[$i][0];
985
-                    } else {
986
-                        $tokenType = $tokens[$i];
987
-                    }
988
-
989
-                    if ($prevNonEmpty === null
990
-                        && isset(Util\Tokens::$emptyTokens[$tokenType]) === false
991
-                    ) {
992
-                        // Found the previous non-empty token.
993
-                        if ($tokenType === ':' || $tokenType === ',') {
994
-                            $newToken['code'] = T_NULLABLE;
995
-                            $newToken['type'] = 'T_NULLABLE';
996
-                            break;
997
-                        }
998
-
999
-                        $prevNonEmpty = $tokenType;
1000
-                    }
1001
-
1002
-                    if ($tokenType === T_FUNCTION) {
1003
-                        $newToken['code'] = T_NULLABLE;
1004
-                        $newToken['type'] = 'T_NULLABLE';
1005
-                        break;
1006
-                    } else if (in_array($tokenType, [T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, '=', '{', ';'], true) === true) {
1007
-                        $newToken['code'] = T_INLINE_THEN;
1008
-                        $newToken['type'] = 'T_INLINE_THEN';
1009
-
1010
-                        $insideInlineIf[] = $stackPtr;
1011
-                        break;
1012
-                    }
1013
-                }//end for
1014
-
1015
-                $finalTokens[$newStackPtr] = $newToken;
1016
-                $newStackPtr++;
1017
-                continue;
1018
-            }//end if
1019
-
1020
-            /*
977
+			if ($tokenIsArray === false && $token[0] === '?') {
978
+				$newToken            = [];
979
+				$newToken['content'] = '?';
980
+
981
+				$prevNonEmpty = null;
982
+				for ($i = ($stackPtr - 1); $i >= 0; $i--) {
983
+					if (is_array($tokens[$i]) === true) {
984
+						$tokenType = $tokens[$i][0];
985
+					} else {
986
+						$tokenType = $tokens[$i];
987
+					}
988
+
989
+					if ($prevNonEmpty === null
990
+						&& isset(Util\Tokens::$emptyTokens[$tokenType]) === false
991
+					) {
992
+						// Found the previous non-empty token.
993
+						if ($tokenType === ':' || $tokenType === ',') {
994
+							$newToken['code'] = T_NULLABLE;
995
+							$newToken['type'] = 'T_NULLABLE';
996
+							break;
997
+						}
998
+
999
+						$prevNonEmpty = $tokenType;
1000
+					}
1001
+
1002
+					if ($tokenType === T_FUNCTION) {
1003
+						$newToken['code'] = T_NULLABLE;
1004
+						$newToken['type'] = 'T_NULLABLE';
1005
+						break;
1006
+					} else if (in_array($tokenType, [T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, '=', '{', ';'], true) === true) {
1007
+						$newToken['code'] = T_INLINE_THEN;
1008
+						$newToken['type'] = 'T_INLINE_THEN';
1009
+
1010
+						$insideInlineIf[] = $stackPtr;
1011
+						break;
1012
+					}
1013
+				}//end for
1014
+
1015
+				$finalTokens[$newStackPtr] = $newToken;
1016
+				$newStackPtr++;
1017
+				continue;
1018
+			}//end if
1019
+
1020
+			/*
1021 1021
                 Tokens after a double colon may be look like scope openers,
1022 1022
                 such as when writing code like Foo::NAMESPACE, but they are
1023 1023
                 only ever variables or strings.
1024 1024
             */
1025 1025
 
1026
-            if ($stackPtr > 1
1027
-                && (is_array($tokens[($stackPtr - 1)]) === true
1028
-                && $tokens[($stackPtr - 1)][0] === T_PAAMAYIM_NEKUDOTAYIM)
1029
-                && $tokenIsArray === true
1030
-                && $token[0] !== T_STRING
1031
-                && $token[0] !== T_VARIABLE
1032
-                && $token[0] !== T_DOLLAR
1033
-                && isset(Util\Tokens::$emptyTokens[$token[0]]) === false
1034
-            ) {
1035
-                $newToken            = [];
1036
-                $newToken['code']    = T_STRING;
1037
-                $newToken['type']    = 'T_STRING';
1038
-                $newToken['content'] = $token[1];
1039
-                $finalTokens[$newStackPtr] = $newToken;
1040
-
1041
-                $newStackPtr++;
1042
-                continue;
1043
-            }
1044
-
1045
-            /*
1026
+			if ($stackPtr > 1
1027
+				&& (is_array($tokens[($stackPtr - 1)]) === true
1028
+				&& $tokens[($stackPtr - 1)][0] === T_PAAMAYIM_NEKUDOTAYIM)
1029
+				&& $tokenIsArray === true
1030
+				&& $token[0] !== T_STRING
1031
+				&& $token[0] !== T_VARIABLE
1032
+				&& $token[0] !== T_DOLLAR
1033
+				&& isset(Util\Tokens::$emptyTokens[$token[0]]) === false
1034
+			) {
1035
+				$newToken            = [];
1036
+				$newToken['code']    = T_STRING;
1037
+				$newToken['type']    = 'T_STRING';
1038
+				$newToken['content'] = $token[1];
1039
+				$finalTokens[$newStackPtr] = $newToken;
1040
+
1041
+				$newStackPtr++;
1042
+				continue;
1043
+			}
1044
+
1045
+			/*
1046 1046
                 The string-like token after a function keyword should always be
1047 1047
                 tokenized as T_STRING even if it appears to be a different token,
1048 1048
                 such as when writing code like: function default(): foo
1049 1049
                 so go forward and change the token type before it is processed.
1050 1050
             */
1051 1051
 
1052
-            if ($tokenIsArray === true
1053
-                && $token[0] === T_FUNCTION
1054
-                && $finalTokens[$lastNotEmptyToken]['code'] !== T_USE
1055
-            ) {
1056
-                for ($x = ($stackPtr + 1); $x < $numTokens; $x++) {
1057
-                    if (is_array($tokens[$x]) === false
1058
-                        || isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === false
1059
-                    ) {
1060
-                        // Non-empty content.
1061
-                        break;
1062
-                    }
1063
-                }
1064
-
1065
-                if ($x < $numTokens && is_array($tokens[$x]) === true) {
1066
-                    $tokens[$x][0] = T_STRING;
1067
-                }
1068
-
1069
-                /*
1052
+			if ($tokenIsArray === true
1053
+				&& $token[0] === T_FUNCTION
1054
+				&& $finalTokens[$lastNotEmptyToken]['code'] !== T_USE
1055
+			) {
1056
+				for ($x = ($stackPtr + 1); $x < $numTokens; $x++) {
1057
+					if (is_array($tokens[$x]) === false
1058
+						|| isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === false
1059
+					) {
1060
+						// Non-empty content.
1061
+						break;
1062
+					}
1063
+				}
1064
+
1065
+				if ($x < $numTokens && is_array($tokens[$x]) === true) {
1066
+					$tokens[$x][0] = T_STRING;
1067
+				}
1068
+
1069
+				/*
1070 1070
                     This is a special condition for T_ARRAY tokens used for
1071 1071
                     function return types. We want to keep the parenthesis map clean,
1072 1072
                     so let's tag these tokens as T_STRING.
1073 1073
                 */
1074 1074
 
1075
-                // Go looking for the colon to start the return type hint.
1076
-                // Start by finding the closing parenthesis of the function.
1077
-                $parenthesisStack  = [];
1078
-                $parenthesisCloser = false;
1079
-                for ($x = ($stackPtr + 1); $x < $numTokens; $x++) {
1080
-                    if (is_array($tokens[$x]) === false && $tokens[$x] === '(') {
1081
-                        $parenthesisStack[] = $x;
1082
-                    } else if (is_array($tokens[$x]) === false && $tokens[$x] === ')') {
1083
-                        array_pop($parenthesisStack);
1084
-                        if (empty($parenthesisStack) === true) {
1085
-                            $parenthesisCloser = $x;
1086
-                            break;
1087
-                        }
1088
-                    }
1089
-                }
1090
-
1091
-                if ($parenthesisCloser !== false) {
1092
-                    for ($x = ($parenthesisCloser + 1); $x < $numTokens; $x++) {
1093
-                        if (is_array($tokens[$x]) === false
1094
-                            || isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === false
1095
-                        ) {
1096
-                            // Non-empty content.
1097
-                            if (is_array($tokens[$x]) === true && $tokens[$x][0] === T_USE) {
1098
-                                // Found a use statements, so search ahead for the closing parenthesis.
1099
-                                for ($x += 1; $x < $numTokens; $x++) {
1100
-                                    if (is_array($tokens[$x]) === false && $tokens[$x] === ')') {
1101
-                                        continue(2);
1102
-                                    }
1103
-                                }
1104
-                            }
1105
-
1106
-                            break;
1107
-                        }
1108
-                    }
1109
-
1110
-                    if (isset($tokens[$x]) === true
1111
-                        && is_array($tokens[$x]) === false
1112
-                        && $tokens[$x] === ':'
1113
-                    ) {
1114
-                        $allowed = [
1115
-                            T_STRING       => T_STRING,
1116
-                            T_ARRAY        => T_ARRAY,
1117
-                            T_CALLABLE     => T_CALLABLE,
1118
-                            T_SELF         => T_SELF,
1119
-                            T_PARENT       => T_PARENT,
1120
-                            T_NS_SEPARATOR => T_NS_SEPARATOR,
1121
-                        ];
1122
-
1123
-                        $allowed += Util\Tokens::$emptyTokens;
1124
-
1125
-                        // Find the start of the return type.
1126
-                        for ($x += 1; $x < $numTokens; $x++) {
1127
-                            if (is_array($tokens[$x]) === true
1128
-                                && isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === true
1129
-                            ) {
1130
-                                // Whitespace or comments before the return type.
1131
-                                continue;
1132
-                            }
1133
-
1134
-                            if (is_array($tokens[$x]) === false && $tokens[$x] === '?') {
1135
-                                // Found a nullable operator, so skip it.
1136
-                                // But also covert the token to save the tokenizer
1137
-                                // a bit of time later on.
1138
-                                $tokens[$x] = [
1139
-                                    T_NULLABLE,
1140
-                                    '?',
1141
-                                ];
1142
-
1143
-                                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1144
-                                    echo "\t\t* token $x changed from ? to T_NULLABLE".PHP_EOL;
1145
-                                }
1146
-
1147
-                                continue;
1148
-                            }
1149
-
1150
-                            break;
1151
-                        }//end for
1152
-
1153
-                        // Any T_ARRAY tokens we find between here and the next
1154
-                        // token that can't be part of the return type need to be
1155
-                        // converted to T_STRING tokens.
1156
-                        for ($x; $x < $numTokens; $x++) {
1157
-                            if (is_array($tokens[$x]) === false || isset($allowed[$tokens[$x][0]]) === false) {
1158
-                                break;
1159
-                            } else if ($tokens[$x][0] === T_ARRAY) {
1160
-                                $tokens[$x][0] = T_STRING;
1161
-
1162
-                                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1163
-                                    echo "\t\t* token $x changed from T_ARRAY to T_STRING".PHP_EOL;
1164
-                                }
1165
-                            }
1166
-                        }
1167
-                    }//end if
1168
-                }//end if
1169
-            }//end if
1170
-
1171
-            /*
1075
+				// Go looking for the colon to start the return type hint.
1076
+				// Start by finding the closing parenthesis of the function.
1077
+				$parenthesisStack  = [];
1078
+				$parenthesisCloser = false;
1079
+				for ($x = ($stackPtr + 1); $x < $numTokens; $x++) {
1080
+					if (is_array($tokens[$x]) === false && $tokens[$x] === '(') {
1081
+						$parenthesisStack[] = $x;
1082
+					} else if (is_array($tokens[$x]) === false && $tokens[$x] === ')') {
1083
+						array_pop($parenthesisStack);
1084
+						if (empty($parenthesisStack) === true) {
1085
+							$parenthesisCloser = $x;
1086
+							break;
1087
+						}
1088
+					}
1089
+				}
1090
+
1091
+				if ($parenthesisCloser !== false) {
1092
+					for ($x = ($parenthesisCloser + 1); $x < $numTokens; $x++) {
1093
+						if (is_array($tokens[$x]) === false
1094
+							|| isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === false
1095
+						) {
1096
+							// Non-empty content.
1097
+							if (is_array($tokens[$x]) === true && $tokens[$x][0] === T_USE) {
1098
+								// Found a use statements, so search ahead for the closing parenthesis.
1099
+								for ($x += 1; $x < $numTokens; $x++) {
1100
+									if (is_array($tokens[$x]) === false && $tokens[$x] === ')') {
1101
+										continue(2);
1102
+									}
1103
+								}
1104
+							}
1105
+
1106
+							break;
1107
+						}
1108
+					}
1109
+
1110
+					if (isset($tokens[$x]) === true
1111
+						&& is_array($tokens[$x]) === false
1112
+						&& $tokens[$x] === ':'
1113
+					) {
1114
+						$allowed = [
1115
+							T_STRING       => T_STRING,
1116
+							T_ARRAY        => T_ARRAY,
1117
+							T_CALLABLE     => T_CALLABLE,
1118
+							T_SELF         => T_SELF,
1119
+							T_PARENT       => T_PARENT,
1120
+							T_NS_SEPARATOR => T_NS_SEPARATOR,
1121
+						];
1122
+
1123
+						$allowed += Util\Tokens::$emptyTokens;
1124
+
1125
+						// Find the start of the return type.
1126
+						for ($x += 1; $x < $numTokens; $x++) {
1127
+							if (is_array($tokens[$x]) === true
1128
+								&& isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === true
1129
+							) {
1130
+								// Whitespace or comments before the return type.
1131
+								continue;
1132
+							}
1133
+
1134
+							if (is_array($tokens[$x]) === false && $tokens[$x] === '?') {
1135
+								// Found a nullable operator, so skip it.
1136
+								// But also covert the token to save the tokenizer
1137
+								// a bit of time later on.
1138
+								$tokens[$x] = [
1139
+									T_NULLABLE,
1140
+									'?',
1141
+								];
1142
+
1143
+								if (PHP_CODESNIFFER_VERBOSITY > 1) {
1144
+									echo "\t\t* token $x changed from ? to T_NULLABLE".PHP_EOL;
1145
+								}
1146
+
1147
+								continue;
1148
+							}
1149
+
1150
+							break;
1151
+						}//end for
1152
+
1153
+						// Any T_ARRAY tokens we find between here and the next
1154
+						// token that can't be part of the return type need to be
1155
+						// converted to T_STRING tokens.
1156
+						for ($x; $x < $numTokens; $x++) {
1157
+							if (is_array($tokens[$x]) === false || isset($allowed[$tokens[$x][0]]) === false) {
1158
+								break;
1159
+							} else if ($tokens[$x][0] === T_ARRAY) {
1160
+								$tokens[$x][0] = T_STRING;
1161
+
1162
+								if (PHP_CODESNIFFER_VERBOSITY > 1) {
1163
+									echo "\t\t* token $x changed from T_ARRAY to T_STRING".PHP_EOL;
1164
+								}
1165
+							}
1166
+						}
1167
+					}//end if
1168
+				}//end if
1169
+			}//end if
1170
+
1171
+			/*
1172 1172
                 Before PHP 7, the <=> operator was tokenized as
1173 1173
                 T_IS_SMALLER_OR_EQUAL followed by T_GREATER_THAN.
1174 1174
                 So look for and combine these tokens in earlier versions.
1175 1175
             */
1176 1176
 
1177
-            if ($tokenIsArray === true
1178
-                && $token[0] === T_IS_SMALLER_OR_EQUAL
1179
-                && isset($tokens[($stackPtr + 1)]) === true
1180
-                && $tokens[($stackPtr + 1)][0] === '>'
1181
-            ) {
1182
-                $newToken            = [];
1183
-                $newToken['code']    = T_SPACESHIP;
1184
-                $newToken['type']    = 'T_SPACESHIP';
1185
-                $newToken['content'] = '<=>';
1186
-                $finalTokens[$newStackPtr] = $newToken;
1187
-
1188
-                $newStackPtr++;
1189
-                $stackPtr++;
1190
-                continue;
1191
-            }
1192
-
1193
-            /*
1177
+			if ($tokenIsArray === true
1178
+				&& $token[0] === T_IS_SMALLER_OR_EQUAL
1179
+				&& isset($tokens[($stackPtr + 1)]) === true
1180
+				&& $tokens[($stackPtr + 1)][0] === '>'
1181
+			) {
1182
+				$newToken            = [];
1183
+				$newToken['code']    = T_SPACESHIP;
1184
+				$newToken['type']    = 'T_SPACESHIP';
1185
+				$newToken['content'] = '<=>';
1186
+				$finalTokens[$newStackPtr] = $newToken;
1187
+
1188
+				$newStackPtr++;
1189
+				$stackPtr++;
1190
+				continue;
1191
+			}
1192
+
1193
+			/*
1194 1194
                 PHP doesn't assign a token to goto labels, so we have to.
1195 1195
                 These are just string tokens with a single colon after them. Double
1196 1196
                 colons are already tokenized and so don't interfere with this check.
@@ -1198,820 +1198,820 @@  discard block
 block discarded – undo
1198 1198
                 goto labels.
1199 1199
             */
1200 1200
 
1201
-            if ($tokenIsArray === true
1202
-                && $token[0] === T_STRING
1203
-                && isset($tokens[($stackPtr + 1)]) === true
1204
-                && $tokens[($stackPtr + 1)] === ':'
1205
-                && $tokens[($stackPtr - 1)][0] !== T_PAAMAYIM_NEKUDOTAYIM
1206
-            ) {
1207
-                $stopTokens = [
1208
-                    T_CASE               => true,
1209
-                    T_SEMICOLON          => true,
1210
-                    T_OPEN_CURLY_BRACKET => true,
1211
-                    T_INLINE_THEN        => true,
1212
-                ];
1213
-
1214
-                for ($x = ($newStackPtr - 1); $x > 0; $x--) {
1215
-                    if (isset($stopTokens[$finalTokens[$x]['code']]) === true) {
1216
-                        break;
1217
-                    }
1218
-                }
1219
-
1220
-                if ($finalTokens[$x]['code'] !== T_CASE
1221
-                    && $finalTokens[$x]['code'] !== T_INLINE_THEN
1222
-                ) {
1223
-                    $finalTokens[$newStackPtr] = [
1224
-                        'content' => $token[1].':',
1225
-                        'code'    => T_GOTO_LABEL,
1226
-                        'type'    => 'T_GOTO_LABEL',
1227
-                    ];
1228
-
1229
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1230
-                        echo "\t\t* token $stackPtr changed from T_STRING to T_GOTO_LABEL".PHP_EOL;
1231
-                        echo "\t\t* skipping T_COLON token ".($stackPtr + 1).PHP_EOL;
1232
-                    }
1233
-
1234
-                    $newStackPtr++;
1235
-                    $stackPtr++;
1236
-                    continue;
1237
-                }
1238
-            }//end if
1239
-
1240
-            /*
1201
+			if ($tokenIsArray === true
1202
+				&& $token[0] === T_STRING
1203
+				&& isset($tokens[($stackPtr + 1)]) === true
1204
+				&& $tokens[($stackPtr + 1)] === ':'
1205
+				&& $tokens[($stackPtr - 1)][0] !== T_PAAMAYIM_NEKUDOTAYIM
1206
+			) {
1207
+				$stopTokens = [
1208
+					T_CASE               => true,
1209
+					T_SEMICOLON          => true,
1210
+					T_OPEN_CURLY_BRACKET => true,
1211
+					T_INLINE_THEN        => true,
1212
+				];
1213
+
1214
+				for ($x = ($newStackPtr - 1); $x > 0; $x--) {
1215
+					if (isset($stopTokens[$finalTokens[$x]['code']]) === true) {
1216
+						break;
1217
+					}
1218
+				}
1219
+
1220
+				if ($finalTokens[$x]['code'] !== T_CASE
1221
+					&& $finalTokens[$x]['code'] !== T_INLINE_THEN
1222
+				) {
1223
+					$finalTokens[$newStackPtr] = [
1224
+						'content' => $token[1].':',
1225
+						'code'    => T_GOTO_LABEL,
1226
+						'type'    => 'T_GOTO_LABEL',
1227
+					];
1228
+
1229
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1230
+						echo "\t\t* token $stackPtr changed from T_STRING to T_GOTO_LABEL".PHP_EOL;
1231
+						echo "\t\t* skipping T_COLON token ".($stackPtr + 1).PHP_EOL;
1232
+					}
1233
+
1234
+					$newStackPtr++;
1235
+					$stackPtr++;
1236
+					continue;
1237
+				}
1238
+			}//end if
1239
+
1240
+			/*
1241 1241
                 If this token has newlines in its content, split each line up
1242 1242
                 and create a new token for each line. We do this so it's easier
1243 1243
                 to ascertain where errors occur on a line.
1244 1244
                 Note that $token[1] is the token's content.
1245 1245
             */
1246 1246
 
1247
-            if ($tokenIsArray === true && strpos($token[1], $this->eolChar) !== false) {
1248
-                $tokenLines = explode($this->eolChar, $token[1]);
1249
-                $numLines   = count($tokenLines);
1250
-                $newToken   = [
1251
-                    'type'    => Util\Tokens::tokenName($token[0]),
1252
-                    'code'    => $token[0],
1253
-                    'content' => '',
1254
-                ];
1255
-
1256
-                for ($i = 0; $i < $numLines; $i++) {
1257
-                    $newToken['content'] = $tokenLines[$i];
1258
-                    if ($i === ($numLines - 1)) {
1259
-                        if ($tokenLines[$i] === '') {
1260
-                            break;
1261
-                        }
1262
-                    } else {
1263
-                        $newToken['content'] .= $this->eolChar;
1264
-                    }
1265
-
1266
-                    $finalTokens[$newStackPtr] = $newToken;
1267
-                    $newStackPtr++;
1268
-                }
1269
-            } else {
1270
-                if ($tokenIsArray === true && $token[0] === T_STRING) {
1271
-                    // Some T_STRING tokens should remain that way
1272
-                    // due to their context.
1273
-                    $context = [
1274
-                        T_OBJECT_OPERATOR      => true,
1275
-                        T_FUNCTION             => true,
1276
-                        T_CLASS                => true,
1277
-                        T_EXTENDS              => true,
1278
-                        T_IMPLEMENTS           => true,
1279
-                        T_NEW                  => true,
1280
-                        T_CONST                => true,
1281
-                        T_NS_SEPARATOR         => true,
1282
-                        T_USE                  => true,
1283
-                        T_NAMESPACE            => true,
1284
-                        T_PAAMAYIM_NEKUDOTAYIM => true,
1285
-                    ];
1286
-
1287
-                    if (isset($context[$finalTokens[$lastNotEmptyToken]['code']]) === true) {
1288
-                        // Special case for syntax like: return new self
1289
-                        // where self should not be a string.
1290
-                        if ($finalTokens[$lastNotEmptyToken]['code'] === T_NEW
1291
-                            && strtolower($token[1]) === 'self'
1292
-                        ) {
1293
-                            $finalTokens[$newStackPtr] = [
1294
-                                'content' => $token[1],
1295
-                                'code'    => T_SELF,
1296
-                                'type'    => 'T_SELF',
1297
-                            ];
1298
-                        } else {
1299
-                            $finalTokens[$newStackPtr] = [
1300
-                                'content' => $token[1],
1301
-                                'code'    => T_STRING,
1302
-                                'type'    => 'T_STRING',
1303
-                            ];
1304
-                        }
1305
-
1306
-                        $newStackPtr++;
1307
-                        continue;
1308
-                    }//end if
1309
-                }//end if
1310
-
1311
-                $newToken = null;
1312
-                if ($tokenIsArray === false) {
1313
-                    if (isset(self::$resolveTokenCache[$token[0]]) === true) {
1314
-                        $newToken = self::$resolveTokenCache[$token[0]];
1315
-                    }
1316
-                } else {
1317
-                    $cacheKey = null;
1318
-                    if ($token[0] === T_STRING) {
1319
-                        $cacheKey = strtolower($token[1]);
1320
-                    } else if ($token[0] !== T_CURLY_OPEN) {
1321
-                        $cacheKey = $token[0];
1322
-                    }
1323
-
1324
-                    if ($cacheKey !== null && isset(self::$resolveTokenCache[$cacheKey]) === true) {
1325
-                        $newToken            = self::$resolveTokenCache[$cacheKey];
1326
-                        $newToken['content'] = $token[1];
1327
-                    }
1328
-                }
1329
-
1330
-                if ($newToken === null) {
1331
-                    $newToken = self::standardiseToken($token);
1332
-                }
1333
-
1334
-                // Convert colons that are actually the ELSE component of an
1335
-                // inline IF statement.
1336
-                if (empty($insideInlineIf) === false && $newToken['code'] === T_COLON) {
1337
-                    // Make sure this isn't the return type separator of a closure.
1338
-                    $isReturnType = false;
1339
-                    for ($i = ($stackPtr - 1); $i > 0; $i--) {
1340
-                        if (is_array($tokens[$i]) === false
1341
-                            || ($tokens[$i][0] !== T_DOC_COMMENT
1342
-                            && $tokens[$i][0] !== T_COMMENT
1343
-                            && $tokens[$i][0] !== T_WHITESPACE)
1344
-                        ) {
1345
-                            break;
1346
-                        }
1347
-                    }
1348
-
1349
-                    if ($tokens[$i] === ')') {
1350
-                        $parenCount = 1;
1351
-                        for ($i--; $i > 0; $i--) {
1352
-                            if ($tokens[$i] === '(') {
1353
-                                $parenCount--;
1354
-                                if ($parenCount === 0) {
1355
-                                    break;
1356
-                                }
1357
-                            } else if ($tokens[$i] === ')') {
1358
-                                $parenCount++;
1359
-                            }
1360
-                        }
1361
-
1362
-                        // We've found the open parenthesis, so if the previous
1363
-                        // non-empty token is FUNCTION or USE, this is a closure.
1364
-                        for ($i--; $i > 0; $i--) {
1365
-                            if (is_array($tokens[$i]) === false
1366
-                                || ($tokens[$i][0] !== T_DOC_COMMENT
1367
-                                && $tokens[$i][0] !== T_COMMENT
1368
-                                && $tokens[$i][0] !== T_WHITESPACE)
1369
-                            ) {
1370
-                                break;
1371
-                            }
1372
-                        }
1373
-
1374
-                        if ($tokens[$i][0] === T_FUNCTION || $tokens[$i][0] === T_USE) {
1375
-                            $isReturnType = true;
1376
-                        }
1377
-                    }//end if
1378
-
1379
-                    if ($isReturnType === false) {
1380
-                        array_pop($insideInlineIf);
1381
-                        $newToken['code'] = T_INLINE_ELSE;
1382
-                        $newToken['type'] = 'T_INLINE_ELSE';
1383
-                    }
1384
-                }//end if
1385
-
1386
-                // This is a special condition for T_ARRAY tokens used for
1387
-                // type hinting function arguments as being arrays. We want to keep
1388
-                // the parenthesis map clean, so let's tag these tokens as
1389
-                // T_STRING.
1390
-                if ($newToken['code'] === T_ARRAY) {
1391
-                    for ($i = $stackPtr; $i < $numTokens; $i++) {
1392
-                        if ($tokens[$i] === '(') {
1393
-                            break;
1394
-                        } else if ($tokens[$i][0] === T_VARIABLE) {
1395
-                            $newToken['code'] = T_STRING;
1396
-                            $newToken['type'] = 'T_STRING';
1397
-                            break;
1398
-                        }
1399
-                    }
1400
-                }
1401
-
1402
-                // This is a special case when checking PHP 5.5+ code in PHP < 5.5
1403
-                // where "finally" should be T_FINALLY instead of T_STRING.
1404
-                if ($newToken['code'] === T_STRING
1405
-                    && strtolower($newToken['content']) === 'finally'
1406
-                ) {
1407
-                    $newToken['code'] = T_FINALLY;
1408
-                    $newToken['type'] = 'T_FINALLY';
1409
-                }
1410
-
1411
-                // This is a special case for the PHP 5.5 classname::class syntax
1412
-                // where "class" should be T_STRING instead of T_CLASS.
1413
-                if (($newToken['code'] === T_CLASS
1414
-                    || $newToken['code'] === T_FUNCTION)
1415
-                    && $finalTokens[$lastNotEmptyToken]['code'] === T_DOUBLE_COLON
1416
-                ) {
1417
-                    $newToken['code'] = T_STRING;
1418
-                    $newToken['type'] = 'T_STRING';
1419
-                }
1420
-
1421
-                // This is a special case for PHP 5.6 use function and use const
1422
-                // where "function" and "const" should be T_STRING instead of T_FUNCTION
1423
-                // and T_CONST.
1424
-                if (($newToken['code'] === T_FUNCTION
1425
-                    || $newToken['code'] === T_CONST)
1426
-                    && ($finalTokens[$lastNotEmptyToken]['code'] === T_USE || $insideUseGroup === true)
1427
-                ) {
1428
-                    $newToken['code'] = T_STRING;
1429
-                    $newToken['type'] = 'T_STRING';
1430
-                }
1431
-
1432
-                // This is a special case for use groups in PHP 7+ where leaving
1433
-                // the curly braces as their normal tokens would confuse
1434
-                // the scope map and sniffs.
1435
-                if ($newToken['code'] === T_OPEN_CURLY_BRACKET
1436
-                    && $finalTokens[$lastNotEmptyToken]['code'] === T_NS_SEPARATOR
1437
-                ) {
1438
-                    $newToken['code'] = T_OPEN_USE_GROUP;
1439
-                    $newToken['type'] = 'T_OPEN_USE_GROUP';
1440
-                    $insideUseGroup   = true;
1441
-                }
1442
-
1443
-                if ($insideUseGroup === true && $newToken['code'] === T_CLOSE_CURLY_BRACKET) {
1444
-                    $newToken['code'] = T_CLOSE_USE_GROUP;
1445
-                    $newToken['type'] = 'T_CLOSE_USE_GROUP';
1446
-                    $insideUseGroup   = false;
1447
-                }
1448
-
1449
-                $finalTokens[$newStackPtr] = $newToken;
1450
-                $newStackPtr++;
1451
-            }//end if
1452
-        }//end for
1453
-
1454
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1455
-            echo "\t*** END PHP TOKENIZING ***".PHP_EOL;
1456
-        }
1457
-
1458
-        return $finalTokens;
1459
-
1460
-    }//end tokenize()
1461
-
1462
-
1463
-    /**
1464
-     * Performs additional processing after main tokenizing.
1465
-     *
1466
-     * This additional processing checks for CASE statements that are using curly
1467
-     * braces for scope openers and closers. It also turns some T_FUNCTION tokens
1468
-     * into T_CLOSURE when they are not standard function definitions. It also
1469
-     * detects short array syntax and converts those square brackets into new tokens.
1470
-     * It also corrects some usage of the static and class keywords. It also
1471
-     * assigns tokens to function return types.
1472
-     *
1473
-     * @return void
1474
-     */
1475
-    protected function processAdditional()
1476
-    {
1477
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1478
-            echo "\t*** START ADDITIONAL PHP PROCESSING ***".PHP_EOL;
1479
-        }
1480
-
1481
-        $numTokens = count($this->tokens);
1482
-        for ($i = ($numTokens - 1); $i >= 0; $i--) {
1483
-            // Check for any unset scope conditions due to alternate IF/ENDIF syntax.
1484
-            if (isset($this->tokens[$i]['scope_opener']) === true
1485
-                && isset($this->tokens[$i]['scope_condition']) === false
1486
-            ) {
1487
-                $this->tokens[$i]['scope_condition'] = $this->tokens[$this->tokens[$i]['scope_opener']]['scope_condition'];
1488
-            }
1489
-
1490
-            if ($this->tokens[$i]['code'] === T_FUNCTION) {
1491
-                /*
1247
+			if ($tokenIsArray === true && strpos($token[1], $this->eolChar) !== false) {
1248
+				$tokenLines = explode($this->eolChar, $token[1]);
1249
+				$numLines   = count($tokenLines);
1250
+				$newToken   = [
1251
+					'type'    => Util\Tokens::tokenName($token[0]),
1252
+					'code'    => $token[0],
1253
+					'content' => '',
1254
+				];
1255
+
1256
+				for ($i = 0; $i < $numLines; $i++) {
1257
+					$newToken['content'] = $tokenLines[$i];
1258
+					if ($i === ($numLines - 1)) {
1259
+						if ($tokenLines[$i] === '') {
1260
+							break;
1261
+						}
1262
+					} else {
1263
+						$newToken['content'] .= $this->eolChar;
1264
+					}
1265
+
1266
+					$finalTokens[$newStackPtr] = $newToken;
1267
+					$newStackPtr++;
1268
+				}
1269
+			} else {
1270
+				if ($tokenIsArray === true && $token[0] === T_STRING) {
1271
+					// Some T_STRING tokens should remain that way
1272
+					// due to their context.
1273
+					$context = [
1274
+						T_OBJECT_OPERATOR      => true,
1275
+						T_FUNCTION             => true,
1276
+						T_CLASS                => true,
1277
+						T_EXTENDS              => true,
1278
+						T_IMPLEMENTS           => true,
1279
+						T_NEW                  => true,
1280
+						T_CONST                => true,
1281
+						T_NS_SEPARATOR         => true,
1282
+						T_USE                  => true,
1283
+						T_NAMESPACE            => true,
1284
+						T_PAAMAYIM_NEKUDOTAYIM => true,
1285
+					];
1286
+
1287
+					if (isset($context[$finalTokens[$lastNotEmptyToken]['code']]) === true) {
1288
+						// Special case for syntax like: return new self
1289
+						// where self should not be a string.
1290
+						if ($finalTokens[$lastNotEmptyToken]['code'] === T_NEW
1291
+							&& strtolower($token[1]) === 'self'
1292
+						) {
1293
+							$finalTokens[$newStackPtr] = [
1294
+								'content' => $token[1],
1295
+								'code'    => T_SELF,
1296
+								'type'    => 'T_SELF',
1297
+							];
1298
+						} else {
1299
+							$finalTokens[$newStackPtr] = [
1300
+								'content' => $token[1],
1301
+								'code'    => T_STRING,
1302
+								'type'    => 'T_STRING',
1303
+							];
1304
+						}
1305
+
1306
+						$newStackPtr++;
1307
+						continue;
1308
+					}//end if
1309
+				}//end if
1310
+
1311
+				$newToken = null;
1312
+				if ($tokenIsArray === false) {
1313
+					if (isset(self::$resolveTokenCache[$token[0]]) === true) {
1314
+						$newToken = self::$resolveTokenCache[$token[0]];
1315
+					}
1316
+				} else {
1317
+					$cacheKey = null;
1318
+					if ($token[0] === T_STRING) {
1319
+						$cacheKey = strtolower($token[1]);
1320
+					} else if ($token[0] !== T_CURLY_OPEN) {
1321
+						$cacheKey = $token[0];
1322
+					}
1323
+
1324
+					if ($cacheKey !== null && isset(self::$resolveTokenCache[$cacheKey]) === true) {
1325
+						$newToken            = self::$resolveTokenCache[$cacheKey];
1326
+						$newToken['content'] = $token[1];
1327
+					}
1328
+				}
1329
+
1330
+				if ($newToken === null) {
1331
+					$newToken = self::standardiseToken($token);
1332
+				}
1333
+
1334
+				// Convert colons that are actually the ELSE component of an
1335
+				// inline IF statement.
1336
+				if (empty($insideInlineIf) === false && $newToken['code'] === T_COLON) {
1337
+					// Make sure this isn't the return type separator of a closure.
1338
+					$isReturnType = false;
1339
+					for ($i = ($stackPtr - 1); $i > 0; $i--) {
1340
+						if (is_array($tokens[$i]) === false
1341
+							|| ($tokens[$i][0] !== T_DOC_COMMENT
1342
+							&& $tokens[$i][0] !== T_COMMENT
1343
+							&& $tokens[$i][0] !== T_WHITESPACE)
1344
+						) {
1345
+							break;
1346
+						}
1347
+					}
1348
+
1349
+					if ($tokens[$i] === ')') {
1350
+						$parenCount = 1;
1351
+						for ($i--; $i > 0; $i--) {
1352
+							if ($tokens[$i] === '(') {
1353
+								$parenCount--;
1354
+								if ($parenCount === 0) {
1355
+									break;
1356
+								}
1357
+							} else if ($tokens[$i] === ')') {
1358
+								$parenCount++;
1359
+							}
1360
+						}
1361
+
1362
+						// We've found the open parenthesis, so if the previous
1363
+						// non-empty token is FUNCTION or USE, this is a closure.
1364
+						for ($i--; $i > 0; $i--) {
1365
+							if (is_array($tokens[$i]) === false
1366
+								|| ($tokens[$i][0] !== T_DOC_COMMENT
1367
+								&& $tokens[$i][0] !== T_COMMENT
1368
+								&& $tokens[$i][0] !== T_WHITESPACE)
1369
+							) {
1370
+								break;
1371
+							}
1372
+						}
1373
+
1374
+						if ($tokens[$i][0] === T_FUNCTION || $tokens[$i][0] === T_USE) {
1375
+							$isReturnType = true;
1376
+						}
1377
+					}//end if
1378
+
1379
+					if ($isReturnType === false) {
1380
+						array_pop($insideInlineIf);
1381
+						$newToken['code'] = T_INLINE_ELSE;
1382
+						$newToken['type'] = 'T_INLINE_ELSE';
1383
+					}
1384
+				}//end if
1385
+
1386
+				// This is a special condition for T_ARRAY tokens used for
1387
+				// type hinting function arguments as being arrays. We want to keep
1388
+				// the parenthesis map clean, so let's tag these tokens as
1389
+				// T_STRING.
1390
+				if ($newToken['code'] === T_ARRAY) {
1391
+					for ($i = $stackPtr; $i < $numTokens; $i++) {
1392
+						if ($tokens[$i] === '(') {
1393
+							break;
1394
+						} else if ($tokens[$i][0] === T_VARIABLE) {
1395
+							$newToken['code'] = T_STRING;
1396
+							$newToken['type'] = 'T_STRING';
1397
+							break;
1398
+						}
1399
+					}
1400
+				}
1401
+
1402
+				// This is a special case when checking PHP 5.5+ code in PHP < 5.5
1403
+				// where "finally" should be T_FINALLY instead of T_STRING.
1404
+				if ($newToken['code'] === T_STRING
1405
+					&& strtolower($newToken['content']) === 'finally'
1406
+				) {
1407
+					$newToken['code'] = T_FINALLY;
1408
+					$newToken['type'] = 'T_FINALLY';
1409
+				}
1410
+
1411
+				// This is a special case for the PHP 5.5 classname::class syntax
1412
+				// where "class" should be T_STRING instead of T_CLASS.
1413
+				if (($newToken['code'] === T_CLASS
1414
+					|| $newToken['code'] === T_FUNCTION)
1415
+					&& $finalTokens[$lastNotEmptyToken]['code'] === T_DOUBLE_COLON
1416
+				) {
1417
+					$newToken['code'] = T_STRING;
1418
+					$newToken['type'] = 'T_STRING';
1419
+				}
1420
+
1421
+				// This is a special case for PHP 5.6 use function and use const
1422
+				// where "function" and "const" should be T_STRING instead of T_FUNCTION
1423
+				// and T_CONST.
1424
+				if (($newToken['code'] === T_FUNCTION
1425
+					|| $newToken['code'] === T_CONST)
1426
+					&& ($finalTokens[$lastNotEmptyToken]['code'] === T_USE || $insideUseGroup === true)
1427
+				) {
1428
+					$newToken['code'] = T_STRING;
1429
+					$newToken['type'] = 'T_STRING';
1430
+				}
1431
+
1432
+				// This is a special case for use groups in PHP 7+ where leaving
1433
+				// the curly braces as their normal tokens would confuse
1434
+				// the scope map and sniffs.
1435
+				if ($newToken['code'] === T_OPEN_CURLY_BRACKET
1436
+					&& $finalTokens[$lastNotEmptyToken]['code'] === T_NS_SEPARATOR
1437
+				) {
1438
+					$newToken['code'] = T_OPEN_USE_GROUP;
1439
+					$newToken['type'] = 'T_OPEN_USE_GROUP';
1440
+					$insideUseGroup   = true;
1441
+				}
1442
+
1443
+				if ($insideUseGroup === true && $newToken['code'] === T_CLOSE_CURLY_BRACKET) {
1444
+					$newToken['code'] = T_CLOSE_USE_GROUP;
1445
+					$newToken['type'] = 'T_CLOSE_USE_GROUP';
1446
+					$insideUseGroup   = false;
1447
+				}
1448
+
1449
+				$finalTokens[$newStackPtr] = $newToken;
1450
+				$newStackPtr++;
1451
+			}//end if
1452
+		}//end for
1453
+
1454
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
1455
+			echo "\t*** END PHP TOKENIZING ***".PHP_EOL;
1456
+		}
1457
+
1458
+		return $finalTokens;
1459
+
1460
+	}//end tokenize()
1461
+
1462
+
1463
+	/**
1464
+	 * Performs additional processing after main tokenizing.
1465
+	 *
1466
+	 * This additional processing checks for CASE statements that are using curly
1467
+	 * braces for scope openers and closers. It also turns some T_FUNCTION tokens
1468
+	 * into T_CLOSURE when they are not standard function definitions. It also
1469
+	 * detects short array syntax and converts those square brackets into new tokens.
1470
+	 * It also corrects some usage of the static and class keywords. It also
1471
+	 * assigns tokens to function return types.
1472
+	 *
1473
+	 * @return void
1474
+	 */
1475
+	protected function processAdditional()
1476
+	{
1477
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
1478
+			echo "\t*** START ADDITIONAL PHP PROCESSING ***".PHP_EOL;
1479
+		}
1480
+
1481
+		$numTokens = count($this->tokens);
1482
+		for ($i = ($numTokens - 1); $i >= 0; $i--) {
1483
+			// Check for any unset scope conditions due to alternate IF/ENDIF syntax.
1484
+			if (isset($this->tokens[$i]['scope_opener']) === true
1485
+				&& isset($this->tokens[$i]['scope_condition']) === false
1486
+			) {
1487
+				$this->tokens[$i]['scope_condition'] = $this->tokens[$this->tokens[$i]['scope_opener']]['scope_condition'];
1488
+			}
1489
+
1490
+			if ($this->tokens[$i]['code'] === T_FUNCTION) {
1491
+				/*
1492 1492
                     Detect functions that are actually closures and
1493 1493
                     assign them a different token.
1494 1494
                 */
1495 1495
 
1496
-                if (isset($this->tokens[$i]['scope_opener']) === true) {
1497
-                    for ($x = ($i + 1); $x < $numTokens; $x++) {
1498
-                        if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false
1499
-                            && $this->tokens[$x]['code'] !== T_BITWISE_AND
1500
-                        ) {
1501
-                            break;
1502
-                        }
1503
-                    }
1504
-
1505
-                    if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS) {
1506
-                        $this->tokens[$i]['code'] = T_CLOSURE;
1507
-                        $this->tokens[$i]['type'] = 'T_CLOSURE';
1508
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1509
-                            $line = $this->tokens[$i]['line'];
1510
-                            echo "\t* token $i on line $line changed from T_FUNCTION to T_CLOSURE".PHP_EOL;
1511
-                        }
1512
-
1513
-                        for ($x = ($this->tokens[$i]['scope_opener'] + 1); $x < $this->tokens[$i]['scope_closer']; $x++) {
1514
-                            if (isset($this->tokens[$x]['conditions'][$i]) === false) {
1515
-                                continue;
1516
-                            }
1517
-
1518
-                            $this->tokens[$x]['conditions'][$i] = T_CLOSURE;
1519
-                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1520
-                                $type = $this->tokens[$x]['type'];
1521
-                                echo "\t\t* cleaned $x ($type) *".PHP_EOL;
1522
-                            }
1523
-                        }
1524
-                    }
1525
-                }//end if
1526
-
1527
-                continue;
1528
-            } else if ($this->tokens[$i]['code'] === T_CLASS && isset($this->tokens[$i]['scope_opener']) === true) {
1529
-                /*
1496
+				if (isset($this->tokens[$i]['scope_opener']) === true) {
1497
+					for ($x = ($i + 1); $x < $numTokens; $x++) {
1498
+						if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false
1499
+							&& $this->tokens[$x]['code'] !== T_BITWISE_AND
1500
+						) {
1501
+							break;
1502
+						}
1503
+					}
1504
+
1505
+					if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS) {
1506
+						$this->tokens[$i]['code'] = T_CLOSURE;
1507
+						$this->tokens[$i]['type'] = 'T_CLOSURE';
1508
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1509
+							$line = $this->tokens[$i]['line'];
1510
+							echo "\t* token $i on line $line changed from T_FUNCTION to T_CLOSURE".PHP_EOL;
1511
+						}
1512
+
1513
+						for ($x = ($this->tokens[$i]['scope_opener'] + 1); $x < $this->tokens[$i]['scope_closer']; $x++) {
1514
+							if (isset($this->tokens[$x]['conditions'][$i]) === false) {
1515
+								continue;
1516
+							}
1517
+
1518
+							$this->tokens[$x]['conditions'][$i] = T_CLOSURE;
1519
+							if (PHP_CODESNIFFER_VERBOSITY > 1) {
1520
+								$type = $this->tokens[$x]['type'];
1521
+								echo "\t\t* cleaned $x ($type) *".PHP_EOL;
1522
+							}
1523
+						}
1524
+					}
1525
+				}//end if
1526
+
1527
+				continue;
1528
+			} else if ($this->tokens[$i]['code'] === T_CLASS && isset($this->tokens[$i]['scope_opener']) === true) {
1529
+				/*
1530 1530
                     Detect anonymous classes and assign them a different token.
1531 1531
                 */
1532 1532
 
1533
-                for ($x = ($i + 1); $x < $numTokens; $x++) {
1534
-                    if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1535
-                        break;
1536
-                    }
1537
-                }
1538
-
1539
-                if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS
1540
-                    || $this->tokens[$x]['code'] === T_OPEN_CURLY_BRACKET
1541
-                    || $this->tokens[$x]['code'] === T_EXTENDS
1542
-                    || $this->tokens[$x]['code'] === T_IMPLEMENTS
1543
-                ) {
1544
-                    $this->tokens[$i]['code'] = T_ANON_CLASS;
1545
-                    $this->tokens[$i]['type'] = 'T_ANON_CLASS';
1546
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1547
-                        $line = $this->tokens[$i]['line'];
1548
-                        echo "\t* token $i on line $line changed from T_CLASS to T_ANON_CLASS".PHP_EOL;
1549
-                    }
1550
-
1551
-                    for ($x = ($this->tokens[$i]['scope_opener'] + 1); $x < $this->tokens[$i]['scope_closer']; $x++) {
1552
-                        if (isset($this->tokens[$x]['conditions'][$i]) === false) {
1553
-                            continue;
1554
-                        }
1555
-
1556
-                        $this->tokens[$x]['conditions'][$i] = T_ANON_CLASS;
1557
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1558
-                            $type = $this->tokens[$x]['type'];
1559
-                            echo "\t\t* cleaned $x ($type) *".PHP_EOL;
1560
-                        }
1561
-                    }
1562
-                }
1563
-
1564
-                continue;
1565
-            } else if ($this->tokens[$i]['code'] === T_OPEN_SQUARE_BRACKET) {
1566
-                if (isset($this->tokens[$i]['bracket_closer']) === false) {
1567
-                    continue;
1568
-                }
1569
-
1570
-                // Unless there is a variable or a bracket before this token,
1571
-                // it is the start of an array being defined using the short syntax.
1572
-                $isShortArray = false;
1573
-                $allowed      = [
1574
-                    T_CLOSE_SQUARE_BRACKET     => T_CLOSE_SQUARE_BRACKET,
1575
-                    T_CLOSE_CURLY_BRACKET      => T_CLOSE_CURLY_BRACKET,
1576
-                    T_CLOSE_PARENTHESIS        => T_CLOSE_PARENTHESIS,
1577
-                    T_VARIABLE                 => T_VARIABLE,
1578
-                    T_OBJECT_OPERATOR          => T_OBJECT_OPERATOR,
1579
-                    T_STRING                   => T_STRING,
1580
-                    T_CONSTANT_ENCAPSED_STRING => T_CONSTANT_ENCAPSED_STRING,
1581
-                ];
1582
-
1583
-                for ($x = ($i - 1); $x >= 0; $x--) {
1584
-                    // If we hit a scope opener, the statement has ended
1585
-                    // without finding anything, so it's probably an array
1586
-                    // using PHP 7.1 short list syntax.
1587
-                    if (isset($this->tokens[$x]['scope_opener']) === true) {
1588
-                        $isShortArray = true;
1589
-                        break;
1590
-                    }
1591
-
1592
-                    if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1593
-                        if (isset($allowed[$this->tokens[$x]['code']]) === false) {
1594
-                            $isShortArray = true;
1595
-                        }
1596
-
1597
-                        break;
1598
-                    }
1599
-                }
1600
-
1601
-                if ($isShortArray === true) {
1602
-                    $this->tokens[$i]['code'] = T_OPEN_SHORT_ARRAY;
1603
-                    $this->tokens[$i]['type'] = 'T_OPEN_SHORT_ARRAY';
1604
-
1605
-                    $closer = $this->tokens[$i]['bracket_closer'];
1606
-                    $this->tokens[$closer]['code'] = T_CLOSE_SHORT_ARRAY;
1607
-                    $this->tokens[$closer]['type'] = 'T_CLOSE_SHORT_ARRAY';
1608
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1609
-                        $line = $this->tokens[$i]['line'];
1610
-                        echo "\t* token $i on line $line changed from T_OPEN_SQUARE_BRACKET to T_OPEN_SHORT_ARRAY".PHP_EOL;
1611
-                        $line = $this->tokens[$closer]['line'];
1612
-                        echo "\t* token $closer on line $line changed from T_CLOSE_SQUARE_BRACKET to T_CLOSE_SHORT_ARRAY".PHP_EOL;
1613
-                    }
1614
-                }
1615
-
1616
-                continue;
1617
-            } else if ($this->tokens[$i]['code'] === T_STATIC) {
1618
-                for ($x = ($i - 1); $x > 0; $x--) {
1619
-                    if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1620
-                        break;
1621
-                    }
1622
-                }
1623
-
1624
-                if ($this->tokens[$x]['code'] === T_INSTANCEOF) {
1625
-                    $this->tokens[$i]['code'] = T_STRING;
1626
-                    $this->tokens[$i]['type'] = 'T_STRING';
1627
-
1628
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1629
-                        $line = $this->tokens[$i]['line'];
1630
-                        echo "\t* token $i on line $line changed from T_STATIC to T_STRING".PHP_EOL;
1631
-                    }
1632
-                }
1633
-
1634
-                continue;
1635
-            } else if ($this->tokens[$i]['code'] === T_TRUE
1636
-                || $this->tokens[$i]['code'] === T_FALSE
1637
-                || $this->tokens[$i]['code'] === T_NULL
1638
-            ) {
1639
-                for ($x = ($i + 1); $i < $numTokens; $x++) {
1640
-                    if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1641
-                        // Non-whitespace content.
1642
-                        break;
1643
-                    }
1644
-                }
1645
-
1646
-                $context = [
1647
-                    T_OBJECT_OPERATOR      => true,
1648
-                    T_NS_SEPARATOR         => true,
1649
-                    T_PAAMAYIM_NEKUDOTAYIM => true,
1650
-                ];
1651
-                if (isset($context[$this->tokens[$x]['code']]) === true) {
1652
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1653
-                        $line = $this->tokens[$i]['line'];
1654
-                        $type = $this->tokens[$i]['type'];
1655
-                        echo "\t* token $i on line $line changed from $type to T_STRING".PHP_EOL;
1656
-                    }
1657
-
1658
-                    $this->tokens[$i]['code'] = T_STRING;
1659
-                    $this->tokens[$i]['type'] = 'T_STRING';
1660
-                }
1661
-            } else if ($this->tokens[$i]['code'] === T_CONST) {
1662
-                // Context sensitive keywords support.
1663
-                for ($x = ($i + 1); $i < $numTokens; $x++) {
1664
-                    if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1665
-                        // Non-whitespace content.
1666
-                        break;
1667
-                    }
1668
-                }
1669
-
1670
-                if ($this->tokens[$x]['code'] !== T_STRING) {
1671
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1672
-                        $line = $this->tokens[$x]['line'];
1673
-                        $type = $this->tokens[$x]['type'];
1674
-                        echo "\t* token $x on line $line changed from $type to T_STRING".PHP_EOL;
1675
-                    }
1676
-
1677
-                    $this->tokens[$x]['code'] = T_STRING;
1678
-                    $this->tokens[$x]['type'] = 'T_STRING';
1679
-                }
1680
-            }//end if
1681
-
1682
-            if (($this->tokens[$i]['code'] !== T_CASE
1683
-                && $this->tokens[$i]['code'] !== T_DEFAULT)
1684
-                || isset($this->tokens[$i]['scope_opener']) === false
1685
-            ) {
1686
-                // Only interested in CASE and DEFAULT statements from here on in.
1687
-                continue;
1688
-            }
1689
-
1690
-            $scopeOpener = $this->tokens[$i]['scope_opener'];
1691
-            $scopeCloser = $this->tokens[$i]['scope_closer'];
1692
-
1693
-            // If the first char after the opener is a curly brace
1694
-            // and that brace has been ignored, it is actually
1695
-            // opening this case statement and the opener and closer are
1696
-            // probably set incorrectly.
1697
-            for ($x = ($scopeOpener + 1); $x < $numTokens; $x++) {
1698
-                if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1699
-                    // Non-whitespace content.
1700
-                    break;
1701
-                }
1702
-            }
1703
-
1704
-            if ($this->tokens[$x]['code'] === T_CASE || $this->tokens[$x]['code'] === T_DEFAULT) {
1705
-                // Special case for multiple CASE statements that share the same
1706
-                // closer. Because we are going backwards through the file, this next
1707
-                // CASE statement is already fixed, so just use its closer and don't
1708
-                // worry about fixing anything.
1709
-                $newCloser = $this->tokens[$x]['scope_closer'];
1710
-                $this->tokens[$i]['scope_closer'] = $newCloser;
1711
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1712
-                    $oldType = $this->tokens[$scopeCloser]['type'];
1713
-                    $newType = $this->tokens[$newCloser]['type'];
1714
-                    $line    = $this->tokens[$i]['line'];
1715
-                    echo "\t* token $i (T_CASE) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL;
1716
-                }
1717
-
1718
-                continue;
1719
-            }
1720
-
1721
-            if ($this->tokens[$x]['code'] !== T_OPEN_CURLY_BRACKET
1722
-                || isset($this->tokens[$x]['scope_condition']) === true
1723
-            ) {
1724
-                // Not a CASE/DEFAULT with a curly brace opener.
1725
-                continue;
1726
-            }
1727
-
1728
-            // The closer for this CASE/DEFAULT should be the closing curly brace and
1729
-            // not whatever it already is. The opener needs to be the opening curly
1730
-            // brace so everything matches up.
1731
-            $newCloser = $this->tokens[$x]['bracket_closer'];
1732
-            foreach ([$i, $x, $newCloser] as $index) {
1733
-                $this->tokens[$index]['scope_condition'] = $i;
1734
-                $this->tokens[$index]['scope_opener']    = $x;
1735
-                $this->tokens[$index]['scope_closer']    = $newCloser;
1736
-            }
1737
-
1738
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1739
-                $line      = $this->tokens[$i]['line'];
1740
-                $tokenType = $this->tokens[$i]['type'];
1741
-
1742
-                $oldType = $this->tokens[$scopeOpener]['type'];
1743
-                $newType = $this->tokens[$x]['type'];
1744
-                echo "\t* token $i ($tokenType) on line $line opener changed from $scopeOpener ($oldType) to $x ($newType)".PHP_EOL;
1745
-
1746
-                $oldType = $this->tokens[$scopeCloser]['type'];
1747
-                $newType = $this->tokens[$newCloser]['type'];
1748
-                echo "\t* token $i ($tokenType) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL;
1749
-            }
1750
-
1751
-            if ($this->tokens[$scopeOpener]['scope_condition'] === $i) {
1752
-                unset($this->tokens[$scopeOpener]['scope_condition']);
1753
-                unset($this->tokens[$scopeOpener]['scope_opener']);
1754
-                unset($this->tokens[$scopeOpener]['scope_closer']);
1755
-            }
1756
-
1757
-            if ($this->tokens[$scopeCloser]['scope_condition'] === $i) {
1758
-                unset($this->tokens[$scopeCloser]['scope_condition']);
1759
-                unset($this->tokens[$scopeCloser]['scope_opener']);
1760
-                unset($this->tokens[$scopeCloser]['scope_closer']);
1761
-            } else {
1762
-                // We were using a shared closer. All tokens that were
1763
-                // sharing this closer with us, except for the scope condition
1764
-                // and it's opener, need to now point to the new closer.
1765
-                $condition = $this->tokens[$scopeCloser]['scope_condition'];
1766
-                $start     = ($this->tokens[$condition]['scope_opener'] + 1);
1767
-                for ($y = $start; $y < $scopeCloser; $y++) {
1768
-                    if (isset($this->tokens[$y]['scope_closer']) === true
1769
-                        && $this->tokens[$y]['scope_closer'] === $scopeCloser
1770
-                    ) {
1771
-                        $this->tokens[$y]['scope_closer'] = $newCloser;
1772
-
1773
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1774
-                            $line      = $this->tokens[$y]['line'];
1775
-                            $tokenType = $this->tokens[$y]['type'];
1776
-                            $oldType   = $this->tokens[$scopeCloser]['type'];
1777
-                            $newType   = $this->tokens[$newCloser]['type'];
1778
-                            echo "\t\t* token $y ($tokenType) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL;
1779
-                        }
1780
-                    }
1781
-                }
1782
-            }//end if
1783
-
1784
-            unset($this->tokens[$x]['bracket_opener']);
1785
-            unset($this->tokens[$x]['bracket_closer']);
1786
-            unset($this->tokens[$newCloser]['bracket_opener']);
1787
-            unset($this->tokens[$newCloser]['bracket_closer']);
1788
-            $this->tokens[$scopeCloser]['conditions'][] = $i;
1789
-
1790
-            // Now fix up all the tokens that think they are
1791
-            // inside the CASE/DEFAULT statement when they are really outside.
1792
-            for ($x = $newCloser; $x < $scopeCloser; $x++) {
1793
-                foreach ($this->tokens[$x]['conditions'] as $num => $oldCond) {
1794
-                    if ($oldCond === $this->tokens[$i]['code']) {
1795
-                        $oldConditions = $this->tokens[$x]['conditions'];
1796
-                        unset($this->tokens[$x]['conditions'][$num]);
1797
-
1798
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1799
-                            $type     = $this->tokens[$x]['type'];
1800
-                            $oldConds = '';
1801
-                            foreach ($oldConditions as $condition) {
1802
-                                $oldConds .= Util\Tokens::tokenName($condition).',';
1803
-                            }
1804
-
1805
-                            $oldConds = rtrim($oldConds, ',');
1806
-
1807
-                            $newConds = '';
1808
-                            foreach ($this->tokens[$x]['conditions'] as $condition) {
1809
-                                $newConds .= Util\Tokens::tokenName($condition).',';
1810
-                            }
1811
-
1812
-                            $newConds = rtrim($newConds, ',');
1813
-
1814
-                            echo "\t\t* cleaned $x ($type) *".PHP_EOL;
1815
-                            echo "\t\t\t=> conditions changed from $oldConds to $newConds".PHP_EOL;
1816
-                        }
1817
-
1818
-                        break;
1819
-                    }//end if
1820
-                }//end foreach
1821
-            }//end for
1822
-        }//end for
1823
-
1824
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1825
-            echo "\t*** END ADDITIONAL PHP PROCESSING ***".PHP_EOL;
1826
-        }
1827
-
1828
-    }//end processAdditional()
1829
-
1830
-
1831
-    /**
1832
-     * Takes a token produced from <code>token_get_all()</code> and produces a
1833
-     * more uniform token.
1834
-     *
1835
-     * @param string|array $token The token to convert.
1836
-     *
1837
-     * @return array The new token.
1838
-     */
1839
-    public static function standardiseToken($token)
1840
-    {
1841
-        if (isset($token[1]) === false) {
1842
-            if (isset(self::$resolveTokenCache[$token[0]]) === true) {
1843
-                return self::$resolveTokenCache[$token[0]];
1844
-            }
1845
-        } else {
1846
-            $cacheKey = null;
1847
-            if ($token[0] === T_STRING) {
1848
-                $cacheKey = strtolower($token[1]);
1849
-            } else if ($token[0] !== T_CURLY_OPEN) {
1850
-                $cacheKey = $token[0];
1851
-            }
1852
-
1853
-            if ($cacheKey !== null && isset(self::$resolveTokenCache[$cacheKey]) === true) {
1854
-                $newToken            = self::$resolveTokenCache[$cacheKey];
1855
-                $newToken['content'] = $token[1];
1856
-                return $newToken;
1857
-            }
1858
-        }
1859
-
1860
-        if (isset($token[1]) === false) {
1861
-            return self::resolveSimpleToken($token[0]);
1862
-        }
1863
-
1864
-        if ($token[0] === T_STRING) {
1865
-            switch ($cacheKey) {
1866
-            case 'false':
1867
-                $newToken['type'] = 'T_FALSE';
1868
-                break;
1869
-            case 'true':
1870
-                $newToken['type'] = 'T_TRUE';
1871
-                break;
1872
-            case 'null':
1873
-                $newToken['type'] = 'T_NULL';
1874
-                break;
1875
-            case 'self':
1876
-                $newToken['type'] = 'T_SELF';
1877
-                break;
1878
-            case 'parent':
1879
-                $newToken['type'] = 'T_PARENT';
1880
-                break;
1881
-            default:
1882
-                $newToken['type'] = 'T_STRING';
1883
-                break;
1884
-            }
1885
-
1886
-            $newToken['code'] = constant($newToken['type']);
1887
-
1888
-            self::$resolveTokenCache[$cacheKey] = $newToken;
1889
-        } else if ($token[0] === T_CURLY_OPEN) {
1890
-            $newToken = [
1891
-                'code' => T_OPEN_CURLY_BRACKET,
1892
-                'type' => 'T_OPEN_CURLY_BRACKET',
1893
-            ];
1894
-        } else {
1895
-            $newToken = [
1896
-                'code' => $token[0],
1897
-                'type' => Util\Tokens::tokenName($token[0]),
1898
-            ];
1899
-
1900
-            self::$resolveTokenCache[$token[0]] = $newToken;
1901
-        }//end if
1902
-
1903
-        $newToken['content'] = $token[1];
1904
-        return $newToken;
1905
-
1906
-    }//end standardiseToken()
1907
-
1908
-
1909
-    /**
1910
-     * Converts simple tokens into a format that conforms to complex tokens
1911
-     * produced by token_get_all().
1912
-     *
1913
-     * Simple tokens are tokens that are not in array form when produced from
1914
-     * token_get_all().
1915
-     *
1916
-     * @param string $token The simple token to convert.
1917
-     *
1918
-     * @return array The new token in array format.
1919
-     */
1920
-    public static function resolveSimpleToken($token)
1921
-    {
1922
-        $newToken = [];
1923
-
1924
-        switch ($token) {
1925
-        case '{':
1926
-            $newToken['type'] = 'T_OPEN_CURLY_BRACKET';
1927
-            break;
1928
-        case '}':
1929
-            $newToken['type'] = 'T_CLOSE_CURLY_BRACKET';
1930
-            break;
1931
-        case '[':
1932
-            $newToken['type'] = 'T_OPEN_SQUARE_BRACKET';
1933
-            break;
1934
-        case ']':
1935
-            $newToken['type'] = 'T_CLOSE_SQUARE_BRACKET';
1936
-            break;
1937
-        case '(':
1938
-            $newToken['type'] = 'T_OPEN_PARENTHESIS';
1939
-            break;
1940
-        case ')':
1941
-            $newToken['type'] = 'T_CLOSE_PARENTHESIS';
1942
-            break;
1943
-        case ':':
1944
-            $newToken['type'] = 'T_COLON';
1945
-            break;
1946
-        case '.':
1947
-            $newToken['type'] = 'T_STRING_CONCAT';
1948
-            break;
1949
-        case ';':
1950
-            $newToken['type'] = 'T_SEMICOLON';
1951
-            break;
1952
-        case '=':
1953
-            $newToken['type'] = 'T_EQUAL';
1954
-            break;
1955
-        case '*':
1956
-            $newToken['type'] = 'T_MULTIPLY';
1957
-            break;
1958
-        case '/':
1959
-            $newToken['type'] = 'T_DIVIDE';
1960
-            break;
1961
-        case '+':
1962
-            $newToken['type'] = 'T_PLUS';
1963
-            break;
1964
-        case '-':
1965
-            $newToken['type'] = 'T_MINUS';
1966
-            break;
1967
-        case '%':
1968
-            $newToken['type'] = 'T_MODULUS';
1969
-            break;
1970
-        case '^':
1971
-            $newToken['type'] = 'T_BITWISE_XOR';
1972
-            break;
1973
-        case '&':
1974
-            $newToken['type'] = 'T_BITWISE_AND';
1975
-            break;
1976
-        case '|':
1977
-            $newToken['type'] = 'T_BITWISE_OR';
1978
-            break;
1979
-        case '~':
1980
-            $newToken['type'] = 'T_BITWISE_NOT';
1981
-            break;
1982
-        case '<':
1983
-            $newToken['type'] = 'T_LESS_THAN';
1984
-            break;
1985
-        case '>':
1986
-            $newToken['type'] = 'T_GREATER_THAN';
1987
-            break;
1988
-        case '!':
1989
-            $newToken['type'] = 'T_BOOLEAN_NOT';
1990
-            break;
1991
-        case ',':
1992
-            $newToken['type'] = 'T_COMMA';
1993
-            break;
1994
-        case '@':
1995
-            $newToken['type'] = 'T_ASPERAND';
1996
-            break;
1997
-        case '$':
1998
-            $newToken['type'] = 'T_DOLLAR';
1999
-            break;
2000
-        case '`':
2001
-            $newToken['type'] = 'T_BACKTICK';
2002
-            break;
2003
-        default:
2004
-            $newToken['type'] = 'T_NONE';
2005
-            break;
2006
-        }//end switch
2007
-
2008
-        $newToken['code']    = constant($newToken['type']);
2009
-        $newToken['content'] = $token;
2010
-
2011
-        self::$resolveTokenCache[$token] = $newToken;
2012
-        return $newToken;
2013
-
2014
-    }//end resolveSimpleToken()
1533
+				for ($x = ($i + 1); $x < $numTokens; $x++) {
1534
+					if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1535
+						break;
1536
+					}
1537
+				}
1538
+
1539
+				if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS
1540
+					|| $this->tokens[$x]['code'] === T_OPEN_CURLY_BRACKET
1541
+					|| $this->tokens[$x]['code'] === T_EXTENDS
1542
+					|| $this->tokens[$x]['code'] === T_IMPLEMENTS
1543
+				) {
1544
+					$this->tokens[$i]['code'] = T_ANON_CLASS;
1545
+					$this->tokens[$i]['type'] = 'T_ANON_CLASS';
1546
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1547
+						$line = $this->tokens[$i]['line'];
1548
+						echo "\t* token $i on line $line changed from T_CLASS to T_ANON_CLASS".PHP_EOL;
1549
+					}
1550
+
1551
+					for ($x = ($this->tokens[$i]['scope_opener'] + 1); $x < $this->tokens[$i]['scope_closer']; $x++) {
1552
+						if (isset($this->tokens[$x]['conditions'][$i]) === false) {
1553
+							continue;
1554
+						}
1555
+
1556
+						$this->tokens[$x]['conditions'][$i] = T_ANON_CLASS;
1557
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1558
+							$type = $this->tokens[$x]['type'];
1559
+							echo "\t\t* cleaned $x ($type) *".PHP_EOL;
1560
+						}
1561
+					}
1562
+				}
1563
+
1564
+				continue;
1565
+			} else if ($this->tokens[$i]['code'] === T_OPEN_SQUARE_BRACKET) {
1566
+				if (isset($this->tokens[$i]['bracket_closer']) === false) {
1567
+					continue;
1568
+				}
1569
+
1570
+				// Unless there is a variable or a bracket before this token,
1571
+				// it is the start of an array being defined using the short syntax.
1572
+				$isShortArray = false;
1573
+				$allowed      = [
1574
+					T_CLOSE_SQUARE_BRACKET     => T_CLOSE_SQUARE_BRACKET,
1575
+					T_CLOSE_CURLY_BRACKET      => T_CLOSE_CURLY_BRACKET,
1576
+					T_CLOSE_PARENTHESIS        => T_CLOSE_PARENTHESIS,
1577
+					T_VARIABLE                 => T_VARIABLE,
1578
+					T_OBJECT_OPERATOR          => T_OBJECT_OPERATOR,
1579
+					T_STRING                   => T_STRING,
1580
+					T_CONSTANT_ENCAPSED_STRING => T_CONSTANT_ENCAPSED_STRING,
1581
+				];
1582
+
1583
+				for ($x = ($i - 1); $x >= 0; $x--) {
1584
+					// If we hit a scope opener, the statement has ended
1585
+					// without finding anything, so it's probably an array
1586
+					// using PHP 7.1 short list syntax.
1587
+					if (isset($this->tokens[$x]['scope_opener']) === true) {
1588
+						$isShortArray = true;
1589
+						break;
1590
+					}
1591
+
1592
+					if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1593
+						if (isset($allowed[$this->tokens[$x]['code']]) === false) {
1594
+							$isShortArray = true;
1595
+						}
1596
+
1597
+						break;
1598
+					}
1599
+				}
1600
+
1601
+				if ($isShortArray === true) {
1602
+					$this->tokens[$i]['code'] = T_OPEN_SHORT_ARRAY;
1603
+					$this->tokens[$i]['type'] = 'T_OPEN_SHORT_ARRAY';
1604
+
1605
+					$closer = $this->tokens[$i]['bracket_closer'];
1606
+					$this->tokens[$closer]['code'] = T_CLOSE_SHORT_ARRAY;
1607
+					$this->tokens[$closer]['type'] = 'T_CLOSE_SHORT_ARRAY';
1608
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1609
+						$line = $this->tokens[$i]['line'];
1610
+						echo "\t* token $i on line $line changed from T_OPEN_SQUARE_BRACKET to T_OPEN_SHORT_ARRAY".PHP_EOL;
1611
+						$line = $this->tokens[$closer]['line'];
1612
+						echo "\t* token $closer on line $line changed from T_CLOSE_SQUARE_BRACKET to T_CLOSE_SHORT_ARRAY".PHP_EOL;
1613
+					}
1614
+				}
1615
+
1616
+				continue;
1617
+			} else if ($this->tokens[$i]['code'] === T_STATIC) {
1618
+				for ($x = ($i - 1); $x > 0; $x--) {
1619
+					if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1620
+						break;
1621
+					}
1622
+				}
1623
+
1624
+				if ($this->tokens[$x]['code'] === T_INSTANCEOF) {
1625
+					$this->tokens[$i]['code'] = T_STRING;
1626
+					$this->tokens[$i]['type'] = 'T_STRING';
1627
+
1628
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1629
+						$line = $this->tokens[$i]['line'];
1630
+						echo "\t* token $i on line $line changed from T_STATIC to T_STRING".PHP_EOL;
1631
+					}
1632
+				}
1633
+
1634
+				continue;
1635
+			} else if ($this->tokens[$i]['code'] === T_TRUE
1636
+				|| $this->tokens[$i]['code'] === T_FALSE
1637
+				|| $this->tokens[$i]['code'] === T_NULL
1638
+			) {
1639
+				for ($x = ($i + 1); $i < $numTokens; $x++) {
1640
+					if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1641
+						// Non-whitespace content.
1642
+						break;
1643
+					}
1644
+				}
1645
+
1646
+				$context = [
1647
+					T_OBJECT_OPERATOR      => true,
1648
+					T_NS_SEPARATOR         => true,
1649
+					T_PAAMAYIM_NEKUDOTAYIM => true,
1650
+				];
1651
+				if (isset($context[$this->tokens[$x]['code']]) === true) {
1652
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1653
+						$line = $this->tokens[$i]['line'];
1654
+						$type = $this->tokens[$i]['type'];
1655
+						echo "\t* token $i on line $line changed from $type to T_STRING".PHP_EOL;
1656
+					}
1657
+
1658
+					$this->tokens[$i]['code'] = T_STRING;
1659
+					$this->tokens[$i]['type'] = 'T_STRING';
1660
+				}
1661
+			} else if ($this->tokens[$i]['code'] === T_CONST) {
1662
+				// Context sensitive keywords support.
1663
+				for ($x = ($i + 1); $i < $numTokens; $x++) {
1664
+					if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1665
+						// Non-whitespace content.
1666
+						break;
1667
+					}
1668
+				}
1669
+
1670
+				if ($this->tokens[$x]['code'] !== T_STRING) {
1671
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1672
+						$line = $this->tokens[$x]['line'];
1673
+						$type = $this->tokens[$x]['type'];
1674
+						echo "\t* token $x on line $line changed from $type to T_STRING".PHP_EOL;
1675
+					}
1676
+
1677
+					$this->tokens[$x]['code'] = T_STRING;
1678
+					$this->tokens[$x]['type'] = 'T_STRING';
1679
+				}
1680
+			}//end if
1681
+
1682
+			if (($this->tokens[$i]['code'] !== T_CASE
1683
+				&& $this->tokens[$i]['code'] !== T_DEFAULT)
1684
+				|| isset($this->tokens[$i]['scope_opener']) === false
1685
+			) {
1686
+				// Only interested in CASE and DEFAULT statements from here on in.
1687
+				continue;
1688
+			}
1689
+
1690
+			$scopeOpener = $this->tokens[$i]['scope_opener'];
1691
+			$scopeCloser = $this->tokens[$i]['scope_closer'];
1692
+
1693
+			// If the first char after the opener is a curly brace
1694
+			// and that brace has been ignored, it is actually
1695
+			// opening this case statement and the opener and closer are
1696
+			// probably set incorrectly.
1697
+			for ($x = ($scopeOpener + 1); $x < $numTokens; $x++) {
1698
+				if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1699
+					// Non-whitespace content.
1700
+					break;
1701
+				}
1702
+			}
1703
+
1704
+			if ($this->tokens[$x]['code'] === T_CASE || $this->tokens[$x]['code'] === T_DEFAULT) {
1705
+				// Special case for multiple CASE statements that share the same
1706
+				// closer. Because we are going backwards through the file, this next
1707
+				// CASE statement is already fixed, so just use its closer and don't
1708
+				// worry about fixing anything.
1709
+				$newCloser = $this->tokens[$x]['scope_closer'];
1710
+				$this->tokens[$i]['scope_closer'] = $newCloser;
1711
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
1712
+					$oldType = $this->tokens[$scopeCloser]['type'];
1713
+					$newType = $this->tokens[$newCloser]['type'];
1714
+					$line    = $this->tokens[$i]['line'];
1715
+					echo "\t* token $i (T_CASE) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL;
1716
+				}
1717
+
1718
+				continue;
1719
+			}
1720
+
1721
+			if ($this->tokens[$x]['code'] !== T_OPEN_CURLY_BRACKET
1722
+				|| isset($this->tokens[$x]['scope_condition']) === true
1723
+			) {
1724
+				// Not a CASE/DEFAULT with a curly brace opener.
1725
+				continue;
1726
+			}
1727
+
1728
+			// The closer for this CASE/DEFAULT should be the closing curly brace and
1729
+			// not whatever it already is. The opener needs to be the opening curly
1730
+			// brace so everything matches up.
1731
+			$newCloser = $this->tokens[$x]['bracket_closer'];
1732
+			foreach ([$i, $x, $newCloser] as $index) {
1733
+				$this->tokens[$index]['scope_condition'] = $i;
1734
+				$this->tokens[$index]['scope_opener']    = $x;
1735
+				$this->tokens[$index]['scope_closer']    = $newCloser;
1736
+			}
1737
+
1738
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
1739
+				$line      = $this->tokens[$i]['line'];
1740
+				$tokenType = $this->tokens[$i]['type'];
1741
+
1742
+				$oldType = $this->tokens[$scopeOpener]['type'];
1743
+				$newType = $this->tokens[$x]['type'];
1744
+				echo "\t* token $i ($tokenType) on line $line opener changed from $scopeOpener ($oldType) to $x ($newType)".PHP_EOL;
1745
+
1746
+				$oldType = $this->tokens[$scopeCloser]['type'];
1747
+				$newType = $this->tokens[$newCloser]['type'];
1748
+				echo "\t* token $i ($tokenType) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL;
1749
+			}
1750
+
1751
+			if ($this->tokens[$scopeOpener]['scope_condition'] === $i) {
1752
+				unset($this->tokens[$scopeOpener]['scope_condition']);
1753
+				unset($this->tokens[$scopeOpener]['scope_opener']);
1754
+				unset($this->tokens[$scopeOpener]['scope_closer']);
1755
+			}
1756
+
1757
+			if ($this->tokens[$scopeCloser]['scope_condition'] === $i) {
1758
+				unset($this->tokens[$scopeCloser]['scope_condition']);
1759
+				unset($this->tokens[$scopeCloser]['scope_opener']);
1760
+				unset($this->tokens[$scopeCloser]['scope_closer']);
1761
+			} else {
1762
+				// We were using a shared closer. All tokens that were
1763
+				// sharing this closer with us, except for the scope condition
1764
+				// and it's opener, need to now point to the new closer.
1765
+				$condition = $this->tokens[$scopeCloser]['scope_condition'];
1766
+				$start     = ($this->tokens[$condition]['scope_opener'] + 1);
1767
+				for ($y = $start; $y < $scopeCloser; $y++) {
1768
+					if (isset($this->tokens[$y]['scope_closer']) === true
1769
+						&& $this->tokens[$y]['scope_closer'] === $scopeCloser
1770
+					) {
1771
+						$this->tokens[$y]['scope_closer'] = $newCloser;
1772
+
1773
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1774
+							$line      = $this->tokens[$y]['line'];
1775
+							$tokenType = $this->tokens[$y]['type'];
1776
+							$oldType   = $this->tokens[$scopeCloser]['type'];
1777
+							$newType   = $this->tokens[$newCloser]['type'];
1778
+							echo "\t\t* token $y ($tokenType) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL;
1779
+						}
1780
+					}
1781
+				}
1782
+			}//end if
1783
+
1784
+			unset($this->tokens[$x]['bracket_opener']);
1785
+			unset($this->tokens[$x]['bracket_closer']);
1786
+			unset($this->tokens[$newCloser]['bracket_opener']);
1787
+			unset($this->tokens[$newCloser]['bracket_closer']);
1788
+			$this->tokens[$scopeCloser]['conditions'][] = $i;
1789
+
1790
+			// Now fix up all the tokens that think they are
1791
+			// inside the CASE/DEFAULT statement when they are really outside.
1792
+			for ($x = $newCloser; $x < $scopeCloser; $x++) {
1793
+				foreach ($this->tokens[$x]['conditions'] as $num => $oldCond) {
1794
+					if ($oldCond === $this->tokens[$i]['code']) {
1795
+						$oldConditions = $this->tokens[$x]['conditions'];
1796
+						unset($this->tokens[$x]['conditions'][$num]);
1797
+
1798
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1799
+							$type     = $this->tokens[$x]['type'];
1800
+							$oldConds = '';
1801
+							foreach ($oldConditions as $condition) {
1802
+								$oldConds .= Util\Tokens::tokenName($condition).',';
1803
+							}
1804
+
1805
+							$oldConds = rtrim($oldConds, ',');
1806
+
1807
+							$newConds = '';
1808
+							foreach ($this->tokens[$x]['conditions'] as $condition) {
1809
+								$newConds .= Util\Tokens::tokenName($condition).',';
1810
+							}
1811
+
1812
+							$newConds = rtrim($newConds, ',');
1813
+
1814
+							echo "\t\t* cleaned $x ($type) *".PHP_EOL;
1815
+							echo "\t\t\t=> conditions changed from $oldConds to $newConds".PHP_EOL;
1816
+						}
1817
+
1818
+						break;
1819
+					}//end if
1820
+				}//end foreach
1821
+			}//end for
1822
+		}//end for
1823
+
1824
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
1825
+			echo "\t*** END ADDITIONAL PHP PROCESSING ***".PHP_EOL;
1826
+		}
1827
+
1828
+	}//end processAdditional()
1829
+
1830
+
1831
+	/**
1832
+	 * Takes a token produced from <code>token_get_all()</code> and produces a
1833
+	 * more uniform token.
1834
+	 *
1835
+	 * @param string|array $token The token to convert.
1836
+	 *
1837
+	 * @return array The new token.
1838
+	 */
1839
+	public static function standardiseToken($token)
1840
+	{
1841
+		if (isset($token[1]) === false) {
1842
+			if (isset(self::$resolveTokenCache[$token[0]]) === true) {
1843
+				return self::$resolveTokenCache[$token[0]];
1844
+			}
1845
+		} else {
1846
+			$cacheKey = null;
1847
+			if ($token[0] === T_STRING) {
1848
+				$cacheKey = strtolower($token[1]);
1849
+			} else if ($token[0] !== T_CURLY_OPEN) {
1850
+				$cacheKey = $token[0];
1851
+			}
1852
+
1853
+			if ($cacheKey !== null && isset(self::$resolveTokenCache[$cacheKey]) === true) {
1854
+				$newToken            = self::$resolveTokenCache[$cacheKey];
1855
+				$newToken['content'] = $token[1];
1856
+				return $newToken;
1857
+			}
1858
+		}
1859
+
1860
+		if (isset($token[1]) === false) {
1861
+			return self::resolveSimpleToken($token[0]);
1862
+		}
1863
+
1864
+		if ($token[0] === T_STRING) {
1865
+			switch ($cacheKey) {
1866
+			case 'false':
1867
+				$newToken['type'] = 'T_FALSE';
1868
+				break;
1869
+			case 'true':
1870
+				$newToken['type'] = 'T_TRUE';
1871
+				break;
1872
+			case 'null':
1873
+				$newToken['type'] = 'T_NULL';
1874
+				break;
1875
+			case 'self':
1876
+				$newToken['type'] = 'T_SELF';
1877
+				break;
1878
+			case 'parent':
1879
+				$newToken['type'] = 'T_PARENT';
1880
+				break;
1881
+			default:
1882
+				$newToken['type'] = 'T_STRING';
1883
+				break;
1884
+			}
1885
+
1886
+			$newToken['code'] = constant($newToken['type']);
1887
+
1888
+			self::$resolveTokenCache[$cacheKey] = $newToken;
1889
+		} else if ($token[0] === T_CURLY_OPEN) {
1890
+			$newToken = [
1891
+				'code' => T_OPEN_CURLY_BRACKET,
1892
+				'type' => 'T_OPEN_CURLY_BRACKET',
1893
+			];
1894
+		} else {
1895
+			$newToken = [
1896
+				'code' => $token[0],
1897
+				'type' => Util\Tokens::tokenName($token[0]),
1898
+			];
1899
+
1900
+			self::$resolveTokenCache[$token[0]] = $newToken;
1901
+		}//end if
1902
+
1903
+		$newToken['content'] = $token[1];
1904
+		return $newToken;
1905
+
1906
+	}//end standardiseToken()
1907
+
1908
+
1909
+	/**
1910
+	 * Converts simple tokens into a format that conforms to complex tokens
1911
+	 * produced by token_get_all().
1912
+	 *
1913
+	 * Simple tokens are tokens that are not in array form when produced from
1914
+	 * token_get_all().
1915
+	 *
1916
+	 * @param string $token The simple token to convert.
1917
+	 *
1918
+	 * @return array The new token in array format.
1919
+	 */
1920
+	public static function resolveSimpleToken($token)
1921
+	{
1922
+		$newToken = [];
1923
+
1924
+		switch ($token) {
1925
+		case '{':
1926
+			$newToken['type'] = 'T_OPEN_CURLY_BRACKET';
1927
+			break;
1928
+		case '}':
1929
+			$newToken['type'] = 'T_CLOSE_CURLY_BRACKET';
1930
+			break;
1931
+		case '[':
1932
+			$newToken['type'] = 'T_OPEN_SQUARE_BRACKET';
1933
+			break;
1934
+		case ']':
1935
+			$newToken['type'] = 'T_CLOSE_SQUARE_BRACKET';
1936
+			break;
1937
+		case '(':
1938
+			$newToken['type'] = 'T_OPEN_PARENTHESIS';
1939
+			break;
1940
+		case ')':
1941
+			$newToken['type'] = 'T_CLOSE_PARENTHESIS';
1942
+			break;
1943
+		case ':':
1944
+			$newToken['type'] = 'T_COLON';
1945
+			break;
1946
+		case '.':
1947
+			$newToken['type'] = 'T_STRING_CONCAT';
1948
+			break;
1949
+		case ';':
1950
+			$newToken['type'] = 'T_SEMICOLON';
1951
+			break;
1952
+		case '=':
1953
+			$newToken['type'] = 'T_EQUAL';
1954
+			break;
1955
+		case '*':
1956
+			$newToken['type'] = 'T_MULTIPLY';
1957
+			break;
1958
+		case '/':
1959
+			$newToken['type'] = 'T_DIVIDE';
1960
+			break;
1961
+		case '+':
1962
+			$newToken['type'] = 'T_PLUS';
1963
+			break;
1964
+		case '-':
1965
+			$newToken['type'] = 'T_MINUS';
1966
+			break;
1967
+		case '%':
1968
+			$newToken['type'] = 'T_MODULUS';
1969
+			break;
1970
+		case '^':
1971
+			$newToken['type'] = 'T_BITWISE_XOR';
1972
+			break;
1973
+		case '&':
1974
+			$newToken['type'] = 'T_BITWISE_AND';
1975
+			break;
1976
+		case '|':
1977
+			$newToken['type'] = 'T_BITWISE_OR';
1978
+			break;
1979
+		case '~':
1980
+			$newToken['type'] = 'T_BITWISE_NOT';
1981
+			break;
1982
+		case '<':
1983
+			$newToken['type'] = 'T_LESS_THAN';
1984
+			break;
1985
+		case '>':
1986
+			$newToken['type'] = 'T_GREATER_THAN';
1987
+			break;
1988
+		case '!':
1989
+			$newToken['type'] = 'T_BOOLEAN_NOT';
1990
+			break;
1991
+		case ',':
1992
+			$newToken['type'] = 'T_COMMA';
1993
+			break;
1994
+		case '@':
1995
+			$newToken['type'] = 'T_ASPERAND';
1996
+			break;
1997
+		case '$':
1998
+			$newToken['type'] = 'T_DOLLAR';
1999
+			break;
2000
+		case '`':
2001
+			$newToken['type'] = 'T_BACKTICK';
2002
+			break;
2003
+		default:
2004
+			$newToken['type'] = 'T_NONE';
2005
+			break;
2006
+		}//end switch
2007
+
2008
+		$newToken['code']    = constant($newToken['type']);
2009
+		$newToken['content'] = $token;
2010
+
2011
+		self::$resolveTokenCache[$token] = $newToken;
2012
+		return $newToken;
2013
+
2014
+	}//end resolveSimpleToken()
2015 2015
 
2016 2016
 
2017 2017
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Tokenizers/Tokenizer.php 1 patch
Indentation   +1620 added lines, -1620 removed lines patch added patch discarded remove patch
@@ -15,1632 +15,1632 @@
 block discarded – undo
15 15
 abstract class Tokenizer
16 16
 {
17 17
 
18
-    /**
19
-     * The config data for the run.
20
-     *
21
-     * @var \PHP_CodeSniffer\Config
22
-     */
23
-    protected $config = null;
24
-
25
-    /**
26
-     * The EOL char used in the content.
27
-     *
28
-     * @var string
29
-     */
30
-    protected $eolChar = [];
31
-
32
-    /**
33
-     * A token-based representation of the content.
34
-     *
35
-     * @var array
36
-     */
37
-    protected $tokens = [];
38
-
39
-    /**
40
-     * The number of tokens in the tokens array.
41
-     *
42
-     * @var integer
43
-     */
44
-    protected $numTokens = 0;
45
-
46
-    /**
47
-     * A list of tokens that are allowed to open a scope.
48
-     *
49
-     * @var array
50
-     */
51
-    public $scopeOpeners = [];
52
-
53
-    /**
54
-     * A list of tokens that end the scope.
55
-     *
56
-     * @var array
57
-     */
58
-    public $endScopeTokens = [];
59
-
60
-    /**
61
-     * Known lengths of tokens.
62
-     *
63
-     * @var array<int, int>
64
-     */
65
-    public $knownLengths = [];
66
-
67
-    /**
68
-     * A list of lines being ignored due to error suppression comments.
69
-     *
70
-     * @var array
71
-     */
72
-    public $ignoredLines = [];
73
-
74
-
75
-    /**
76
-     * Initialise and run the tokenizer.
77
-     *
78
-     * @param string                         $content The content to tokenize,
79
-     * @param \PHP_CodeSniffer\Config | null $config  The config data for the run.
80
-     * @param string                         $eolChar The EOL char used in the content.
81
-     *
82
-     * @return void
83
-     * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the file appears to be minified.
84
-     */
85
-    public function __construct($content, $config, $eolChar='\n')
86
-    {
87
-        $this->eolChar = $eolChar;
88
-
89
-        $this->config = $config;
90
-        $this->tokens = $this->tokenize($content);
91
-
92
-        if ($config === null) {
93
-            return;
94
-        }
95
-
96
-        $this->createPositionMap();
97
-        $this->createTokenMap();
98
-        $this->createParenthesisNestingMap();
99
-        $this->createScopeMap();
100
-        $this->createLevelMap();
101
-
102
-        // Allow the tokenizer to do additional processing if required.
103
-        $this->processAdditional();
104
-
105
-    }//end __construct()
106
-
107
-
108
-    /**
109
-     * Checks the content to see if it looks minified.
110
-     *
111
-     * @param string $content The content to tokenize.
112
-     * @param string $eolChar The EOL char used in the content.
113
-     *
114
-     * @return boolean
115
-     */
116
-    protected function isMinifiedContent($content, $eolChar='\n')
117
-    {
118
-        // Minified files often have a very large number of characters per line
119
-        // and cause issues when tokenizing.
120
-        $numChars = strlen($content);
121
-        $numLines = (substr_count($content, $eolChar) + 1);
122
-        $average  = ($numChars / $numLines);
123
-        if ($average > 100) {
124
-            return true;
125
-        }
126
-
127
-        return false;
128
-
129
-    }//end isMinifiedContent()
130
-
131
-
132
-    /**
133
-     * Gets the array of tokens.
134
-     *
135
-     * @return array
136
-     */
137
-    public function getTokens()
138
-    {
139
-        return $this->tokens;
140
-
141
-    }//end getTokens()
142
-
143
-
144
-    /**
145
-     * Creates an array of tokens when given some content.
146
-     *
147
-     * @param string $string The string to tokenize.
148
-     *
149
-     * @return array
150
-     */
151
-    abstract protected function tokenize($string);
152
-
153
-
154
-    /**
155
-     * Performs additional processing after main tokenizing.
156
-     *
157
-     * @return void
158
-     */
159
-    abstract protected function processAdditional();
160
-
161
-
162
-    /**
163
-     * Sets token position information.
164
-     *
165
-     * Can also convert tabs into spaces. Each tab can represent between
166
-     * 1 and $width spaces, so this cannot be a straight string replace.
167
-     *
168
-     * @return void
169
-     */
170
-    private function createPositionMap()
171
-    {
172
-        $currColumn = 1;
173
-        $lineNumber = 1;
174
-        $eolLen     = strlen($this->eolChar);
175
-        $ignoring   = null;
176
-        $inTests    = defined('PHP_CODESNIFFER_IN_TESTS');
177
-
178
-        $checkEncoding = false;
179
-        if (function_exists('iconv_strlen') === true) {
180
-            $checkEncoding = true;
181
-        }
182
-
183
-        $checkAnnotations = $this->config->annotations;
184
-        $encoding         = $this->config->encoding;
185
-        $tabWidth         = $this->config->tabWidth;
186
-
187
-        $tokensWithTabs = [
188
-            T_WHITESPACE               => true,
189
-            T_COMMENT                  => true,
190
-            T_DOC_COMMENT              => true,
191
-            T_DOC_COMMENT_WHITESPACE   => true,
192
-            T_DOC_COMMENT_STRING       => true,
193
-            T_CONSTANT_ENCAPSED_STRING => true,
194
-            T_DOUBLE_QUOTED_STRING     => true,
195
-            T_HEREDOC                  => true,
196
-            T_NOWDOC                   => true,
197
-            T_INLINE_HTML              => true,
198
-        ];
199
-
200
-        $this->numTokens = count($this->tokens);
201
-        for ($i = 0; $i < $this->numTokens; $i++) {
202
-            $this->tokens[$i]['line']   = $lineNumber;
203
-            $this->tokens[$i]['column'] = $currColumn;
204
-
205
-            if (isset($this->knownLengths[$this->tokens[$i]['code']]) === true) {
206
-                // There are no tabs in the tokens we know the length of.
207
-                $length      = $this->knownLengths[$this->tokens[$i]['code']];
208
-                $currColumn += $length;
209
-            } else if ($tabWidth === 0
210
-                || isset($tokensWithTabs[$this->tokens[$i]['code']]) === false
211
-                || strpos($this->tokens[$i]['content'], "\t") === false
212
-            ) {
213
-                // There are no tabs in this content, or we aren't replacing them.
214
-                if ($checkEncoding === true) {
215
-                    // Not using the default encoding, so take a bit more care.
216
-                    $oldLevel = error_reporting();
217
-                    error_reporting(0);
218
-                    $length = iconv_strlen($this->tokens[$i]['content'], $encoding);
219
-                    error_reporting($oldLevel);
220
-
221
-                    if ($length === false) {
222
-                        // String contained invalid characters, so revert to default.
223
-                        $length = strlen($this->tokens[$i]['content']);
224
-                    }
225
-                } else {
226
-                    $length = strlen($this->tokens[$i]['content']);
227
-                }
228
-
229
-                $currColumn += $length;
230
-            } else {
231
-                $this->replaceTabsInToken($this->tokens[$i]);
232
-                $length      = $this->tokens[$i]['length'];
233
-                $currColumn += $length;
234
-            }//end if
235
-
236
-            $this->tokens[$i]['length'] = $length;
237
-
238
-            if (isset($this->knownLengths[$this->tokens[$i]['code']]) === false
239
-                && strpos($this->tokens[$i]['content'], $this->eolChar) !== false
240
-            ) {
241
-                $lineNumber++;
242
-                $currColumn = 1;
243
-
244
-                // Newline chars are not counted in the token length.
245
-                $this->tokens[$i]['length'] -= $eolLen;
246
-            }
247
-
248
-            if ($this->tokens[$i]['code'] === T_COMMENT
249
-                || $this->tokens[$i]['code'] === T_DOC_COMMENT_STRING
250
-                || $this->tokens[$i]['code'] === T_DOC_COMMENT_TAG
251
-                || ($inTests === true && $this->tokens[$i]['code'] === T_INLINE_HTML)
252
-            ) {
253
-                $commentText      = ltrim($this->tokens[$i]['content'], " \t/*");
254
-                $commentText      = rtrim($commentText, " */\t\r\n");
255
-                $commentTextLower = strtolower($commentText);
256
-                if (strpos($commentText, '@codingStandards') !== false) {
257
-                    // If this comment is the only thing on the line, it tells us
258
-                    // to ignore the following line. If the line contains other content
259
-                    // then we are just ignoring this one single line.
260
-                    $ownLine = false;
261
-                    if ($i > 0) {
262
-                        for ($prev = ($i - 1); $prev >= 0; $prev--) {
263
-                            if ($this->tokens[$prev]['code'] === T_WHITESPACE) {
264
-                                continue;
265
-                            }
266
-
267
-                            break;
268
-                        }
269
-
270
-                        if ($this->tokens[$prev]['line'] !== $this->tokens[$i]['line']) {
271
-                            $ownLine = true;
272
-                        }
273
-                    }
274
-
275
-                    if ($ignoring === null
276
-                        && strpos($commentText, '@codingStandardsIgnoreStart') !== false
277
-                    ) {
278
-                        $ignoring = ['.all' => true];
279
-                        if ($ownLine === true) {
280
-                            $this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
281
-                        }
282
-                    } else if ($ignoring !== null
283
-                        && strpos($commentText, '@codingStandardsIgnoreEnd') !== false
284
-                    ) {
285
-                        if ($ownLine === true) {
286
-                            $this->ignoredLines[$this->tokens[$i]['line']] = ['.all' => true];
287
-                        } else {
288
-                            $this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
289
-                        }
290
-
291
-                        $ignoring = null;
292
-                    } else if ($ignoring === null
293
-                        && strpos($commentText, '@codingStandardsIgnoreLine') !== false
294
-                    ) {
295
-                        $ignoring = ['.all' => true];
296
-                        if ($ownLine === true) {
297
-                            $this->ignoredLines[$this->tokens[$i]['line']]       = $ignoring;
298
-                            $this->ignoredLines[($this->tokens[$i]['line'] + 1)] = $ignoring;
299
-                        } else {
300
-                            $this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
301
-                        }
302
-
303
-                        $ignoring = null;
304
-                    }//end if
305
-                } else if (substr($commentTextLower, 0, 6) === 'phpcs:'
306
-                    || substr($commentTextLower, 0, 7) === '@phpcs:'
307
-                ) {
308
-                    // If the @phpcs: syntax is being used, strip the @ to make
309
-                    // comparisons easier.
310
-                    if ($commentText[0] === '@') {
311
-                        $commentText      = substr($commentText, 1);
312
-                        $commentTextLower = strtolower($commentText);
313
-                    }
314
-
315
-                    // If there is a comment on the end, strip it off.
316
-                    $commentStart = strpos($commentTextLower, ' --');
317
-                    if ($commentStart !== false) {
318
-                        $commentText      = substr($commentText, 0, $commentStart);
319
-                        $commentTextLower = strtolower($commentText);
320
-                    }
321
-
322
-                    // If this comment is the only thing on the line, it tells us
323
-                    // to ignore the following line. If the line contains other content
324
-                    // then we are just ignoring this one single line.
325
-                    $lineHasOtherContent = false;
326
-                    $lineHasOtherTokens  = false;
327
-                    if ($i > 0) {
328
-                        for ($prev = ($i - 1); $prev > 0; $prev--) {
329
-                            if ($this->tokens[$prev]['line'] !== $this->tokens[$i]['line']) {
330
-                                // Changed lines.
331
-                                break;
332
-                            }
333
-
334
-                            if ($this->tokens[$prev]['code'] === T_WHITESPACE
335
-                                || ($this->tokens[$prev]['code'] === T_INLINE_HTML
336
-                                && trim($this->tokens[$prev]['content']) === '')
337
-                            ) {
338
-                                continue;
339
-                            }
340
-
341
-                            $lineHasOtherTokens = true;
342
-
343
-                            if ($this->tokens[$prev]['code'] === T_OPEN_TAG) {
344
-                                continue;
345
-                            }
346
-
347
-                            $lineHasOtherContent = true;
348
-                            break;
349
-                        }//end for
350
-
351
-                        $changedLines = false;
352
-                        for ($next = $i; $next < $this->numTokens; $next++) {
353
-                            if ($changedLines === true) {
354
-                                // Changed lines.
355
-                                break;
356
-                            }
357
-
358
-                            if (isset($this->knownLengths[$this->tokens[$next]['code']]) === false
359
-                                && strpos($this->tokens[$next]['content'], $this->eolChar) !== false
360
-                            ) {
361
-                                // Last token on the current line.
362
-                                $changedLines = true;
363
-                            }
364
-
365
-                            if ($next === $i) {
366
-                                continue;
367
-                            }
368
-
369
-                            if ($this->tokens[$next]['code'] === T_WHITESPACE
370
-                                || ($this->tokens[$next]['code'] === T_INLINE_HTML
371
-                                && trim($this->tokens[$next]['content']) === '')
372
-                            ) {
373
-                                continue;
374
-                            }
375
-
376
-                            $lineHasOtherTokens = true;
377
-
378
-                            if ($this->tokens[$next]['code'] === T_CLOSE_TAG) {
379
-                                continue;
380
-                            }
381
-
382
-                            $lineHasOtherContent = true;
383
-                            break;
384
-                        }//end for
385
-                    }//end if
386
-
387
-                    if (substr($commentTextLower, 0, 9) === 'phpcs:set') {
388
-                        // Ignore standards for complete lines that change sniff settings.
389
-                        if ($lineHasOtherTokens === false) {
390
-                            $this->ignoredLines[$this->tokens[$i]['line']] = ['.all' => true];
391
-                        }
392
-
393
-                        // Need to maintain case here, to get the correct sniff code.
394
-                        $parts = explode(' ', substr($commentText, 10));
395
-                        if (count($parts) >= 2) {
396
-                            $sniffParts = explode('.', $parts[0]);
397
-                            if (count($sniffParts) >= 3) {
398
-                                $this->tokens[$i]['sniffCode']          = array_shift($parts);
399
-                                $this->tokens[$i]['sniffProperty']      = array_shift($parts);
400
-                                $this->tokens[$i]['sniffPropertyValue'] = rtrim(implode(' ', $parts), " */\r\n");
401
-                            }
402
-                        }
403
-
404
-                        $this->tokens[$i]['code'] = T_PHPCS_SET;
405
-                        $this->tokens[$i]['type'] = 'T_PHPCS_SET';
406
-                    } else if (substr($commentTextLower, 0, 16) === 'phpcs:ignorefile') {
407
-                        // The whole file will be ignored, but at least set the correct token.
408
-                        $this->tokens[$i]['code'] = T_PHPCS_IGNORE_FILE;
409
-                        $this->tokens[$i]['type'] = 'T_PHPCS_IGNORE_FILE';
410
-                    } else if (substr($commentTextLower, 0, 13) === 'phpcs:disable') {
411
-                        if ($lineHasOtherContent === false) {
412
-                            // Completely ignore the comment line.
413
-                            $this->ignoredLines[$this->tokens[$i]['line']] = ['.all' => true];
414
-                        }
415
-
416
-                        if ($ignoring === null) {
417
-                            $ignoring = [];
418
-                        }
419
-
420
-                        $disabledSniffs = [];
421
-
422
-                        $additionalText = substr($commentText, 14);
423
-                        if ($additionalText === false) {
424
-                            $ignoring = ['.all' => true];
425
-                        } else {
426
-                            $parts = explode(',', substr($commentText, 13));
427
-                            foreach ($parts as $sniffCode) {
428
-                                $sniffCode = trim($sniffCode);
429
-                                $disabledSniffs[$sniffCode] = true;
430
-                                $ignoring[$sniffCode]       = true;
431
-
432
-                                // This newly disabled sniff might be disabling an existing
433
-                                // enabled exception that we are tracking.
434
-                                if (isset($ignoring['.except']) === true) {
435
-                                    foreach (array_keys($ignoring['.except']) as $ignoredSniffCode) {
436
-                                        if ($ignoredSniffCode === $sniffCode
437
-                                            || strpos($ignoredSniffCode, $sniffCode.'.') === 0
438
-                                        ) {
439
-                                            unset($ignoring['.except'][$ignoredSniffCode]);
440
-                                        }
441
-                                    }
442
-
443
-                                    if (empty($ignoring['.except']) === true) {
444
-                                        unset($ignoring['.except']);
445
-                                    }
446
-                                }
447
-                            }//end foreach
448
-                        }//end if
449
-
450
-                        $this->tokens[$i]['code']       = T_PHPCS_DISABLE;
451
-                        $this->tokens[$i]['type']       = 'T_PHPCS_DISABLE';
452
-                        $this->tokens[$i]['sniffCodes'] = $disabledSniffs;
453
-                    } else if (substr($commentTextLower, 0, 12) === 'phpcs:enable') {
454
-                        if ($ignoring !== null) {
455
-                            $enabledSniffs = [];
456
-
457
-                            $additionalText = substr($commentText, 13);
458
-                            if ($additionalText === false) {
459
-                                $ignoring = null;
460
-                            } else {
461
-                                $parts = explode(',', substr($commentText, 13));
462
-                                foreach ($parts as $sniffCode) {
463
-                                    $sniffCode = trim($sniffCode);
464
-                                    $enabledSniffs[$sniffCode] = true;
465
-
466
-                                    // This new enabled sniff might remove previously disabled
467
-                                    // sniffs if it is actually a standard or category of sniffs.
468
-                                    foreach (array_keys($ignoring) as $ignoredSniffCode) {
469
-                                        if ($ignoredSniffCode === $sniffCode
470
-                                            || strpos($ignoredSniffCode, $sniffCode.'.') === 0
471
-                                        ) {
472
-                                            unset($ignoring[$ignoredSniffCode]);
473
-                                        }
474
-                                    }
475
-
476
-                                    // This new enabled sniff might be able to clear up
477
-                                    // previously enabled sniffs if it is actually a standard or
478
-                                    // category of sniffs.
479
-                                    if (isset($ignoring['.except']) === true) {
480
-                                        foreach (array_keys($ignoring['.except']) as $ignoredSniffCode) {
481
-                                            if ($ignoredSniffCode === $sniffCode
482
-                                                || strpos($ignoredSniffCode, $sniffCode.'.') === 0
483
-                                            ) {
484
-                                                unset($ignoring['.except'][$ignoredSniffCode]);
485
-                                            }
486
-                                        }
487
-                                    }
488
-                                }//end foreach
489
-
490
-                                if (empty($ignoring) === true) {
491
-                                    $ignoring = null;
492
-                                } else {
493
-                                    if (isset($ignoring['.except']) === true) {
494
-                                        $ignoring['.except'] += $enabledSniffs;
495
-                                    } else {
496
-                                        $ignoring['.except'] = $enabledSniffs;
497
-                                    }
498
-                                }
499
-                            }//end if
500
-
501
-                            if ($lineHasOtherContent === false) {
502
-                                // Completely ignore the comment line.
503
-                                $this->ignoredLines[$this->tokens[$i]['line']] = ['.all' => true];
504
-                            } else {
505
-                                // The comment is on the same line as the code it is ignoring,
506
-                                // so respect the new ignore rules.
507
-                                $this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
508
-                            }
509
-
510
-                            $this->tokens[$i]['sniffCodes'] = $enabledSniffs;
511
-                        }//end if
512
-
513
-                        $this->tokens[$i]['code'] = T_PHPCS_ENABLE;
514
-                        $this->tokens[$i]['type'] = 'T_PHPCS_ENABLE';
515
-                    } else if (substr($commentTextLower, 0, 12) === 'phpcs:ignore') {
516
-                        $ignoreRules = [];
517
-
518
-                        $additionalText = substr($commentText, 13);
519
-                        if ($additionalText === false) {
520
-                            $ignoreRules = ['.all' => true];
521
-                        } else {
522
-                            $parts = explode(',', substr($commentText, 13));
523
-                            foreach ($parts as $sniffCode) {
524
-                                $ignoreRules[trim($sniffCode)] = true;
525
-                            }
526
-                        }
527
-
528
-                        $this->tokens[$i]['code']       = T_PHPCS_IGNORE;
529
-                        $this->tokens[$i]['type']       = 'T_PHPCS_IGNORE';
530
-                        $this->tokens[$i]['sniffCodes'] = $ignoreRules;
531
-
532
-                        if ($ignoring !== null) {
533
-                            $ignoreRules += $ignoring;
534
-                        }
535
-
536
-                        if ($lineHasOtherContent === false) {
537
-                            // Completely ignore the comment line, and set the following
538
-                            // line to include the ignore rules we've set.
539
-                            $this->ignoredLines[$this->tokens[$i]['line']]       = ['.all' => true];
540
-                            $this->ignoredLines[($this->tokens[$i]['line'] + 1)] = $ignoreRules;
541
-                        } else {
542
-                            // The comment is on the same line as the code it is ignoring,
543
-                            // so respect the ignore rules it set.
544
-                            $this->ignoredLines[$this->tokens[$i]['line']] = $ignoreRules;
545
-                        }
546
-                    }//end if
547
-                }//end if
548
-            }//end if
549
-
550
-            if ($ignoring !== null && isset($this->ignoredLines[$this->tokens[$i]['line']]) === false) {
551
-                $this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
552
-            }
553
-        }//end for
554
-
555
-        // If annotations are being ignored, we clear out all the ignore rules
556
-        // but leave the annotations tokenized as normal.
557
-        if ($checkAnnotations === false) {
558
-            $this->ignoredLines = [];
559
-        }
560
-
561
-    }//end createPositionMap()
562
-
563
-
564
-    /**
565
-     * Replaces tabs in original token content with spaces.
566
-     *
567
-     * Each tab can represent between 1 and $config->tabWidth spaces,
568
-     * so this cannot be a straight string replace. The original content
569
-     * is placed into an orig_content index and the new token length is also
570
-     * set in the length index.
571
-     *
572
-     * @param array  $token    The token to replace tabs inside.
573
-     * @param string $prefix   The character to use to represent the start of a tab.
574
-     * @param string $padding  The character to use to represent the end of a tab.
575
-     * @param int    $tabWidth The number of spaces each tab represents.
576
-     *
577
-     * @return void
578
-     */
579
-    public function replaceTabsInToken(&$token, $prefix=' ', $padding=' ', $tabWidth=null)
580
-    {
581
-        $checkEncoding = false;
582
-        if (function_exists('iconv_strlen') === true) {
583
-            $checkEncoding = true;
584
-        }
585
-
586
-        $currColumn = $token['column'];
587
-        if ($tabWidth === null) {
588
-            $tabWidth = $this->config->tabWidth;
589
-            if ($tabWidth === 0) {
590
-                $tabWidth = 1;
591
-            }
592
-        }
593
-
594
-        if (rtrim($token['content'], "\t") === '') {
595
-            // String only contains tabs, so we can shortcut the process.
596
-            $numTabs = strlen($token['content']);
597
-
598
-            $firstTabSize = ($tabWidth - (($currColumn - 1) % $tabWidth));
599
-            $length       = ($firstTabSize + ($tabWidth * ($numTabs - 1)));
600
-            $newContent   = $prefix.str_repeat($padding, ($length - 1));
601
-        } else {
602
-            // We need to determine the length of each tab.
603
-            $tabs = explode("\t", $token['content']);
604
-
605
-            $numTabs    = (count($tabs) - 1);
606
-            $tabNum     = 0;
607
-            $newContent = '';
608
-            $length     = 0;
609
-
610
-            foreach ($tabs as $content) {
611
-                if ($content !== '') {
612
-                    $newContent .= $content;
613
-                    if ($checkEncoding === true) {
614
-                        // Not using the default encoding, so take a bit more care.
615
-                        $oldLevel = error_reporting();
616
-                        error_reporting(0);
617
-                        $contentLength = iconv_strlen($content, $this->config->encoding);
618
-                        error_reporting($oldLevel);
619
-                        if ($contentLength === false) {
620
-                            // String contained invalid characters, so revert to default.
621
-                            $contentLength = strlen($content);
622
-                        }
623
-                    } else {
624
-                        $contentLength = strlen($content);
625
-                    }
626
-
627
-                    $currColumn += $contentLength;
628
-                    $length     += $contentLength;
629
-                }
630
-
631
-                // The last piece of content does not have a tab after it.
632
-                if ($tabNum === $numTabs) {
633
-                    break;
634
-                }
635
-
636
-                // Process the tab that comes after the content.
637
-                $lastCurrColumn = $currColumn;
638
-                $tabNum++;
639
-
640
-                // Move the pointer to the next tab stop.
641
-                if (($currColumn % $tabWidth) === 0) {
642
-                    // This is the first tab, and we are already at a
643
-                    // tab stop, so this tab counts as a single space.
644
-                    $currColumn++;
645
-                } else {
646
-                    $currColumn++;
647
-                    while (($currColumn % $tabWidth) !== 0) {
648
-                        $currColumn++;
649
-                    }
650
-
651
-                    $currColumn++;
652
-                }
653
-
654
-                $length     += ($currColumn - $lastCurrColumn);
655
-                $newContent .= $prefix.str_repeat($padding, ($currColumn - $lastCurrColumn - 1));
656
-            }//end foreach
657
-        }//end if
658
-
659
-        $token['orig_content'] = $token['content'];
660
-        $token['content']      = $newContent;
661
-        $token['length']       = $length;
662
-
663
-    }//end replaceTabsInToken()
664
-
665
-
666
-    /**
667
-     * Creates a map of brackets positions.
668
-     *
669
-     * @return void
670
-     */
671
-    private function createTokenMap()
672
-    {
673
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
674
-            echo "\t*** START TOKEN MAP ***".PHP_EOL;
675
-        }
676
-
677
-        $squareOpeners   = [];
678
-        $curlyOpeners    = [];
679
-        $this->numTokens = count($this->tokens);
680
-
681
-        $openers   = [];
682
-        $openOwner = null;
683
-
684
-        for ($i = 0; $i < $this->numTokens; $i++) {
685
-            /*
18
+	/**
19
+	 * The config data for the run.
20
+	 *
21
+	 * @var \PHP_CodeSniffer\Config
22
+	 */
23
+	protected $config = null;
24
+
25
+	/**
26
+	 * The EOL char used in the content.
27
+	 *
28
+	 * @var string
29
+	 */
30
+	protected $eolChar = [];
31
+
32
+	/**
33
+	 * A token-based representation of the content.
34
+	 *
35
+	 * @var array
36
+	 */
37
+	protected $tokens = [];
38
+
39
+	/**
40
+	 * The number of tokens in the tokens array.
41
+	 *
42
+	 * @var integer
43
+	 */
44
+	protected $numTokens = 0;
45
+
46
+	/**
47
+	 * A list of tokens that are allowed to open a scope.
48
+	 *
49
+	 * @var array
50
+	 */
51
+	public $scopeOpeners = [];
52
+
53
+	/**
54
+	 * A list of tokens that end the scope.
55
+	 *
56
+	 * @var array
57
+	 */
58
+	public $endScopeTokens = [];
59
+
60
+	/**
61
+	 * Known lengths of tokens.
62
+	 *
63
+	 * @var array<int, int>
64
+	 */
65
+	public $knownLengths = [];
66
+
67
+	/**
68
+	 * A list of lines being ignored due to error suppression comments.
69
+	 *
70
+	 * @var array
71
+	 */
72
+	public $ignoredLines = [];
73
+
74
+
75
+	/**
76
+	 * Initialise and run the tokenizer.
77
+	 *
78
+	 * @param string                         $content The content to tokenize,
79
+	 * @param \PHP_CodeSniffer\Config | null $config  The config data for the run.
80
+	 * @param string                         $eolChar The EOL char used in the content.
81
+	 *
82
+	 * @return void
83
+	 * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the file appears to be minified.
84
+	 */
85
+	public function __construct($content, $config, $eolChar='\n')
86
+	{
87
+		$this->eolChar = $eolChar;
88
+
89
+		$this->config = $config;
90
+		$this->tokens = $this->tokenize($content);
91
+
92
+		if ($config === null) {
93
+			return;
94
+		}
95
+
96
+		$this->createPositionMap();
97
+		$this->createTokenMap();
98
+		$this->createParenthesisNestingMap();
99
+		$this->createScopeMap();
100
+		$this->createLevelMap();
101
+
102
+		// Allow the tokenizer to do additional processing if required.
103
+		$this->processAdditional();
104
+
105
+	}//end __construct()
106
+
107
+
108
+	/**
109
+	 * Checks the content to see if it looks minified.
110
+	 *
111
+	 * @param string $content The content to tokenize.
112
+	 * @param string $eolChar The EOL char used in the content.
113
+	 *
114
+	 * @return boolean
115
+	 */
116
+	protected function isMinifiedContent($content, $eolChar='\n')
117
+	{
118
+		// Minified files often have a very large number of characters per line
119
+		// and cause issues when tokenizing.
120
+		$numChars = strlen($content);
121
+		$numLines = (substr_count($content, $eolChar) + 1);
122
+		$average  = ($numChars / $numLines);
123
+		if ($average > 100) {
124
+			return true;
125
+		}
126
+
127
+		return false;
128
+
129
+	}//end isMinifiedContent()
130
+
131
+
132
+	/**
133
+	 * Gets the array of tokens.
134
+	 *
135
+	 * @return array
136
+	 */
137
+	public function getTokens()
138
+	{
139
+		return $this->tokens;
140
+
141
+	}//end getTokens()
142
+
143
+
144
+	/**
145
+	 * Creates an array of tokens when given some content.
146
+	 *
147
+	 * @param string $string The string to tokenize.
148
+	 *
149
+	 * @return array
150
+	 */
151
+	abstract protected function tokenize($string);
152
+
153
+
154
+	/**
155
+	 * Performs additional processing after main tokenizing.
156
+	 *
157
+	 * @return void
158
+	 */
159
+	abstract protected function processAdditional();
160
+
161
+
162
+	/**
163
+	 * Sets token position information.
164
+	 *
165
+	 * Can also convert tabs into spaces. Each tab can represent between
166
+	 * 1 and $width spaces, so this cannot be a straight string replace.
167
+	 *
168
+	 * @return void
169
+	 */
170
+	private function createPositionMap()
171
+	{
172
+		$currColumn = 1;
173
+		$lineNumber = 1;
174
+		$eolLen     = strlen($this->eolChar);
175
+		$ignoring   = null;
176
+		$inTests    = defined('PHP_CODESNIFFER_IN_TESTS');
177
+
178
+		$checkEncoding = false;
179
+		if (function_exists('iconv_strlen') === true) {
180
+			$checkEncoding = true;
181
+		}
182
+
183
+		$checkAnnotations = $this->config->annotations;
184
+		$encoding         = $this->config->encoding;
185
+		$tabWidth         = $this->config->tabWidth;
186
+
187
+		$tokensWithTabs = [
188
+			T_WHITESPACE               => true,
189
+			T_COMMENT                  => true,
190
+			T_DOC_COMMENT              => true,
191
+			T_DOC_COMMENT_WHITESPACE   => true,
192
+			T_DOC_COMMENT_STRING       => true,
193
+			T_CONSTANT_ENCAPSED_STRING => true,
194
+			T_DOUBLE_QUOTED_STRING     => true,
195
+			T_HEREDOC                  => true,
196
+			T_NOWDOC                   => true,
197
+			T_INLINE_HTML              => true,
198
+		];
199
+
200
+		$this->numTokens = count($this->tokens);
201
+		for ($i = 0; $i < $this->numTokens; $i++) {
202
+			$this->tokens[$i]['line']   = $lineNumber;
203
+			$this->tokens[$i]['column'] = $currColumn;
204
+
205
+			if (isset($this->knownLengths[$this->tokens[$i]['code']]) === true) {
206
+				// There are no tabs in the tokens we know the length of.
207
+				$length      = $this->knownLengths[$this->tokens[$i]['code']];
208
+				$currColumn += $length;
209
+			} else if ($tabWidth === 0
210
+				|| isset($tokensWithTabs[$this->tokens[$i]['code']]) === false
211
+				|| strpos($this->tokens[$i]['content'], "\t") === false
212
+			) {
213
+				// There are no tabs in this content, or we aren't replacing them.
214
+				if ($checkEncoding === true) {
215
+					// Not using the default encoding, so take a bit more care.
216
+					$oldLevel = error_reporting();
217
+					error_reporting(0);
218
+					$length = iconv_strlen($this->tokens[$i]['content'], $encoding);
219
+					error_reporting($oldLevel);
220
+
221
+					if ($length === false) {
222
+						// String contained invalid characters, so revert to default.
223
+						$length = strlen($this->tokens[$i]['content']);
224
+					}
225
+				} else {
226
+					$length = strlen($this->tokens[$i]['content']);
227
+				}
228
+
229
+				$currColumn += $length;
230
+			} else {
231
+				$this->replaceTabsInToken($this->tokens[$i]);
232
+				$length      = $this->tokens[$i]['length'];
233
+				$currColumn += $length;
234
+			}//end if
235
+
236
+			$this->tokens[$i]['length'] = $length;
237
+
238
+			if (isset($this->knownLengths[$this->tokens[$i]['code']]) === false
239
+				&& strpos($this->tokens[$i]['content'], $this->eolChar) !== false
240
+			) {
241
+				$lineNumber++;
242
+				$currColumn = 1;
243
+
244
+				// Newline chars are not counted in the token length.
245
+				$this->tokens[$i]['length'] -= $eolLen;
246
+			}
247
+
248
+			if ($this->tokens[$i]['code'] === T_COMMENT
249
+				|| $this->tokens[$i]['code'] === T_DOC_COMMENT_STRING
250
+				|| $this->tokens[$i]['code'] === T_DOC_COMMENT_TAG
251
+				|| ($inTests === true && $this->tokens[$i]['code'] === T_INLINE_HTML)
252
+			) {
253
+				$commentText      = ltrim($this->tokens[$i]['content'], " \t/*");
254
+				$commentText      = rtrim($commentText, " */\t\r\n");
255
+				$commentTextLower = strtolower($commentText);
256
+				if (strpos($commentText, '@codingStandards') !== false) {
257
+					// If this comment is the only thing on the line, it tells us
258
+					// to ignore the following line. If the line contains other content
259
+					// then we are just ignoring this one single line.
260
+					$ownLine = false;
261
+					if ($i > 0) {
262
+						for ($prev = ($i - 1); $prev >= 0; $prev--) {
263
+							if ($this->tokens[$prev]['code'] === T_WHITESPACE) {
264
+								continue;
265
+							}
266
+
267
+							break;
268
+						}
269
+
270
+						if ($this->tokens[$prev]['line'] !== $this->tokens[$i]['line']) {
271
+							$ownLine = true;
272
+						}
273
+					}
274
+
275
+					if ($ignoring === null
276
+						&& strpos($commentText, '@codingStandardsIgnoreStart') !== false
277
+					) {
278
+						$ignoring = ['.all' => true];
279
+						if ($ownLine === true) {
280
+							$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
281
+						}
282
+					} else if ($ignoring !== null
283
+						&& strpos($commentText, '@codingStandardsIgnoreEnd') !== false
284
+					) {
285
+						if ($ownLine === true) {
286
+							$this->ignoredLines[$this->tokens[$i]['line']] = ['.all' => true];
287
+						} else {
288
+							$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
289
+						}
290
+
291
+						$ignoring = null;
292
+					} else if ($ignoring === null
293
+						&& strpos($commentText, '@codingStandardsIgnoreLine') !== false
294
+					) {
295
+						$ignoring = ['.all' => true];
296
+						if ($ownLine === true) {
297
+							$this->ignoredLines[$this->tokens[$i]['line']]       = $ignoring;
298
+							$this->ignoredLines[($this->tokens[$i]['line'] + 1)] = $ignoring;
299
+						} else {
300
+							$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
301
+						}
302
+
303
+						$ignoring = null;
304
+					}//end if
305
+				} else if (substr($commentTextLower, 0, 6) === 'phpcs:'
306
+					|| substr($commentTextLower, 0, 7) === '@phpcs:'
307
+				) {
308
+					// If the @phpcs: syntax is being used, strip the @ to make
309
+					// comparisons easier.
310
+					if ($commentText[0] === '@') {
311
+						$commentText      = substr($commentText, 1);
312
+						$commentTextLower = strtolower($commentText);
313
+					}
314
+
315
+					// If there is a comment on the end, strip it off.
316
+					$commentStart = strpos($commentTextLower, ' --');
317
+					if ($commentStart !== false) {
318
+						$commentText      = substr($commentText, 0, $commentStart);
319
+						$commentTextLower = strtolower($commentText);
320
+					}
321
+
322
+					// If this comment is the only thing on the line, it tells us
323
+					// to ignore the following line. If the line contains other content
324
+					// then we are just ignoring this one single line.
325
+					$lineHasOtherContent = false;
326
+					$lineHasOtherTokens  = false;
327
+					if ($i > 0) {
328
+						for ($prev = ($i - 1); $prev > 0; $prev--) {
329
+							if ($this->tokens[$prev]['line'] !== $this->tokens[$i]['line']) {
330
+								// Changed lines.
331
+								break;
332
+							}
333
+
334
+							if ($this->tokens[$prev]['code'] === T_WHITESPACE
335
+								|| ($this->tokens[$prev]['code'] === T_INLINE_HTML
336
+								&& trim($this->tokens[$prev]['content']) === '')
337
+							) {
338
+								continue;
339
+							}
340
+
341
+							$lineHasOtherTokens = true;
342
+
343
+							if ($this->tokens[$prev]['code'] === T_OPEN_TAG) {
344
+								continue;
345
+							}
346
+
347
+							$lineHasOtherContent = true;
348
+							break;
349
+						}//end for
350
+
351
+						$changedLines = false;
352
+						for ($next = $i; $next < $this->numTokens; $next++) {
353
+							if ($changedLines === true) {
354
+								// Changed lines.
355
+								break;
356
+							}
357
+
358
+							if (isset($this->knownLengths[$this->tokens[$next]['code']]) === false
359
+								&& strpos($this->tokens[$next]['content'], $this->eolChar) !== false
360
+							) {
361
+								// Last token on the current line.
362
+								$changedLines = true;
363
+							}
364
+
365
+							if ($next === $i) {
366
+								continue;
367
+							}
368
+
369
+							if ($this->tokens[$next]['code'] === T_WHITESPACE
370
+								|| ($this->tokens[$next]['code'] === T_INLINE_HTML
371
+								&& trim($this->tokens[$next]['content']) === '')
372
+							) {
373
+								continue;
374
+							}
375
+
376
+							$lineHasOtherTokens = true;
377
+
378
+							if ($this->tokens[$next]['code'] === T_CLOSE_TAG) {
379
+								continue;
380
+							}
381
+
382
+							$lineHasOtherContent = true;
383
+							break;
384
+						}//end for
385
+					}//end if
386
+
387
+					if (substr($commentTextLower, 0, 9) === 'phpcs:set') {
388
+						// Ignore standards for complete lines that change sniff settings.
389
+						if ($lineHasOtherTokens === false) {
390
+							$this->ignoredLines[$this->tokens[$i]['line']] = ['.all' => true];
391
+						}
392
+
393
+						// Need to maintain case here, to get the correct sniff code.
394
+						$parts = explode(' ', substr($commentText, 10));
395
+						if (count($parts) >= 2) {
396
+							$sniffParts = explode('.', $parts[0]);
397
+							if (count($sniffParts) >= 3) {
398
+								$this->tokens[$i]['sniffCode']          = array_shift($parts);
399
+								$this->tokens[$i]['sniffProperty']      = array_shift($parts);
400
+								$this->tokens[$i]['sniffPropertyValue'] = rtrim(implode(' ', $parts), " */\r\n");
401
+							}
402
+						}
403
+
404
+						$this->tokens[$i]['code'] = T_PHPCS_SET;
405
+						$this->tokens[$i]['type'] = 'T_PHPCS_SET';
406
+					} else if (substr($commentTextLower, 0, 16) === 'phpcs:ignorefile') {
407
+						// The whole file will be ignored, but at least set the correct token.
408
+						$this->tokens[$i]['code'] = T_PHPCS_IGNORE_FILE;
409
+						$this->tokens[$i]['type'] = 'T_PHPCS_IGNORE_FILE';
410
+					} else if (substr($commentTextLower, 0, 13) === 'phpcs:disable') {
411
+						if ($lineHasOtherContent === false) {
412
+							// Completely ignore the comment line.
413
+							$this->ignoredLines[$this->tokens[$i]['line']] = ['.all' => true];
414
+						}
415
+
416
+						if ($ignoring === null) {
417
+							$ignoring = [];
418
+						}
419
+
420
+						$disabledSniffs = [];
421
+
422
+						$additionalText = substr($commentText, 14);
423
+						if ($additionalText === false) {
424
+							$ignoring = ['.all' => true];
425
+						} else {
426
+							$parts = explode(',', substr($commentText, 13));
427
+							foreach ($parts as $sniffCode) {
428
+								$sniffCode = trim($sniffCode);
429
+								$disabledSniffs[$sniffCode] = true;
430
+								$ignoring[$sniffCode]       = true;
431
+
432
+								// This newly disabled sniff might be disabling an existing
433
+								// enabled exception that we are tracking.
434
+								if (isset($ignoring['.except']) === true) {
435
+									foreach (array_keys($ignoring['.except']) as $ignoredSniffCode) {
436
+										if ($ignoredSniffCode === $sniffCode
437
+											|| strpos($ignoredSniffCode, $sniffCode.'.') === 0
438
+										) {
439
+											unset($ignoring['.except'][$ignoredSniffCode]);
440
+										}
441
+									}
442
+
443
+									if (empty($ignoring['.except']) === true) {
444
+										unset($ignoring['.except']);
445
+									}
446
+								}
447
+							}//end foreach
448
+						}//end if
449
+
450
+						$this->tokens[$i]['code']       = T_PHPCS_DISABLE;
451
+						$this->tokens[$i]['type']       = 'T_PHPCS_DISABLE';
452
+						$this->tokens[$i]['sniffCodes'] = $disabledSniffs;
453
+					} else if (substr($commentTextLower, 0, 12) === 'phpcs:enable') {
454
+						if ($ignoring !== null) {
455
+							$enabledSniffs = [];
456
+
457
+							$additionalText = substr($commentText, 13);
458
+							if ($additionalText === false) {
459
+								$ignoring = null;
460
+							} else {
461
+								$parts = explode(',', substr($commentText, 13));
462
+								foreach ($parts as $sniffCode) {
463
+									$sniffCode = trim($sniffCode);
464
+									$enabledSniffs[$sniffCode] = true;
465
+
466
+									// This new enabled sniff might remove previously disabled
467
+									// sniffs if it is actually a standard or category of sniffs.
468
+									foreach (array_keys($ignoring) as $ignoredSniffCode) {
469
+										if ($ignoredSniffCode === $sniffCode
470
+											|| strpos($ignoredSniffCode, $sniffCode.'.') === 0
471
+										) {
472
+											unset($ignoring[$ignoredSniffCode]);
473
+										}
474
+									}
475
+
476
+									// This new enabled sniff might be able to clear up
477
+									// previously enabled sniffs if it is actually a standard or
478
+									// category of sniffs.
479
+									if (isset($ignoring['.except']) === true) {
480
+										foreach (array_keys($ignoring['.except']) as $ignoredSniffCode) {
481
+											if ($ignoredSniffCode === $sniffCode
482
+												|| strpos($ignoredSniffCode, $sniffCode.'.') === 0
483
+											) {
484
+												unset($ignoring['.except'][$ignoredSniffCode]);
485
+											}
486
+										}
487
+									}
488
+								}//end foreach
489
+
490
+								if (empty($ignoring) === true) {
491
+									$ignoring = null;
492
+								} else {
493
+									if (isset($ignoring['.except']) === true) {
494
+										$ignoring['.except'] += $enabledSniffs;
495
+									} else {
496
+										$ignoring['.except'] = $enabledSniffs;
497
+									}
498
+								}
499
+							}//end if
500
+
501
+							if ($lineHasOtherContent === false) {
502
+								// Completely ignore the comment line.
503
+								$this->ignoredLines[$this->tokens[$i]['line']] = ['.all' => true];
504
+							} else {
505
+								// The comment is on the same line as the code it is ignoring,
506
+								// so respect the new ignore rules.
507
+								$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
508
+							}
509
+
510
+							$this->tokens[$i]['sniffCodes'] = $enabledSniffs;
511
+						}//end if
512
+
513
+						$this->tokens[$i]['code'] = T_PHPCS_ENABLE;
514
+						$this->tokens[$i]['type'] = 'T_PHPCS_ENABLE';
515
+					} else if (substr($commentTextLower, 0, 12) === 'phpcs:ignore') {
516
+						$ignoreRules = [];
517
+
518
+						$additionalText = substr($commentText, 13);
519
+						if ($additionalText === false) {
520
+							$ignoreRules = ['.all' => true];
521
+						} else {
522
+							$parts = explode(',', substr($commentText, 13));
523
+							foreach ($parts as $sniffCode) {
524
+								$ignoreRules[trim($sniffCode)] = true;
525
+							}
526
+						}
527
+
528
+						$this->tokens[$i]['code']       = T_PHPCS_IGNORE;
529
+						$this->tokens[$i]['type']       = 'T_PHPCS_IGNORE';
530
+						$this->tokens[$i]['sniffCodes'] = $ignoreRules;
531
+
532
+						if ($ignoring !== null) {
533
+							$ignoreRules += $ignoring;
534
+						}
535
+
536
+						if ($lineHasOtherContent === false) {
537
+							// Completely ignore the comment line, and set the following
538
+							// line to include the ignore rules we've set.
539
+							$this->ignoredLines[$this->tokens[$i]['line']]       = ['.all' => true];
540
+							$this->ignoredLines[($this->tokens[$i]['line'] + 1)] = $ignoreRules;
541
+						} else {
542
+							// The comment is on the same line as the code it is ignoring,
543
+							// so respect the ignore rules it set.
544
+							$this->ignoredLines[$this->tokens[$i]['line']] = $ignoreRules;
545
+						}
546
+					}//end if
547
+				}//end if
548
+			}//end if
549
+
550
+			if ($ignoring !== null && isset($this->ignoredLines[$this->tokens[$i]['line']]) === false) {
551
+				$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
552
+			}
553
+		}//end for
554
+
555
+		// If annotations are being ignored, we clear out all the ignore rules
556
+		// but leave the annotations tokenized as normal.
557
+		if ($checkAnnotations === false) {
558
+			$this->ignoredLines = [];
559
+		}
560
+
561
+	}//end createPositionMap()
562
+
563
+
564
+	/**
565
+	 * Replaces tabs in original token content with spaces.
566
+	 *
567
+	 * Each tab can represent between 1 and $config->tabWidth spaces,
568
+	 * so this cannot be a straight string replace. The original content
569
+	 * is placed into an orig_content index and the new token length is also
570
+	 * set in the length index.
571
+	 *
572
+	 * @param array  $token    The token to replace tabs inside.
573
+	 * @param string $prefix   The character to use to represent the start of a tab.
574
+	 * @param string $padding  The character to use to represent the end of a tab.
575
+	 * @param int    $tabWidth The number of spaces each tab represents.
576
+	 *
577
+	 * @return void
578
+	 */
579
+	public function replaceTabsInToken(&$token, $prefix=' ', $padding=' ', $tabWidth=null)
580
+	{
581
+		$checkEncoding = false;
582
+		if (function_exists('iconv_strlen') === true) {
583
+			$checkEncoding = true;
584
+		}
585
+
586
+		$currColumn = $token['column'];
587
+		if ($tabWidth === null) {
588
+			$tabWidth = $this->config->tabWidth;
589
+			if ($tabWidth === 0) {
590
+				$tabWidth = 1;
591
+			}
592
+		}
593
+
594
+		if (rtrim($token['content'], "\t") === '') {
595
+			// String only contains tabs, so we can shortcut the process.
596
+			$numTabs = strlen($token['content']);
597
+
598
+			$firstTabSize = ($tabWidth - (($currColumn - 1) % $tabWidth));
599
+			$length       = ($firstTabSize + ($tabWidth * ($numTabs - 1)));
600
+			$newContent   = $prefix.str_repeat($padding, ($length - 1));
601
+		} else {
602
+			// We need to determine the length of each tab.
603
+			$tabs = explode("\t", $token['content']);
604
+
605
+			$numTabs    = (count($tabs) - 1);
606
+			$tabNum     = 0;
607
+			$newContent = '';
608
+			$length     = 0;
609
+
610
+			foreach ($tabs as $content) {
611
+				if ($content !== '') {
612
+					$newContent .= $content;
613
+					if ($checkEncoding === true) {
614
+						// Not using the default encoding, so take a bit more care.
615
+						$oldLevel = error_reporting();
616
+						error_reporting(0);
617
+						$contentLength = iconv_strlen($content, $this->config->encoding);
618
+						error_reporting($oldLevel);
619
+						if ($contentLength === false) {
620
+							// String contained invalid characters, so revert to default.
621
+							$contentLength = strlen($content);
622
+						}
623
+					} else {
624
+						$contentLength = strlen($content);
625
+					}
626
+
627
+					$currColumn += $contentLength;
628
+					$length     += $contentLength;
629
+				}
630
+
631
+				// The last piece of content does not have a tab after it.
632
+				if ($tabNum === $numTabs) {
633
+					break;
634
+				}
635
+
636
+				// Process the tab that comes after the content.
637
+				$lastCurrColumn = $currColumn;
638
+				$tabNum++;
639
+
640
+				// Move the pointer to the next tab stop.
641
+				if (($currColumn % $tabWidth) === 0) {
642
+					// This is the first tab, and we are already at a
643
+					// tab stop, so this tab counts as a single space.
644
+					$currColumn++;
645
+				} else {
646
+					$currColumn++;
647
+					while (($currColumn % $tabWidth) !== 0) {
648
+						$currColumn++;
649
+					}
650
+
651
+					$currColumn++;
652
+				}
653
+
654
+				$length     += ($currColumn - $lastCurrColumn);
655
+				$newContent .= $prefix.str_repeat($padding, ($currColumn - $lastCurrColumn - 1));
656
+			}//end foreach
657
+		}//end if
658
+
659
+		$token['orig_content'] = $token['content'];
660
+		$token['content']      = $newContent;
661
+		$token['length']       = $length;
662
+
663
+	}//end replaceTabsInToken()
664
+
665
+
666
+	/**
667
+	 * Creates a map of brackets positions.
668
+	 *
669
+	 * @return void
670
+	 */
671
+	private function createTokenMap()
672
+	{
673
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
674
+			echo "\t*** START TOKEN MAP ***".PHP_EOL;
675
+		}
676
+
677
+		$squareOpeners   = [];
678
+		$curlyOpeners    = [];
679
+		$this->numTokens = count($this->tokens);
680
+
681
+		$openers   = [];
682
+		$openOwner = null;
683
+
684
+		for ($i = 0; $i < $this->numTokens; $i++) {
685
+			/*
686 686
                 Parenthesis mapping.
687 687
             */
688 688
 
689
-            if (isset(Util\Tokens::$parenthesisOpeners[$this->tokens[$i]['code']]) === true) {
690
-                $this->tokens[$i]['parenthesis_opener'] = null;
691
-                $this->tokens[$i]['parenthesis_closer'] = null;
692
-                $this->tokens[$i]['parenthesis_owner']  = $i;
693
-                $openOwner = $i;
694
-            } else if ($this->tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
695
-                $openers[] = $i;
696
-                $this->tokens[$i]['parenthesis_opener'] = $i;
697
-                if ($openOwner !== null) {
698
-                    $this->tokens[$openOwner]['parenthesis_opener'] = $i;
699
-                    $this->tokens[$i]['parenthesis_owner']          = $openOwner;
700
-                    $openOwner = null;
701
-                }
702
-            } else if ($this->tokens[$i]['code'] === T_CLOSE_PARENTHESIS) {
703
-                // Did we set an owner for this set of parenthesis?
704
-                $numOpeners = count($openers);
705
-                if ($numOpeners !== 0) {
706
-                    $opener = array_pop($openers);
707
-                    if (isset($this->tokens[$opener]['parenthesis_owner']) === true) {
708
-                        $owner = $this->tokens[$opener]['parenthesis_owner'];
709
-
710
-                        $this->tokens[$owner]['parenthesis_closer'] = $i;
711
-                        $this->tokens[$i]['parenthesis_owner']      = $owner;
712
-                    }
713
-
714
-                    $this->tokens[$i]['parenthesis_opener']      = $opener;
715
-                    $this->tokens[$i]['parenthesis_closer']      = $i;
716
-                    $this->tokens[$opener]['parenthesis_closer'] = $i;
717
-                }
718
-            }//end if
719
-
720
-            /*
689
+			if (isset(Util\Tokens::$parenthesisOpeners[$this->tokens[$i]['code']]) === true) {
690
+				$this->tokens[$i]['parenthesis_opener'] = null;
691
+				$this->tokens[$i]['parenthesis_closer'] = null;
692
+				$this->tokens[$i]['parenthesis_owner']  = $i;
693
+				$openOwner = $i;
694
+			} else if ($this->tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
695
+				$openers[] = $i;
696
+				$this->tokens[$i]['parenthesis_opener'] = $i;
697
+				if ($openOwner !== null) {
698
+					$this->tokens[$openOwner]['parenthesis_opener'] = $i;
699
+					$this->tokens[$i]['parenthesis_owner']          = $openOwner;
700
+					$openOwner = null;
701
+				}
702
+			} else if ($this->tokens[$i]['code'] === T_CLOSE_PARENTHESIS) {
703
+				// Did we set an owner for this set of parenthesis?
704
+				$numOpeners = count($openers);
705
+				if ($numOpeners !== 0) {
706
+					$opener = array_pop($openers);
707
+					if (isset($this->tokens[$opener]['parenthesis_owner']) === true) {
708
+						$owner = $this->tokens[$opener]['parenthesis_owner'];
709
+
710
+						$this->tokens[$owner]['parenthesis_closer'] = $i;
711
+						$this->tokens[$i]['parenthesis_owner']      = $owner;
712
+					}
713
+
714
+					$this->tokens[$i]['parenthesis_opener']      = $opener;
715
+					$this->tokens[$i]['parenthesis_closer']      = $i;
716
+					$this->tokens[$opener]['parenthesis_closer'] = $i;
717
+				}
718
+			}//end if
719
+
720
+			/*
721 721
                 Bracket mapping.
722 722
             */
723 723
 
724
-            switch ($this->tokens[$i]['code']) {
725
-            case T_OPEN_SQUARE_BRACKET:
726
-                $squareOpeners[] = $i;
727
-
728
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
729
-                    echo str_repeat("\t", count($squareOpeners));
730
-                    echo str_repeat("\t", count($curlyOpeners));
731
-                    echo "=> Found square bracket opener at $i".PHP_EOL;
732
-                }
733
-                break;
734
-            case T_OPEN_CURLY_BRACKET:
735
-                if (isset($this->tokens[$i]['scope_closer']) === false) {
736
-                    $curlyOpeners[] = $i;
737
-
738
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
739
-                        echo str_repeat("\t", count($squareOpeners));
740
-                        echo str_repeat("\t", count($curlyOpeners));
741
-                        echo "=> Found curly bracket opener at $i".PHP_EOL;
742
-                    }
743
-                }
744
-                break;
745
-            case T_CLOSE_SQUARE_BRACKET:
746
-                if (empty($squareOpeners) === false) {
747
-                    $opener = array_pop($squareOpeners);
748
-                    $this->tokens[$i]['bracket_opener']      = $opener;
749
-                    $this->tokens[$i]['bracket_closer']      = $i;
750
-                    $this->tokens[$opener]['bracket_opener'] = $opener;
751
-                    $this->tokens[$opener]['bracket_closer'] = $i;
752
-
753
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
754
-                        echo str_repeat("\t", count($squareOpeners));
755
-                        echo str_repeat("\t", count($curlyOpeners));
756
-                        echo "\t=> Found square bracket closer at $i for $opener".PHP_EOL;
757
-                    }
758
-                }
759
-                break;
760
-            case T_CLOSE_CURLY_BRACKET:
761
-                if (empty($curlyOpeners) === false
762
-                    && isset($this->tokens[$i]['scope_opener']) === false
763
-                ) {
764
-                    $opener = array_pop($curlyOpeners);
765
-                    $this->tokens[$i]['bracket_opener']      = $opener;
766
-                    $this->tokens[$i]['bracket_closer']      = $i;
767
-                    $this->tokens[$opener]['bracket_opener'] = $opener;
768
-                    $this->tokens[$opener]['bracket_closer'] = $i;
769
-
770
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
771
-                        echo str_repeat("\t", count($squareOpeners));
772
-                        echo str_repeat("\t", count($curlyOpeners));
773
-                        echo "\t=> Found curly bracket closer at $i for $opener".PHP_EOL;
774
-                    }
775
-                }
776
-                break;
777
-            default:
778
-                continue 2;
779
-            }//end switch
780
-        }//end for
781
-
782
-        // Cleanup for any openers that we didn't find closers for.
783
-        // This typically means there was a syntax error breaking things.
784
-        foreach ($openers as $opener) {
785
-            unset($this->tokens[$opener]['parenthesis_opener']);
786
-            unset($this->tokens[$opener]['parenthesis_owner']);
787
-        }
788
-
789
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
790
-            echo "\t*** END TOKEN MAP ***".PHP_EOL;
791
-        }
792
-
793
-    }//end createTokenMap()
794
-
795
-
796
-    /**
797
-     * Creates a map for the parenthesis tokens that surround other tokens.
798
-     *
799
-     * @return void
800
-     */
801
-    private function createParenthesisNestingMap()
802
-    {
803
-        $map = [];
804
-        for ($i = 0; $i < $this->numTokens; $i++) {
805
-            if (isset($this->tokens[$i]['parenthesis_opener']) === true
806
-                && $i === $this->tokens[$i]['parenthesis_opener']
807
-            ) {
808
-                if (empty($map) === false) {
809
-                    $this->tokens[$i]['nested_parenthesis'] = $map;
810
-                }
811
-
812
-                if (isset($this->tokens[$i]['parenthesis_closer']) === true) {
813
-                    $map[$this->tokens[$i]['parenthesis_opener']]
814
-                        = $this->tokens[$i]['parenthesis_closer'];
815
-                }
816
-            } else if (isset($this->tokens[$i]['parenthesis_closer']) === true
817
-                && $i === $this->tokens[$i]['parenthesis_closer']
818
-            ) {
819
-                array_pop($map);
820
-                if (empty($map) === false) {
821
-                    $this->tokens[$i]['nested_parenthesis'] = $map;
822
-                }
823
-            } else {
824
-                if (empty($map) === false) {
825
-                    $this->tokens[$i]['nested_parenthesis'] = $map;
826
-                }
827
-            }//end if
828
-        }//end for
829
-
830
-    }//end createParenthesisNestingMap()
831
-
832
-
833
-    /**
834
-     * Creates a scope map of tokens that open scopes.
835
-     *
836
-     * @return void
837
-     * @see    recurseScopeMap()
838
-     */
839
-    private function createScopeMap()
840
-    {
841
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
842
-            echo "\t*** START SCOPE MAP ***".PHP_EOL;
843
-        }
844
-
845
-        for ($i = 0; $i < $this->numTokens; $i++) {
846
-            // Check to see if the current token starts a new scope.
847
-            if (isset($this->scopeOpeners[$this->tokens[$i]['code']]) === true) {
848
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
849
-                    $type    = $this->tokens[$i]['type'];
850
-                    $content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
851
-                    echo "\tStart scope map at $i:$type => $content".PHP_EOL;
852
-                }
853
-
854
-                if (isset($this->tokens[$i]['scope_condition']) === true) {
855
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
856
-                        echo "\t* already processed, skipping *".PHP_EOL;
857
-                    }
858
-
859
-                    continue;
860
-                }
861
-
862
-                $i = $this->recurseScopeMap($i);
863
-            }//end if
864
-        }//end for
865
-
866
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
867
-            echo "\t*** END SCOPE MAP ***".PHP_EOL;
868
-        }
869
-
870
-    }//end createScopeMap()
871
-
872
-
873
-    /**
874
-     * Recurses though the scope openers to build a scope map.
875
-     *
876
-     * @param int $stackPtr The position in the stack of the token that
877
-     *                      opened the scope (eg. an IF token or FOR token).
878
-     * @param int $depth    How many scope levels down we are.
879
-     * @param int $ignore   How many curly braces we are ignoring.
880
-     *
881
-     * @return int The position in the stack that closed the scope.
882
-     */
883
-    private function recurseScopeMap($stackPtr, $depth=1, &$ignore=0)
884
-    {
885
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
886
-            echo str_repeat("\t", $depth);
887
-            echo "=> Begin scope map recursion at token $stackPtr with depth $depth".PHP_EOL;
888
-        }
889
-
890
-        $opener    = null;
891
-        $currType  = $this->tokens[$stackPtr]['code'];
892
-        $startLine = $this->tokens[$stackPtr]['line'];
893
-
894
-        // We will need this to restore the value if we end up
895
-        // returning a token ID that causes our calling function to go back
896
-        // over already ignored braces.
897
-        $originalIgnore = $ignore;
898
-
899
-        // If the start token for this scope opener is the same as
900
-        // the scope token, we have already found our opener.
901
-        if (isset($this->scopeOpeners[$currType]['start'][$currType]) === true) {
902
-            $opener = $stackPtr;
903
-        }
904
-
905
-        for ($i = ($stackPtr + 1); $i < $this->numTokens; $i++) {
906
-            $tokenType = $this->tokens[$i]['code'];
907
-
908
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
909
-                $type    = $this->tokens[$i]['type'];
910
-                $line    = $this->tokens[$i]['line'];
911
-                $content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
912
-
913
-                echo str_repeat("\t", $depth);
914
-                echo "Process token $i on line $line [";
915
-                if ($opener !== null) {
916
-                    echo "opener:$opener;";
917
-                }
918
-
919
-                if ($ignore > 0) {
920
-                    echo "ignore=$ignore;";
921
-                }
922
-
923
-                echo "]: $type => $content".PHP_EOL;
924
-            }//end if
925
-
926
-            // Very special case for IF statements in PHP that can be defined without
927
-            // scope tokens. E.g., if (1) 1; 1 ? (1 ? 1 : 1) : 1;
928
-            // If an IF statement below this one has an opener but no
929
-            // keyword, the opener will be incorrectly assigned to this IF statement.
930
-            // The same case also applies to USE statements, which don't have to have
931
-            // openers, so a following USE statement can cause an incorrect brace match.
932
-            if (($currType === T_IF || $currType === T_ELSE || $currType === T_USE)
933
-                && $opener === null
934
-                && ($this->tokens[$i]['code'] === T_SEMICOLON
935
-                || $this->tokens[$i]['code'] === T_CLOSE_TAG)
936
-            ) {
937
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
938
-                    $type = $this->tokens[$stackPtr]['type'];
939
-                    echo str_repeat("\t", $depth);
940
-                    if ($this->tokens[$i]['code'] === T_SEMICOLON) {
941
-                        $closerType = 'semicolon';
942
-                    } else {
943
-                        $closerType = 'close tag';
944
-                    }
945
-
946
-                    echo "=> Found $closerType before scope opener for $stackPtr:$type, bailing".PHP_EOL;
947
-                }
948
-
949
-                return $i;
950
-            }
951
-
952
-            // Special case for PHP control structures that have no braces.
953
-            // If we find a curly brace closer before we find the opener,
954
-            // we're not going to find an opener. That closer probably belongs to
955
-            // a control structure higher up.
956
-            if ($opener === null
957
-                && $ignore === 0
958
-                && $tokenType === T_CLOSE_CURLY_BRACKET
959
-                && isset($this->scopeOpeners[$currType]['end'][$tokenType]) === true
960
-            ) {
961
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
962
-                    $type = $this->tokens[$stackPtr]['type'];
963
-                    echo str_repeat("\t", $depth);
964
-                    echo "=> Found curly brace closer before scope opener for $stackPtr:$type, bailing".PHP_EOL;
965
-                }
966
-
967
-                return ($i - 1);
968
-            }
969
-
970
-            if ($opener !== null
971
-                && (isset($this->tokens[$i]['scope_opener']) === false
972
-                || $this->scopeOpeners[$this->tokens[$stackPtr]['code']]['shared'] === true)
973
-                && isset($this->scopeOpeners[$currType]['end'][$tokenType]) === true
974
-            ) {
975
-                if ($ignore > 0 && $tokenType === T_CLOSE_CURLY_BRACKET) {
976
-                    // The last opening bracket must have been for a string
977
-                    // offset or alike, so let's ignore it.
978
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
979
-                        echo str_repeat("\t", $depth);
980
-                        echo '* finished ignoring curly brace *'.PHP_EOL;
981
-                    }
982
-
983
-                    $ignore--;
984
-                    continue;
985
-                } else if ($this->tokens[$opener]['code'] === T_OPEN_CURLY_BRACKET
986
-                    && $tokenType !== T_CLOSE_CURLY_BRACKET
987
-                ) {
988
-                    // The opener is a curly bracket so the closer must be a curly bracket as well.
989
-                    // We ignore this closer to handle cases such as T_ELSE or T_ELSEIF being considered
990
-                    // a closer of T_IF when it should not.
991
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
992
-                        $type = $this->tokens[$stackPtr]['type'];
993
-                        echo str_repeat("\t", $depth);
994
-                        echo "=> Ignoring non-curly scope closer for $stackPtr:$type".PHP_EOL;
995
-                    }
996
-                } else {
997
-                    $scopeCloser = $i;
998
-                    $todo        = [
999
-                        $stackPtr,
1000
-                        $opener,
1001
-                    ];
1002
-
1003
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1004
-                        $type       = $this->tokens[$stackPtr]['type'];
1005
-                        $closerType = $this->tokens[$scopeCloser]['type'];
1006
-                        echo str_repeat("\t", $depth);
1007
-                        echo "=> Found scope closer ($scopeCloser:$closerType) for $stackPtr:$type".PHP_EOL;
1008
-                    }
1009
-
1010
-                    $validCloser = true;
1011
-                    if (($this->tokens[$stackPtr]['code'] === T_IF || $this->tokens[$stackPtr]['code'] === T_ELSEIF)
1012
-                        && ($tokenType === T_ELSE || $tokenType === T_ELSEIF)
1013
-                    ) {
1014
-                        // To be a closer, this token must have an opener.
1015
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1016
-                            echo str_repeat("\t", $depth);
1017
-                            echo "* closer needs to be tested *".PHP_EOL;
1018
-                        }
1019
-
1020
-                        $i = self::recurseScopeMap($i, ($depth + 1), $ignore);
1021
-
1022
-                        if (isset($this->tokens[$scopeCloser]['scope_opener']) === false) {
1023
-                            $validCloser = false;
1024
-                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1025
-                                echo str_repeat("\t", $depth);
1026
-                                echo "* closer is not valid (no opener found) *".PHP_EOL;
1027
-                            }
1028
-                        } else if ($this->tokens[$this->tokens[$scopeCloser]['scope_opener']]['code'] !== $this->tokens[$opener]['code']) {
1029
-                            $validCloser = false;
1030
-                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1031
-                                echo str_repeat("\t", $depth);
1032
-                                $type       = $this->tokens[$this->tokens[$scopeCloser]['scope_opener']]['type'];
1033
-                                $openerType = $this->tokens[$opener]['type'];
1034
-                                echo "* closer is not valid (mismatched opener type; $type != $openerType) *".PHP_EOL;
1035
-                            }
1036
-                        } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
1037
-                            echo str_repeat("\t", $depth);
1038
-                            echo "* closer was valid *".PHP_EOL;
1039
-                        }
1040
-                    } else {
1041
-                        // The closer was not processed, so we need to
1042
-                        // complete that token as well.
1043
-                        $todo[] = $scopeCloser;
1044
-                    }//end if
1045
-
1046
-                    if ($validCloser === true) {
1047
-                        foreach ($todo as $token) {
1048
-                            $this->tokens[$token]['scope_condition'] = $stackPtr;
1049
-                            $this->tokens[$token]['scope_opener']    = $opener;
1050
-                            $this->tokens[$token]['scope_closer']    = $scopeCloser;
1051
-                        }
1052
-
1053
-                        if ($this->scopeOpeners[$this->tokens[$stackPtr]['code']]['shared'] === true) {
1054
-                            // As we are going back to where we started originally, restore
1055
-                            // the ignore value back to its original value.
1056
-                            $ignore = $originalIgnore;
1057
-                            return $opener;
1058
-                        } else if ($scopeCloser === $i
1059
-                            && isset($this->scopeOpeners[$tokenType]) === true
1060
-                        ) {
1061
-                            // Unset scope_condition here or else the token will appear to have
1062
-                            // already been processed, and it will be skipped. Normally we want that,
1063
-                            // but in this case, the token is both a closer and an opener, so
1064
-                            // it needs to act like an opener. This is also why we return the
1065
-                            // token before this one; so the closer has a chance to be processed
1066
-                            // a second time, but as an opener.
1067
-                            unset($this->tokens[$scopeCloser]['scope_condition']);
1068
-                            return ($i - 1);
1069
-                        } else {
1070
-                            return $i;
1071
-                        }
1072
-                    } else {
1073
-                        continue;
1074
-                    }//end if
1075
-                }//end if
1076
-            }//end if
1077
-
1078
-            // Is this an opening condition ?
1079
-            if (isset($this->scopeOpeners[$tokenType]) === true) {
1080
-                if ($opener === null) {
1081
-                    if ($tokenType === T_USE) {
1082
-                        // PHP use keywords are special because they can be
1083
-                        // used as blocks but also inline in function definitions.
1084
-                        // So if we find them nested inside another opener, just skip them.
1085
-                        continue;
1086
-                    }
1087
-
1088
-                    if ($tokenType === T_FUNCTION
1089
-                        && $this->tokens[$stackPtr]['code'] !== T_FUNCTION
1090
-                    ) {
1091
-                        // Probably a closure, so process it manually.
1092
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1093
-                            $type = $this->tokens[$stackPtr]['type'];
1094
-                            echo str_repeat("\t", $depth);
1095
-                            echo "=> Found function before scope opener for $stackPtr:$type, processing manually".PHP_EOL;
1096
-                        }
1097
-
1098
-                        if (isset($this->tokens[$i]['scope_closer']) === true) {
1099
-                            // We've already processed this closure.
1100
-                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1101
-                                echo str_repeat("\t", $depth);
1102
-                                echo '* already processed, skipping *'.PHP_EOL;
1103
-                            }
1104
-
1105
-                            $i = $this->tokens[$i]['scope_closer'];
1106
-                            continue;
1107
-                        }
1108
-
1109
-                        $i = self::recurseScopeMap($i, ($depth + 1), $ignore);
1110
-                        continue;
1111
-                    }//end if
1112
-
1113
-                    if ($tokenType === T_CLASS) {
1114
-                        // Probably an anonymous class inside another anonymous class,
1115
-                        // so process it manually.
1116
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1117
-                            $type = $this->tokens[$stackPtr]['type'];
1118
-                            echo str_repeat("\t", $depth);
1119
-                            echo "=> Found class before scope opener for $stackPtr:$type, processing manually".PHP_EOL;
1120
-                        }
1121
-
1122
-                        if (isset($this->tokens[$i]['scope_closer']) === true) {
1123
-                            // We've already processed this anon class.
1124
-                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1125
-                                echo str_repeat("\t", $depth);
1126
-                                echo '* already processed, skipping *'.PHP_EOL;
1127
-                            }
1128
-
1129
-                            $i = $this->tokens[$i]['scope_closer'];
1130
-                            continue;
1131
-                        }
1132
-
1133
-                        $i = self::recurseScopeMap($i, ($depth + 1), $ignore);
1134
-                        continue;
1135
-                    }//end if
1136
-
1137
-                    // Found another opening condition but still haven't
1138
-                    // found our opener, so we are never going to find one.
1139
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1140
-                        $type = $this->tokens[$stackPtr]['type'];
1141
-                        echo str_repeat("\t", $depth);
1142
-                        echo "=> Found new opening condition before scope opener for $stackPtr:$type, ";
1143
-                    }
1144
-
1145
-                    if (($this->tokens[$stackPtr]['code'] === T_IF
1146
-                        || $this->tokens[$stackPtr]['code'] === T_ELSEIF
1147
-                        || $this->tokens[$stackPtr]['code'] === T_ELSE)
1148
-                        && ($this->tokens[$i]['code'] === T_ELSE
1149
-                        || $this->tokens[$i]['code'] === T_ELSEIF)
1150
-                    ) {
1151
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1152
-                            echo "continuing".PHP_EOL;
1153
-                        }
1154
-
1155
-                        return ($i - 1);
1156
-                    } else {
1157
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1158
-                            echo "backtracking".PHP_EOL;
1159
-                        }
1160
-
1161
-                        return $stackPtr;
1162
-                    }
1163
-                }//end if
1164
-
1165
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1166
-                    echo str_repeat("\t", $depth);
1167
-                    echo '* token is an opening condition *'.PHP_EOL;
1168
-                }
1169
-
1170
-                $isShared = ($this->scopeOpeners[$tokenType]['shared'] === true);
1171
-
1172
-                if (isset($this->tokens[$i]['scope_condition']) === true) {
1173
-                    // We've been here before.
1174
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1175
-                        echo str_repeat("\t", $depth);
1176
-                        echo '* already processed, skipping *'.PHP_EOL;
1177
-                    }
1178
-
1179
-                    if ($isShared === false
1180
-                        && isset($this->tokens[$i]['scope_closer']) === true
1181
-                    ) {
1182
-                        $i = $this->tokens[$i]['scope_closer'];
1183
-                    }
1184
-
1185
-                    continue;
1186
-                } else if ($currType === $tokenType
1187
-                    && $isShared === false
1188
-                    && $opener === null
1189
-                ) {
1190
-                    // We haven't yet found our opener, but we have found another
1191
-                    // scope opener which is the same type as us, and we don't
1192
-                    // share openers, so we will never find one.
1193
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1194
-                        echo str_repeat("\t", $depth);
1195
-                        echo '* it was another token\'s opener, bailing *'.PHP_EOL;
1196
-                    }
1197
-
1198
-                    return $stackPtr;
1199
-                } else {
1200
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1201
-                        echo str_repeat("\t", $depth);
1202
-                        echo '* searching for opener *'.PHP_EOL;
1203
-                    }
1204
-
1205
-                    if (isset($this->scopeOpeners[$tokenType]['end'][T_CLOSE_CURLY_BRACKET]) === true) {
1206
-                        $oldIgnore = $ignore;
1207
-                        $ignore    = 0;
1208
-                    }
1209
-
1210
-                    // PHP has a max nesting level for functions. Stop before we hit that limit
1211
-                    // because too many loops means we've run into trouble anyway.
1212
-                    if ($depth > 50) {
1213
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1214
-                            echo str_repeat("\t", $depth);
1215
-                            echo '* reached maximum nesting level; aborting *'.PHP_EOL;
1216
-                        }
1217
-
1218
-                        throw new RuntimeException('Maximum nesting level reached; file could not be processed');
1219
-                    }
1220
-
1221
-                    $oldDepth = $depth;
1222
-                    if ($isShared === true
1223
-                        && isset($this->scopeOpeners[$tokenType]['with'][$currType]) === true
1224
-                    ) {
1225
-                        // Don't allow the depth to increment because this is
1226
-                        // possibly not a true nesting if we are sharing our closer.
1227
-                        // This can happen, for example, when a SWITCH has a large
1228
-                        // number of CASE statements with the same shared BREAK.
1229
-                        $depth--;
1230
-                    }
1231
-
1232
-                    $i     = self::recurseScopeMap($i, ($depth + 1), $ignore);
1233
-                    $depth = $oldDepth;
1234
-
1235
-                    if (isset($this->scopeOpeners[$tokenType]['end'][T_CLOSE_CURLY_BRACKET]) === true) {
1236
-                        $ignore = $oldIgnore;
1237
-                    }
1238
-                }//end if
1239
-            }//end if
1240
-
1241
-            if (isset($this->scopeOpeners[$currType]['start'][$tokenType]) === true
1242
-                && $opener === null
1243
-            ) {
1244
-                if ($tokenType === T_OPEN_CURLY_BRACKET) {
1245
-                    if (isset($this->tokens[$stackPtr]['parenthesis_closer']) === true
1246
-                        && $i < $this->tokens[$stackPtr]['parenthesis_closer']
1247
-                    ) {
1248
-                        // We found a curly brace inside the condition of the
1249
-                        // current scope opener, so it must be a string offset.
1250
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1251
-                            echo str_repeat("\t", $depth);
1252
-                            echo '* ignoring curly brace inside condition *'.PHP_EOL;
1253
-                        }
1254
-
1255
-                        $ignore++;
1256
-                    } else {
1257
-                        // Make sure this is actually an opener and not a
1258
-                        // string offset (e.g., $var{0}).
1259
-                        for ($x = ($i - 1); $x > 0; $x--) {
1260
-                            if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === true) {
1261
-                                continue;
1262
-                            } else {
1263
-                                // If the first non-whitespace/comment token looks like this
1264
-                                // brace is a string offset, or this brace is mid-way through
1265
-                                // a new statement, it isn't a scope opener.
1266
-                                $disallowed  = Util\Tokens::$assignmentTokens;
1267
-                                $disallowed += [
1268
-                                    T_DOLLAR           => true,
1269
-                                    T_VARIABLE         => true,
1270
-                                    T_OBJECT_OPERATOR  => true,
1271
-                                    T_COMMA            => true,
1272
-                                    T_OPEN_PARENTHESIS => true,
1273
-                                ];
1274
-
1275
-                                if (isset($disallowed[$this->tokens[$x]['code']]) === true) {
1276
-                                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1277
-                                        echo str_repeat("\t", $depth);
1278
-                                        echo '* ignoring curly brace *'.PHP_EOL;
1279
-                                    }
1280
-
1281
-                                    $ignore++;
1282
-                                }
1283
-
1284
-                                break;
1285
-                            }//end if
1286
-                        }//end for
1287
-                    }//end if
1288
-                }//end if
1289
-
1290
-                if ($ignore === 0 || $tokenType !== T_OPEN_CURLY_BRACKET) {
1291
-                    // We found the opening scope token for $currType.
1292
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1293
-                        $type = $this->tokens[$stackPtr]['type'];
1294
-                        echo str_repeat("\t", $depth);
1295
-                        echo "=> Found scope opener for $stackPtr:$type".PHP_EOL;
1296
-                    }
1297
-
1298
-                    $opener = $i;
1299
-                }
1300
-            } else if ($tokenType === T_OPEN_PARENTHESIS) {
1301
-                if (isset($this->tokens[$i]['parenthesis_owner']) === true) {
1302
-                    $owner = $this->tokens[$i]['parenthesis_owner'];
1303
-                    if (isset(Util\Tokens::$scopeOpeners[$this->tokens[$owner]['code']]) === true
1304
-                        && isset($this->tokens[$i]['parenthesis_closer']) === true
1305
-                    ) {
1306
-                        // If we get into here, then we opened a parenthesis for
1307
-                        // a scope (eg. an if or else if) so we need to update the
1308
-                        // start of the line so that when we check to see
1309
-                        // if the closing parenthesis is more than 3 lines away from
1310
-                        // the statement, we check from the closing parenthesis.
1311
-                        $startLine = $this->tokens[$this->tokens[$i]['parenthesis_closer']]['line'];
1312
-                    }
1313
-                }
1314
-            } else if ($tokenType === T_OPEN_CURLY_BRACKET && $opener !== null) {
1315
-                // We opened something that we don't have a scope opener for.
1316
-                // Examples of this are curly brackets for string offsets etc.
1317
-                // We want to ignore this so that we don't have an invalid scope
1318
-                // map.
1319
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1320
-                    echo str_repeat("\t", $depth);
1321
-                    echo '* ignoring curly brace *'.PHP_EOL;
1322
-                }
1323
-
1324
-                $ignore++;
1325
-            } else if ($tokenType === T_CLOSE_CURLY_BRACKET && $ignore > 0) {
1326
-                // We found the end token for the opener we were ignoring.
1327
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1328
-                    echo str_repeat("\t", $depth);
1329
-                    echo '* finished ignoring curly brace *'.PHP_EOL;
1330
-                }
1331
-
1332
-                $ignore--;
1333
-            } else if ($opener === null
1334
-                && isset($this->scopeOpeners[$currType]) === true
1335
-            ) {
1336
-                // If we still haven't found the opener after 30 lines,
1337
-                // we're not going to find it, unless we know it requires
1338
-                // an opener (in which case we better keep looking) or the last
1339
-                // token was empty (in which case we'll just confirm there is
1340
-                // more code in this file and not just a big comment).
1341
-                if ($this->tokens[$i]['line'] >= ($startLine + 30)
1342
-                    && isset(Util\Tokens::$emptyTokens[$this->tokens[($i - 1)]['code']]) === false
1343
-                ) {
1344
-                    if ($this->scopeOpeners[$currType]['strict'] === true) {
1345
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1346
-                            $type  = $this->tokens[$stackPtr]['type'];
1347
-                            $lines = ($this->tokens[$i]['line'] - $startLine);
1348
-                            echo str_repeat("\t", $depth);
1349
-                            echo "=> Still looking for $stackPtr:$type scope opener after $lines lines".PHP_EOL;
1350
-                        }
1351
-                    } else {
1352
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1353
-                            $type = $this->tokens[$stackPtr]['type'];
1354
-                            echo str_repeat("\t", $depth);
1355
-                            echo "=> Couldn't find scope opener for $stackPtr:$type, bailing".PHP_EOL;
1356
-                        }
1357
-
1358
-                        return $stackPtr;
1359
-                    }
1360
-                }
1361
-            } else if ($opener !== null
1362
-                && $tokenType !== T_BREAK
1363
-                && isset($this->endScopeTokens[$tokenType]) === true
1364
-            ) {
1365
-                if (isset($this->tokens[$i]['scope_condition']) === false) {
1366
-                    if ($ignore > 0) {
1367
-                        // We found the end token for the opener we were ignoring.
1368
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1369
-                            echo str_repeat("\t", $depth);
1370
-                            echo '* finished ignoring curly brace *'.PHP_EOL;
1371
-                        }
1372
-
1373
-                        $ignore--;
1374
-                    } else {
1375
-                        // We found a token that closes the scope but it doesn't
1376
-                        // have a condition, so it belongs to another token and
1377
-                        // our token doesn't have a closer, so pretend this is
1378
-                        // the closer.
1379
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1380
-                            $type = $this->tokens[$stackPtr]['type'];
1381
-                            echo str_repeat("\t", $depth);
1382
-                            echo "=> Found (unexpected) scope closer for $stackPtr:$type".PHP_EOL;
1383
-                        }
1384
-
1385
-                        foreach ([$stackPtr, $opener] as $token) {
1386
-                            $this->tokens[$token]['scope_condition'] = $stackPtr;
1387
-                            $this->tokens[$token]['scope_opener']    = $opener;
1388
-                            $this->tokens[$token]['scope_closer']    = $i;
1389
-                        }
1390
-
1391
-                        return ($i - 1);
1392
-                    }//end if
1393
-                }//end if
1394
-            }//end if
1395
-        }//end for
1396
-
1397
-        return $stackPtr;
1398
-
1399
-    }//end recurseScopeMap()
1400
-
1401
-
1402
-    /**
1403
-     * Constructs the level map.
1404
-     *
1405
-     * The level map adds a 'level' index to each token which indicates the
1406
-     * depth that a token within a set of scope blocks. It also adds a
1407
-     * 'conditions' index which is an array of the scope conditions that opened
1408
-     * each of the scopes - position 0 being the first scope opener.
1409
-     *
1410
-     * @return void
1411
-     */
1412
-    private function createLevelMap()
1413
-    {
1414
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1415
-            echo "\t*** START LEVEL MAP ***".PHP_EOL;
1416
-        }
1417
-
1418
-        $this->numTokens = count($this->tokens);
1419
-        $level           = 0;
1420
-        $conditions      = [];
1421
-        $lastOpener      = null;
1422
-        $openers         = [];
1423
-
1424
-        for ($i = 0; $i < $this->numTokens; $i++) {
1425
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1426
-                $type = $this->tokens[$i]['type'];
1427
-                $line = $this->tokens[$i]['line'];
1428
-                $len  = $this->tokens[$i]['length'];
1429
-                $col  = $this->tokens[$i]['column'];
1430
-
1431
-                $content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
1432
-
1433
-                echo str_repeat("\t", ($level + 1));
1434
-                echo "Process token $i on line $line [col:$col;len:$len;lvl:$level;";
1435
-                if (empty($conditions) !== true) {
1436
-                    $condString = 'conds;';
1437
-                    foreach ($conditions as $condition) {
1438
-                        $condString .= Util\Tokens::tokenName($condition).',';
1439
-                    }
1440
-
1441
-                    echo rtrim($condString, ',').';';
1442
-                }
1443
-
1444
-                echo "]: $type => $content".PHP_EOL;
1445
-            }//end if
1446
-
1447
-            $this->tokens[$i]['level']      = $level;
1448
-            $this->tokens[$i]['conditions'] = $conditions;
1449
-
1450
-            if (isset($this->tokens[$i]['scope_condition']) === true) {
1451
-                // Check to see if this token opened the scope.
1452
-                if ($this->tokens[$i]['scope_opener'] === $i) {
1453
-                    $stackPtr = $this->tokens[$i]['scope_condition'];
1454
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1455
-                        $type = $this->tokens[$stackPtr]['type'];
1456
-                        echo str_repeat("\t", ($level + 1));
1457
-                        echo "=> Found scope opener for $stackPtr:$type".PHP_EOL;
1458
-                    }
1459
-
1460
-                    $stackPtr = $this->tokens[$i]['scope_condition'];
1461
-
1462
-                    // If we find a scope opener that has a shared closer,
1463
-                    // then we need to go back over the condition map that we
1464
-                    // just created and fix ourselves as we just added some
1465
-                    // conditions where there was none. This happens for T_CASE
1466
-                    // statements that are using the same break statement.
1467
-                    if ($lastOpener !== null && $this->tokens[$lastOpener]['scope_closer'] === $this->tokens[$i]['scope_closer']) {
1468
-                        // This opener shares its closer with the previous opener,
1469
-                        // but we still need to check if the two openers share their
1470
-                        // closer with each other directly (like CASE and DEFAULT)
1471
-                        // or if they are just sharing because one doesn't have a
1472
-                        // closer (like CASE with no BREAK using a SWITCHes closer).
1473
-                        $thisType = $this->tokens[$this->tokens[$i]['scope_condition']]['code'];
1474
-                        $opener   = $this->tokens[$lastOpener]['scope_condition'];
1475
-
1476
-                        $isShared = isset($this->scopeOpeners[$thisType]['with'][$this->tokens[$opener]['code']]);
1477
-
1478
-                        reset($this->scopeOpeners[$thisType]['end']);
1479
-                        reset($this->scopeOpeners[$this->tokens[$opener]['code']]['end']);
1480
-                        $sameEnd = (current($this->scopeOpeners[$thisType]['end']) === current($this->scopeOpeners[$this->tokens[$opener]['code']]['end']));
1481
-
1482
-                        if ($isShared === true && $sameEnd === true) {
1483
-                            $badToken = $opener;
1484
-                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1485
-                                $type = $this->tokens[$badToken]['type'];
1486
-                                echo str_repeat("\t", ($level + 1));
1487
-                                echo "* shared closer, cleaning up $badToken:$type *".PHP_EOL;
1488
-                            }
1489
-
1490
-                            for ($x = $this->tokens[$i]['scope_condition']; $x <= $i; $x++) {
1491
-                                $oldConditions = $this->tokens[$x]['conditions'];
1492
-                                $oldLevel      = $this->tokens[$x]['level'];
1493
-                                $this->tokens[$x]['level']--;
1494
-                                unset($this->tokens[$x]['conditions'][$badToken]);
1495
-                                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1496
-                                    $type     = $this->tokens[$x]['type'];
1497
-                                    $oldConds = '';
1498
-                                    foreach ($oldConditions as $condition) {
1499
-                                        $oldConds .= Util\Tokens::tokenName($condition).',';
1500
-                                    }
1501
-
1502
-                                    $oldConds = rtrim($oldConds, ',');
1503
-
1504
-                                    $newConds = '';
1505
-                                    foreach ($this->tokens[$x]['conditions'] as $condition) {
1506
-                                        $newConds .= Util\Tokens::tokenName($condition).',';
1507
-                                    }
1508
-
1509
-                                    $newConds = rtrim($newConds, ',');
1510
-
1511
-                                    $newLevel = $this->tokens[$x]['level'];
1512
-                                    echo str_repeat("\t", ($level + 1));
1513
-                                    echo "* cleaned $x:$type *".PHP_EOL;
1514
-                                    echo str_repeat("\t", ($level + 2));
1515
-                                    echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
1516
-                                    echo str_repeat("\t", ($level + 2));
1517
-                                    echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
1518
-                                }//end if
1519
-                            }//end for
1520
-
1521
-                            unset($conditions[$badToken]);
1522
-                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1523
-                                $type = $this->tokens[$badToken]['type'];
1524
-                                echo str_repeat("\t", ($level + 1));
1525
-                                echo "* token $badToken:$type removed from conditions array *".PHP_EOL;
1526
-                            }
1527
-
1528
-                            unset($openers[$lastOpener]);
1529
-
1530
-                            $level--;
1531
-                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1532
-                                echo str_repeat("\t", ($level + 2));
1533
-                                echo '* level decreased *'.PHP_EOL;
1534
-                            }
1535
-                        }//end if
1536
-                    }//end if
1537
-
1538
-                    $level++;
1539
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1540
-                        echo str_repeat("\t", ($level + 1));
1541
-                        echo '* level increased *'.PHP_EOL;
1542
-                    }
1543
-
1544
-                    $conditions[$stackPtr] = $this->tokens[$stackPtr]['code'];
1545
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1546
-                        $type = $this->tokens[$stackPtr]['type'];
1547
-                        echo str_repeat("\t", ($level + 1));
1548
-                        echo "* token $stackPtr:$type added to conditions array *".PHP_EOL;
1549
-                    }
1550
-
1551
-                    $lastOpener = $this->tokens[$i]['scope_opener'];
1552
-                    if ($lastOpener !== null) {
1553
-                        $openers[$lastOpener] = $lastOpener;
1554
-                    }
1555
-                } else if ($lastOpener !== null && $this->tokens[$lastOpener]['scope_closer'] === $i) {
1556
-                    foreach (array_reverse($openers) as $opener) {
1557
-                        if ($this->tokens[$opener]['scope_closer'] === $i) {
1558
-                            $oldOpener = array_pop($openers);
1559
-                            if (empty($openers) === false) {
1560
-                                $lastOpener           = array_pop($openers);
1561
-                                $openers[$lastOpener] = $lastOpener;
1562
-                            } else {
1563
-                                $lastOpener = null;
1564
-                            }
1565
-
1566
-                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1567
-                                $type = $this->tokens[$oldOpener]['type'];
1568
-                                echo str_repeat("\t", ($level + 1));
1569
-                                echo "=> Found scope closer for $oldOpener:$type".PHP_EOL;
1570
-                            }
1571
-
1572
-                            $oldCondition = array_pop($conditions);
1573
-                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1574
-                                echo str_repeat("\t", ($level + 1));
1575
-                                echo '* token '.Util\Tokens::tokenName($oldCondition).' removed from conditions array *'.PHP_EOL;
1576
-                            }
1577
-
1578
-                            // Make sure this closer actually belongs to us.
1579
-                            // Either the condition also has to think this is the
1580
-                            // closer, or it has to allow sharing with us.
1581
-                            $condition = $this->tokens[$this->tokens[$i]['scope_condition']]['code'];
1582
-                            if ($condition !== $oldCondition) {
1583
-                                if (isset($this->scopeOpeners[$oldCondition]['with'][$condition]) === false) {
1584
-                                    $badToken = $this->tokens[$oldOpener]['scope_condition'];
1585
-
1586
-                                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1587
-                                        $type = Util\Tokens::tokenName($oldCondition);
1588
-                                        echo str_repeat("\t", ($level + 1));
1589
-                                        echo "* scope closer was bad, cleaning up $badToken:$type *".PHP_EOL;
1590
-                                    }
1591
-
1592
-                                    for ($x = ($oldOpener + 1); $x <= $i; $x++) {
1593
-                                        $oldConditions = $this->tokens[$x]['conditions'];
1594
-                                        $oldLevel      = $this->tokens[$x]['level'];
1595
-                                        $this->tokens[$x]['level']--;
1596
-                                        unset($this->tokens[$x]['conditions'][$badToken]);
1597
-                                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1598
-                                            $type     = $this->tokens[$x]['type'];
1599
-                                            $oldConds = '';
1600
-                                            foreach ($oldConditions as $condition) {
1601
-                                                $oldConds .= Util\Tokens::tokenName($condition).',';
1602
-                                            }
1603
-
1604
-                                            $oldConds = rtrim($oldConds, ',');
1605
-
1606
-                                            $newConds = '';
1607
-                                            foreach ($this->tokens[$x]['conditions'] as $condition) {
1608
-                                                $newConds .= Util\Tokens::tokenName($condition).',';
1609
-                                            }
1610
-
1611
-                                            $newConds = rtrim($newConds, ',');
1612
-
1613
-                                            $newLevel = $this->tokens[$x]['level'];
1614
-                                            echo str_repeat("\t", ($level + 1));
1615
-                                            echo "* cleaned $x:$type *".PHP_EOL;
1616
-                                            echo str_repeat("\t", ($level + 2));
1617
-                                            echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
1618
-                                            echo str_repeat("\t", ($level + 2));
1619
-                                            echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
1620
-                                        }//end if
1621
-                                    }//end for
1622
-                                }//end if
1623
-                            }//end if
1624
-
1625
-                            $level--;
1626
-                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1627
-                                echo str_repeat("\t", ($level + 2));
1628
-                                echo '* level decreased *'.PHP_EOL;
1629
-                            }
1630
-
1631
-                            $this->tokens[$i]['level']      = $level;
1632
-                            $this->tokens[$i]['conditions'] = $conditions;
1633
-                        }//end if
1634
-                    }//end foreach
1635
-                }//end if
1636
-            }//end if
1637
-        }//end for
1638
-
1639
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1640
-            echo "\t*** END LEVEL MAP ***".PHP_EOL;
1641
-        }
1642
-
1643
-    }//end createLevelMap()
724
+			switch ($this->tokens[$i]['code']) {
725
+			case T_OPEN_SQUARE_BRACKET:
726
+				$squareOpeners[] = $i;
727
+
728
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
729
+					echo str_repeat("\t", count($squareOpeners));
730
+					echo str_repeat("\t", count($curlyOpeners));
731
+					echo "=> Found square bracket opener at $i".PHP_EOL;
732
+				}
733
+				break;
734
+			case T_OPEN_CURLY_BRACKET:
735
+				if (isset($this->tokens[$i]['scope_closer']) === false) {
736
+					$curlyOpeners[] = $i;
737
+
738
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
739
+						echo str_repeat("\t", count($squareOpeners));
740
+						echo str_repeat("\t", count($curlyOpeners));
741
+						echo "=> Found curly bracket opener at $i".PHP_EOL;
742
+					}
743
+				}
744
+				break;
745
+			case T_CLOSE_SQUARE_BRACKET:
746
+				if (empty($squareOpeners) === false) {
747
+					$opener = array_pop($squareOpeners);
748
+					$this->tokens[$i]['bracket_opener']      = $opener;
749
+					$this->tokens[$i]['bracket_closer']      = $i;
750
+					$this->tokens[$opener]['bracket_opener'] = $opener;
751
+					$this->tokens[$opener]['bracket_closer'] = $i;
752
+
753
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
754
+						echo str_repeat("\t", count($squareOpeners));
755
+						echo str_repeat("\t", count($curlyOpeners));
756
+						echo "\t=> Found square bracket closer at $i for $opener".PHP_EOL;
757
+					}
758
+				}
759
+				break;
760
+			case T_CLOSE_CURLY_BRACKET:
761
+				if (empty($curlyOpeners) === false
762
+					&& isset($this->tokens[$i]['scope_opener']) === false
763
+				) {
764
+					$opener = array_pop($curlyOpeners);
765
+					$this->tokens[$i]['bracket_opener']      = $opener;
766
+					$this->tokens[$i]['bracket_closer']      = $i;
767
+					$this->tokens[$opener]['bracket_opener'] = $opener;
768
+					$this->tokens[$opener]['bracket_closer'] = $i;
769
+
770
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
771
+						echo str_repeat("\t", count($squareOpeners));
772
+						echo str_repeat("\t", count($curlyOpeners));
773
+						echo "\t=> Found curly bracket closer at $i for $opener".PHP_EOL;
774
+					}
775
+				}
776
+				break;
777
+			default:
778
+				continue 2;
779
+			}//end switch
780
+		}//end for
781
+
782
+		// Cleanup for any openers that we didn't find closers for.
783
+		// This typically means there was a syntax error breaking things.
784
+		foreach ($openers as $opener) {
785
+			unset($this->tokens[$opener]['parenthesis_opener']);
786
+			unset($this->tokens[$opener]['parenthesis_owner']);
787
+		}
788
+
789
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
790
+			echo "\t*** END TOKEN MAP ***".PHP_EOL;
791
+		}
792
+
793
+	}//end createTokenMap()
794
+
795
+
796
+	/**
797
+	 * Creates a map for the parenthesis tokens that surround other tokens.
798
+	 *
799
+	 * @return void
800
+	 */
801
+	private function createParenthesisNestingMap()
802
+	{
803
+		$map = [];
804
+		for ($i = 0; $i < $this->numTokens; $i++) {
805
+			if (isset($this->tokens[$i]['parenthesis_opener']) === true
806
+				&& $i === $this->tokens[$i]['parenthesis_opener']
807
+			) {
808
+				if (empty($map) === false) {
809
+					$this->tokens[$i]['nested_parenthesis'] = $map;
810
+				}
811
+
812
+				if (isset($this->tokens[$i]['parenthesis_closer']) === true) {
813
+					$map[$this->tokens[$i]['parenthesis_opener']]
814
+						= $this->tokens[$i]['parenthesis_closer'];
815
+				}
816
+			} else if (isset($this->tokens[$i]['parenthesis_closer']) === true
817
+				&& $i === $this->tokens[$i]['parenthesis_closer']
818
+			) {
819
+				array_pop($map);
820
+				if (empty($map) === false) {
821
+					$this->tokens[$i]['nested_parenthesis'] = $map;
822
+				}
823
+			} else {
824
+				if (empty($map) === false) {
825
+					$this->tokens[$i]['nested_parenthesis'] = $map;
826
+				}
827
+			}//end if
828
+		}//end for
829
+
830
+	}//end createParenthesisNestingMap()
831
+
832
+
833
+	/**
834
+	 * Creates a scope map of tokens that open scopes.
835
+	 *
836
+	 * @return void
837
+	 * @see    recurseScopeMap()
838
+	 */
839
+	private function createScopeMap()
840
+	{
841
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
842
+			echo "\t*** START SCOPE MAP ***".PHP_EOL;
843
+		}
844
+
845
+		for ($i = 0; $i < $this->numTokens; $i++) {
846
+			// Check to see if the current token starts a new scope.
847
+			if (isset($this->scopeOpeners[$this->tokens[$i]['code']]) === true) {
848
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
849
+					$type    = $this->tokens[$i]['type'];
850
+					$content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
851
+					echo "\tStart scope map at $i:$type => $content".PHP_EOL;
852
+				}
853
+
854
+				if (isset($this->tokens[$i]['scope_condition']) === true) {
855
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
856
+						echo "\t* already processed, skipping *".PHP_EOL;
857
+					}
858
+
859
+					continue;
860
+				}
861
+
862
+				$i = $this->recurseScopeMap($i);
863
+			}//end if
864
+		}//end for
865
+
866
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
867
+			echo "\t*** END SCOPE MAP ***".PHP_EOL;
868
+		}
869
+
870
+	}//end createScopeMap()
871
+
872
+
873
+	/**
874
+	 * Recurses though the scope openers to build a scope map.
875
+	 *
876
+	 * @param int $stackPtr The position in the stack of the token that
877
+	 *                      opened the scope (eg. an IF token or FOR token).
878
+	 * @param int $depth    How many scope levels down we are.
879
+	 * @param int $ignore   How many curly braces we are ignoring.
880
+	 *
881
+	 * @return int The position in the stack that closed the scope.
882
+	 */
883
+	private function recurseScopeMap($stackPtr, $depth=1, &$ignore=0)
884
+	{
885
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
886
+			echo str_repeat("\t", $depth);
887
+			echo "=> Begin scope map recursion at token $stackPtr with depth $depth".PHP_EOL;
888
+		}
889
+
890
+		$opener    = null;
891
+		$currType  = $this->tokens[$stackPtr]['code'];
892
+		$startLine = $this->tokens[$stackPtr]['line'];
893
+
894
+		// We will need this to restore the value if we end up
895
+		// returning a token ID that causes our calling function to go back
896
+		// over already ignored braces.
897
+		$originalIgnore = $ignore;
898
+
899
+		// If the start token for this scope opener is the same as
900
+		// the scope token, we have already found our opener.
901
+		if (isset($this->scopeOpeners[$currType]['start'][$currType]) === true) {
902
+			$opener = $stackPtr;
903
+		}
904
+
905
+		for ($i = ($stackPtr + 1); $i < $this->numTokens; $i++) {
906
+			$tokenType = $this->tokens[$i]['code'];
907
+
908
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
909
+				$type    = $this->tokens[$i]['type'];
910
+				$line    = $this->tokens[$i]['line'];
911
+				$content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
912
+
913
+				echo str_repeat("\t", $depth);
914
+				echo "Process token $i on line $line [";
915
+				if ($opener !== null) {
916
+					echo "opener:$opener;";
917
+				}
918
+
919
+				if ($ignore > 0) {
920
+					echo "ignore=$ignore;";
921
+				}
922
+
923
+				echo "]: $type => $content".PHP_EOL;
924
+			}//end if
925
+
926
+			// Very special case for IF statements in PHP that can be defined without
927
+			// scope tokens. E.g., if (1) 1; 1 ? (1 ? 1 : 1) : 1;
928
+			// If an IF statement below this one has an opener but no
929
+			// keyword, the opener will be incorrectly assigned to this IF statement.
930
+			// The same case also applies to USE statements, which don't have to have
931
+			// openers, so a following USE statement can cause an incorrect brace match.
932
+			if (($currType === T_IF || $currType === T_ELSE || $currType === T_USE)
933
+				&& $opener === null
934
+				&& ($this->tokens[$i]['code'] === T_SEMICOLON
935
+				|| $this->tokens[$i]['code'] === T_CLOSE_TAG)
936
+			) {
937
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
938
+					$type = $this->tokens[$stackPtr]['type'];
939
+					echo str_repeat("\t", $depth);
940
+					if ($this->tokens[$i]['code'] === T_SEMICOLON) {
941
+						$closerType = 'semicolon';
942
+					} else {
943
+						$closerType = 'close tag';
944
+					}
945
+
946
+					echo "=> Found $closerType before scope opener for $stackPtr:$type, bailing".PHP_EOL;
947
+				}
948
+
949
+				return $i;
950
+			}
951
+
952
+			// Special case for PHP control structures that have no braces.
953
+			// If we find a curly brace closer before we find the opener,
954
+			// we're not going to find an opener. That closer probably belongs to
955
+			// a control structure higher up.
956
+			if ($opener === null
957
+				&& $ignore === 0
958
+				&& $tokenType === T_CLOSE_CURLY_BRACKET
959
+				&& isset($this->scopeOpeners[$currType]['end'][$tokenType]) === true
960
+			) {
961
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
962
+					$type = $this->tokens[$stackPtr]['type'];
963
+					echo str_repeat("\t", $depth);
964
+					echo "=> Found curly brace closer before scope opener for $stackPtr:$type, bailing".PHP_EOL;
965
+				}
966
+
967
+				return ($i - 1);
968
+			}
969
+
970
+			if ($opener !== null
971
+				&& (isset($this->tokens[$i]['scope_opener']) === false
972
+				|| $this->scopeOpeners[$this->tokens[$stackPtr]['code']]['shared'] === true)
973
+				&& isset($this->scopeOpeners[$currType]['end'][$tokenType]) === true
974
+			) {
975
+				if ($ignore > 0 && $tokenType === T_CLOSE_CURLY_BRACKET) {
976
+					// The last opening bracket must have been for a string
977
+					// offset or alike, so let's ignore it.
978
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
979
+						echo str_repeat("\t", $depth);
980
+						echo '* finished ignoring curly brace *'.PHP_EOL;
981
+					}
982
+
983
+					$ignore--;
984
+					continue;
985
+				} else if ($this->tokens[$opener]['code'] === T_OPEN_CURLY_BRACKET
986
+					&& $tokenType !== T_CLOSE_CURLY_BRACKET
987
+				) {
988
+					// The opener is a curly bracket so the closer must be a curly bracket as well.
989
+					// We ignore this closer to handle cases such as T_ELSE or T_ELSEIF being considered
990
+					// a closer of T_IF when it should not.
991
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
992
+						$type = $this->tokens[$stackPtr]['type'];
993
+						echo str_repeat("\t", $depth);
994
+						echo "=> Ignoring non-curly scope closer for $stackPtr:$type".PHP_EOL;
995
+					}
996
+				} else {
997
+					$scopeCloser = $i;
998
+					$todo        = [
999
+						$stackPtr,
1000
+						$opener,
1001
+					];
1002
+
1003
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1004
+						$type       = $this->tokens[$stackPtr]['type'];
1005
+						$closerType = $this->tokens[$scopeCloser]['type'];
1006
+						echo str_repeat("\t", $depth);
1007
+						echo "=> Found scope closer ($scopeCloser:$closerType) for $stackPtr:$type".PHP_EOL;
1008
+					}
1009
+
1010
+					$validCloser = true;
1011
+					if (($this->tokens[$stackPtr]['code'] === T_IF || $this->tokens[$stackPtr]['code'] === T_ELSEIF)
1012
+						&& ($tokenType === T_ELSE || $tokenType === T_ELSEIF)
1013
+					) {
1014
+						// To be a closer, this token must have an opener.
1015
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1016
+							echo str_repeat("\t", $depth);
1017
+							echo "* closer needs to be tested *".PHP_EOL;
1018
+						}
1019
+
1020
+						$i = self::recurseScopeMap($i, ($depth + 1), $ignore);
1021
+
1022
+						if (isset($this->tokens[$scopeCloser]['scope_opener']) === false) {
1023
+							$validCloser = false;
1024
+							if (PHP_CODESNIFFER_VERBOSITY > 1) {
1025
+								echo str_repeat("\t", $depth);
1026
+								echo "* closer is not valid (no opener found) *".PHP_EOL;
1027
+							}
1028
+						} else if ($this->tokens[$this->tokens[$scopeCloser]['scope_opener']]['code'] !== $this->tokens[$opener]['code']) {
1029
+							$validCloser = false;
1030
+							if (PHP_CODESNIFFER_VERBOSITY > 1) {
1031
+								echo str_repeat("\t", $depth);
1032
+								$type       = $this->tokens[$this->tokens[$scopeCloser]['scope_opener']]['type'];
1033
+								$openerType = $this->tokens[$opener]['type'];
1034
+								echo "* closer is not valid (mismatched opener type; $type != $openerType) *".PHP_EOL;
1035
+							}
1036
+						} else if (PHP_CODESNIFFER_VERBOSITY > 1) {
1037
+							echo str_repeat("\t", $depth);
1038
+							echo "* closer was valid *".PHP_EOL;
1039
+						}
1040
+					} else {
1041
+						// The closer was not processed, so we need to
1042
+						// complete that token as well.
1043
+						$todo[] = $scopeCloser;
1044
+					}//end if
1045
+
1046
+					if ($validCloser === true) {
1047
+						foreach ($todo as $token) {
1048
+							$this->tokens[$token]['scope_condition'] = $stackPtr;
1049
+							$this->tokens[$token]['scope_opener']    = $opener;
1050
+							$this->tokens[$token]['scope_closer']    = $scopeCloser;
1051
+						}
1052
+
1053
+						if ($this->scopeOpeners[$this->tokens[$stackPtr]['code']]['shared'] === true) {
1054
+							// As we are going back to where we started originally, restore
1055
+							// the ignore value back to its original value.
1056
+							$ignore = $originalIgnore;
1057
+							return $opener;
1058
+						} else if ($scopeCloser === $i
1059
+							&& isset($this->scopeOpeners[$tokenType]) === true
1060
+						) {
1061
+							// Unset scope_condition here or else the token will appear to have
1062
+							// already been processed, and it will be skipped. Normally we want that,
1063
+							// but in this case, the token is both a closer and an opener, so
1064
+							// it needs to act like an opener. This is also why we return the
1065
+							// token before this one; so the closer has a chance to be processed
1066
+							// a second time, but as an opener.
1067
+							unset($this->tokens[$scopeCloser]['scope_condition']);
1068
+							return ($i - 1);
1069
+						} else {
1070
+							return $i;
1071
+						}
1072
+					} else {
1073
+						continue;
1074
+					}//end if
1075
+				}//end if
1076
+			}//end if
1077
+
1078
+			// Is this an opening condition ?
1079
+			if (isset($this->scopeOpeners[$tokenType]) === true) {
1080
+				if ($opener === null) {
1081
+					if ($tokenType === T_USE) {
1082
+						// PHP use keywords are special because they can be
1083
+						// used as blocks but also inline in function definitions.
1084
+						// So if we find them nested inside another opener, just skip them.
1085
+						continue;
1086
+					}
1087
+
1088
+					if ($tokenType === T_FUNCTION
1089
+						&& $this->tokens[$stackPtr]['code'] !== T_FUNCTION
1090
+					) {
1091
+						// Probably a closure, so process it manually.
1092
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1093
+							$type = $this->tokens[$stackPtr]['type'];
1094
+							echo str_repeat("\t", $depth);
1095
+							echo "=> Found function before scope opener for $stackPtr:$type, processing manually".PHP_EOL;
1096
+						}
1097
+
1098
+						if (isset($this->tokens[$i]['scope_closer']) === true) {
1099
+							// We've already processed this closure.
1100
+							if (PHP_CODESNIFFER_VERBOSITY > 1) {
1101
+								echo str_repeat("\t", $depth);
1102
+								echo '* already processed, skipping *'.PHP_EOL;
1103
+							}
1104
+
1105
+							$i = $this->tokens[$i]['scope_closer'];
1106
+							continue;
1107
+						}
1108
+
1109
+						$i = self::recurseScopeMap($i, ($depth + 1), $ignore);
1110
+						continue;
1111
+					}//end if
1112
+
1113
+					if ($tokenType === T_CLASS) {
1114
+						// Probably an anonymous class inside another anonymous class,
1115
+						// so process it manually.
1116
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1117
+							$type = $this->tokens[$stackPtr]['type'];
1118
+							echo str_repeat("\t", $depth);
1119
+							echo "=> Found class before scope opener for $stackPtr:$type, processing manually".PHP_EOL;
1120
+						}
1121
+
1122
+						if (isset($this->tokens[$i]['scope_closer']) === true) {
1123
+							// We've already processed this anon class.
1124
+							if (PHP_CODESNIFFER_VERBOSITY > 1) {
1125
+								echo str_repeat("\t", $depth);
1126
+								echo '* already processed, skipping *'.PHP_EOL;
1127
+							}
1128
+
1129
+							$i = $this->tokens[$i]['scope_closer'];
1130
+							continue;
1131
+						}
1132
+
1133
+						$i = self::recurseScopeMap($i, ($depth + 1), $ignore);
1134
+						continue;
1135
+					}//end if
1136
+
1137
+					// Found another opening condition but still haven't
1138
+					// found our opener, so we are never going to find one.
1139
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1140
+						$type = $this->tokens[$stackPtr]['type'];
1141
+						echo str_repeat("\t", $depth);
1142
+						echo "=> Found new opening condition before scope opener for $stackPtr:$type, ";
1143
+					}
1144
+
1145
+					if (($this->tokens[$stackPtr]['code'] === T_IF
1146
+						|| $this->tokens[$stackPtr]['code'] === T_ELSEIF
1147
+						|| $this->tokens[$stackPtr]['code'] === T_ELSE)
1148
+						&& ($this->tokens[$i]['code'] === T_ELSE
1149
+						|| $this->tokens[$i]['code'] === T_ELSEIF)
1150
+					) {
1151
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1152
+							echo "continuing".PHP_EOL;
1153
+						}
1154
+
1155
+						return ($i - 1);
1156
+					} else {
1157
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1158
+							echo "backtracking".PHP_EOL;
1159
+						}
1160
+
1161
+						return $stackPtr;
1162
+					}
1163
+				}//end if
1164
+
1165
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
1166
+					echo str_repeat("\t", $depth);
1167
+					echo '* token is an opening condition *'.PHP_EOL;
1168
+				}
1169
+
1170
+				$isShared = ($this->scopeOpeners[$tokenType]['shared'] === true);
1171
+
1172
+				if (isset($this->tokens[$i]['scope_condition']) === true) {
1173
+					// We've been here before.
1174
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1175
+						echo str_repeat("\t", $depth);
1176
+						echo '* already processed, skipping *'.PHP_EOL;
1177
+					}
1178
+
1179
+					if ($isShared === false
1180
+						&& isset($this->tokens[$i]['scope_closer']) === true
1181
+					) {
1182
+						$i = $this->tokens[$i]['scope_closer'];
1183
+					}
1184
+
1185
+					continue;
1186
+				} else if ($currType === $tokenType
1187
+					&& $isShared === false
1188
+					&& $opener === null
1189
+				) {
1190
+					// We haven't yet found our opener, but we have found another
1191
+					// scope opener which is the same type as us, and we don't
1192
+					// share openers, so we will never find one.
1193
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1194
+						echo str_repeat("\t", $depth);
1195
+						echo '* it was another token\'s opener, bailing *'.PHP_EOL;
1196
+					}
1197
+
1198
+					return $stackPtr;
1199
+				} else {
1200
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1201
+						echo str_repeat("\t", $depth);
1202
+						echo '* searching for opener *'.PHP_EOL;
1203
+					}
1204
+
1205
+					if (isset($this->scopeOpeners[$tokenType]['end'][T_CLOSE_CURLY_BRACKET]) === true) {
1206
+						$oldIgnore = $ignore;
1207
+						$ignore    = 0;
1208
+					}
1209
+
1210
+					// PHP has a max nesting level for functions. Stop before we hit that limit
1211
+					// because too many loops means we've run into trouble anyway.
1212
+					if ($depth > 50) {
1213
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1214
+							echo str_repeat("\t", $depth);
1215
+							echo '* reached maximum nesting level; aborting *'.PHP_EOL;
1216
+						}
1217
+
1218
+						throw new RuntimeException('Maximum nesting level reached; file could not be processed');
1219
+					}
1220
+
1221
+					$oldDepth = $depth;
1222
+					if ($isShared === true
1223
+						&& isset($this->scopeOpeners[$tokenType]['with'][$currType]) === true
1224
+					) {
1225
+						// Don't allow the depth to increment because this is
1226
+						// possibly not a true nesting if we are sharing our closer.
1227
+						// This can happen, for example, when a SWITCH has a large
1228
+						// number of CASE statements with the same shared BREAK.
1229
+						$depth--;
1230
+					}
1231
+
1232
+					$i     = self::recurseScopeMap($i, ($depth + 1), $ignore);
1233
+					$depth = $oldDepth;
1234
+
1235
+					if (isset($this->scopeOpeners[$tokenType]['end'][T_CLOSE_CURLY_BRACKET]) === true) {
1236
+						$ignore = $oldIgnore;
1237
+					}
1238
+				}//end if
1239
+			}//end if
1240
+
1241
+			if (isset($this->scopeOpeners[$currType]['start'][$tokenType]) === true
1242
+				&& $opener === null
1243
+			) {
1244
+				if ($tokenType === T_OPEN_CURLY_BRACKET) {
1245
+					if (isset($this->tokens[$stackPtr]['parenthesis_closer']) === true
1246
+						&& $i < $this->tokens[$stackPtr]['parenthesis_closer']
1247
+					) {
1248
+						// We found a curly brace inside the condition of the
1249
+						// current scope opener, so it must be a string offset.
1250
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1251
+							echo str_repeat("\t", $depth);
1252
+							echo '* ignoring curly brace inside condition *'.PHP_EOL;
1253
+						}
1254
+
1255
+						$ignore++;
1256
+					} else {
1257
+						// Make sure this is actually an opener and not a
1258
+						// string offset (e.g., $var{0}).
1259
+						for ($x = ($i - 1); $x > 0; $x--) {
1260
+							if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === true) {
1261
+								continue;
1262
+							} else {
1263
+								// If the first non-whitespace/comment token looks like this
1264
+								// brace is a string offset, or this brace is mid-way through
1265
+								// a new statement, it isn't a scope opener.
1266
+								$disallowed  = Util\Tokens::$assignmentTokens;
1267
+								$disallowed += [
1268
+									T_DOLLAR           => true,
1269
+									T_VARIABLE         => true,
1270
+									T_OBJECT_OPERATOR  => true,
1271
+									T_COMMA            => true,
1272
+									T_OPEN_PARENTHESIS => true,
1273
+								];
1274
+
1275
+								if (isset($disallowed[$this->tokens[$x]['code']]) === true) {
1276
+									if (PHP_CODESNIFFER_VERBOSITY > 1) {
1277
+										echo str_repeat("\t", $depth);
1278
+										echo '* ignoring curly brace *'.PHP_EOL;
1279
+									}
1280
+
1281
+									$ignore++;
1282
+								}
1283
+
1284
+								break;
1285
+							}//end if
1286
+						}//end for
1287
+					}//end if
1288
+				}//end if
1289
+
1290
+				if ($ignore === 0 || $tokenType !== T_OPEN_CURLY_BRACKET) {
1291
+					// We found the opening scope token for $currType.
1292
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1293
+						$type = $this->tokens[$stackPtr]['type'];
1294
+						echo str_repeat("\t", $depth);
1295
+						echo "=> Found scope opener for $stackPtr:$type".PHP_EOL;
1296
+					}
1297
+
1298
+					$opener = $i;
1299
+				}
1300
+			} else if ($tokenType === T_OPEN_PARENTHESIS) {
1301
+				if (isset($this->tokens[$i]['parenthesis_owner']) === true) {
1302
+					$owner = $this->tokens[$i]['parenthesis_owner'];
1303
+					if (isset(Util\Tokens::$scopeOpeners[$this->tokens[$owner]['code']]) === true
1304
+						&& isset($this->tokens[$i]['parenthesis_closer']) === true
1305
+					) {
1306
+						// If we get into here, then we opened a parenthesis for
1307
+						// a scope (eg. an if or else if) so we need to update the
1308
+						// start of the line so that when we check to see
1309
+						// if the closing parenthesis is more than 3 lines away from
1310
+						// the statement, we check from the closing parenthesis.
1311
+						$startLine = $this->tokens[$this->tokens[$i]['parenthesis_closer']]['line'];
1312
+					}
1313
+				}
1314
+			} else if ($tokenType === T_OPEN_CURLY_BRACKET && $opener !== null) {
1315
+				// We opened something that we don't have a scope opener for.
1316
+				// Examples of this are curly brackets for string offsets etc.
1317
+				// We want to ignore this so that we don't have an invalid scope
1318
+				// map.
1319
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
1320
+					echo str_repeat("\t", $depth);
1321
+					echo '* ignoring curly brace *'.PHP_EOL;
1322
+				}
1323
+
1324
+				$ignore++;
1325
+			} else if ($tokenType === T_CLOSE_CURLY_BRACKET && $ignore > 0) {
1326
+				// We found the end token for the opener we were ignoring.
1327
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
1328
+					echo str_repeat("\t", $depth);
1329
+					echo '* finished ignoring curly brace *'.PHP_EOL;
1330
+				}
1331
+
1332
+				$ignore--;
1333
+			} else if ($opener === null
1334
+				&& isset($this->scopeOpeners[$currType]) === true
1335
+			) {
1336
+				// If we still haven't found the opener after 30 lines,
1337
+				// we're not going to find it, unless we know it requires
1338
+				// an opener (in which case we better keep looking) or the last
1339
+				// token was empty (in which case we'll just confirm there is
1340
+				// more code in this file and not just a big comment).
1341
+				if ($this->tokens[$i]['line'] >= ($startLine + 30)
1342
+					&& isset(Util\Tokens::$emptyTokens[$this->tokens[($i - 1)]['code']]) === false
1343
+				) {
1344
+					if ($this->scopeOpeners[$currType]['strict'] === true) {
1345
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1346
+							$type  = $this->tokens[$stackPtr]['type'];
1347
+							$lines = ($this->tokens[$i]['line'] - $startLine);
1348
+							echo str_repeat("\t", $depth);
1349
+							echo "=> Still looking for $stackPtr:$type scope opener after $lines lines".PHP_EOL;
1350
+						}
1351
+					} else {
1352
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1353
+							$type = $this->tokens[$stackPtr]['type'];
1354
+							echo str_repeat("\t", $depth);
1355
+							echo "=> Couldn't find scope opener for $stackPtr:$type, bailing".PHP_EOL;
1356
+						}
1357
+
1358
+						return $stackPtr;
1359
+					}
1360
+				}
1361
+			} else if ($opener !== null
1362
+				&& $tokenType !== T_BREAK
1363
+				&& isset($this->endScopeTokens[$tokenType]) === true
1364
+			) {
1365
+				if (isset($this->tokens[$i]['scope_condition']) === false) {
1366
+					if ($ignore > 0) {
1367
+						// We found the end token for the opener we were ignoring.
1368
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1369
+							echo str_repeat("\t", $depth);
1370
+							echo '* finished ignoring curly brace *'.PHP_EOL;
1371
+						}
1372
+
1373
+						$ignore--;
1374
+					} else {
1375
+						// We found a token that closes the scope but it doesn't
1376
+						// have a condition, so it belongs to another token and
1377
+						// our token doesn't have a closer, so pretend this is
1378
+						// the closer.
1379
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1380
+							$type = $this->tokens[$stackPtr]['type'];
1381
+							echo str_repeat("\t", $depth);
1382
+							echo "=> Found (unexpected) scope closer for $stackPtr:$type".PHP_EOL;
1383
+						}
1384
+
1385
+						foreach ([$stackPtr, $opener] as $token) {
1386
+							$this->tokens[$token]['scope_condition'] = $stackPtr;
1387
+							$this->tokens[$token]['scope_opener']    = $opener;
1388
+							$this->tokens[$token]['scope_closer']    = $i;
1389
+						}
1390
+
1391
+						return ($i - 1);
1392
+					}//end if
1393
+				}//end if
1394
+			}//end if
1395
+		}//end for
1396
+
1397
+		return $stackPtr;
1398
+
1399
+	}//end recurseScopeMap()
1400
+
1401
+
1402
+	/**
1403
+	 * Constructs the level map.
1404
+	 *
1405
+	 * The level map adds a 'level' index to each token which indicates the
1406
+	 * depth that a token within a set of scope blocks. It also adds a
1407
+	 * 'conditions' index which is an array of the scope conditions that opened
1408
+	 * each of the scopes - position 0 being the first scope opener.
1409
+	 *
1410
+	 * @return void
1411
+	 */
1412
+	private function createLevelMap()
1413
+	{
1414
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
1415
+			echo "\t*** START LEVEL MAP ***".PHP_EOL;
1416
+		}
1417
+
1418
+		$this->numTokens = count($this->tokens);
1419
+		$level           = 0;
1420
+		$conditions      = [];
1421
+		$lastOpener      = null;
1422
+		$openers         = [];
1423
+
1424
+		for ($i = 0; $i < $this->numTokens; $i++) {
1425
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
1426
+				$type = $this->tokens[$i]['type'];
1427
+				$line = $this->tokens[$i]['line'];
1428
+				$len  = $this->tokens[$i]['length'];
1429
+				$col  = $this->tokens[$i]['column'];
1430
+
1431
+				$content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
1432
+
1433
+				echo str_repeat("\t", ($level + 1));
1434
+				echo "Process token $i on line $line [col:$col;len:$len;lvl:$level;";
1435
+				if (empty($conditions) !== true) {
1436
+					$condString = 'conds;';
1437
+					foreach ($conditions as $condition) {
1438
+						$condString .= Util\Tokens::tokenName($condition).',';
1439
+					}
1440
+
1441
+					echo rtrim($condString, ',').';';
1442
+				}
1443
+
1444
+				echo "]: $type => $content".PHP_EOL;
1445
+			}//end if
1446
+
1447
+			$this->tokens[$i]['level']      = $level;
1448
+			$this->tokens[$i]['conditions'] = $conditions;
1449
+
1450
+			if (isset($this->tokens[$i]['scope_condition']) === true) {
1451
+				// Check to see if this token opened the scope.
1452
+				if ($this->tokens[$i]['scope_opener'] === $i) {
1453
+					$stackPtr = $this->tokens[$i]['scope_condition'];
1454
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1455
+						$type = $this->tokens[$stackPtr]['type'];
1456
+						echo str_repeat("\t", ($level + 1));
1457
+						echo "=> Found scope opener for $stackPtr:$type".PHP_EOL;
1458
+					}
1459
+
1460
+					$stackPtr = $this->tokens[$i]['scope_condition'];
1461
+
1462
+					// If we find a scope opener that has a shared closer,
1463
+					// then we need to go back over the condition map that we
1464
+					// just created and fix ourselves as we just added some
1465
+					// conditions where there was none. This happens for T_CASE
1466
+					// statements that are using the same break statement.
1467
+					if ($lastOpener !== null && $this->tokens[$lastOpener]['scope_closer'] === $this->tokens[$i]['scope_closer']) {
1468
+						// This opener shares its closer with the previous opener,
1469
+						// but we still need to check if the two openers share their
1470
+						// closer with each other directly (like CASE and DEFAULT)
1471
+						// or if they are just sharing because one doesn't have a
1472
+						// closer (like CASE with no BREAK using a SWITCHes closer).
1473
+						$thisType = $this->tokens[$this->tokens[$i]['scope_condition']]['code'];
1474
+						$opener   = $this->tokens[$lastOpener]['scope_condition'];
1475
+
1476
+						$isShared = isset($this->scopeOpeners[$thisType]['with'][$this->tokens[$opener]['code']]);
1477
+
1478
+						reset($this->scopeOpeners[$thisType]['end']);
1479
+						reset($this->scopeOpeners[$this->tokens[$opener]['code']]['end']);
1480
+						$sameEnd = (current($this->scopeOpeners[$thisType]['end']) === current($this->scopeOpeners[$this->tokens[$opener]['code']]['end']));
1481
+
1482
+						if ($isShared === true && $sameEnd === true) {
1483
+							$badToken = $opener;
1484
+							if (PHP_CODESNIFFER_VERBOSITY > 1) {
1485
+								$type = $this->tokens[$badToken]['type'];
1486
+								echo str_repeat("\t", ($level + 1));
1487
+								echo "* shared closer, cleaning up $badToken:$type *".PHP_EOL;
1488
+							}
1489
+
1490
+							for ($x = $this->tokens[$i]['scope_condition']; $x <= $i; $x++) {
1491
+								$oldConditions = $this->tokens[$x]['conditions'];
1492
+								$oldLevel      = $this->tokens[$x]['level'];
1493
+								$this->tokens[$x]['level']--;
1494
+								unset($this->tokens[$x]['conditions'][$badToken]);
1495
+								if (PHP_CODESNIFFER_VERBOSITY > 1) {
1496
+									$type     = $this->tokens[$x]['type'];
1497
+									$oldConds = '';
1498
+									foreach ($oldConditions as $condition) {
1499
+										$oldConds .= Util\Tokens::tokenName($condition).',';
1500
+									}
1501
+
1502
+									$oldConds = rtrim($oldConds, ',');
1503
+
1504
+									$newConds = '';
1505
+									foreach ($this->tokens[$x]['conditions'] as $condition) {
1506
+										$newConds .= Util\Tokens::tokenName($condition).',';
1507
+									}
1508
+
1509
+									$newConds = rtrim($newConds, ',');
1510
+
1511
+									$newLevel = $this->tokens[$x]['level'];
1512
+									echo str_repeat("\t", ($level + 1));
1513
+									echo "* cleaned $x:$type *".PHP_EOL;
1514
+									echo str_repeat("\t", ($level + 2));
1515
+									echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
1516
+									echo str_repeat("\t", ($level + 2));
1517
+									echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
1518
+								}//end if
1519
+							}//end for
1520
+
1521
+							unset($conditions[$badToken]);
1522
+							if (PHP_CODESNIFFER_VERBOSITY > 1) {
1523
+								$type = $this->tokens[$badToken]['type'];
1524
+								echo str_repeat("\t", ($level + 1));
1525
+								echo "* token $badToken:$type removed from conditions array *".PHP_EOL;
1526
+							}
1527
+
1528
+							unset($openers[$lastOpener]);
1529
+
1530
+							$level--;
1531
+							if (PHP_CODESNIFFER_VERBOSITY > 1) {
1532
+								echo str_repeat("\t", ($level + 2));
1533
+								echo '* level decreased *'.PHP_EOL;
1534
+							}
1535
+						}//end if
1536
+					}//end if
1537
+
1538
+					$level++;
1539
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1540
+						echo str_repeat("\t", ($level + 1));
1541
+						echo '* level increased *'.PHP_EOL;
1542
+					}
1543
+
1544
+					$conditions[$stackPtr] = $this->tokens[$stackPtr]['code'];
1545
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1546
+						$type = $this->tokens[$stackPtr]['type'];
1547
+						echo str_repeat("\t", ($level + 1));
1548
+						echo "* token $stackPtr:$type added to conditions array *".PHP_EOL;
1549
+					}
1550
+
1551
+					$lastOpener = $this->tokens[$i]['scope_opener'];
1552
+					if ($lastOpener !== null) {
1553
+						$openers[$lastOpener] = $lastOpener;
1554
+					}
1555
+				} else if ($lastOpener !== null && $this->tokens[$lastOpener]['scope_closer'] === $i) {
1556
+					foreach (array_reverse($openers) as $opener) {
1557
+						if ($this->tokens[$opener]['scope_closer'] === $i) {
1558
+							$oldOpener = array_pop($openers);
1559
+							if (empty($openers) === false) {
1560
+								$lastOpener           = array_pop($openers);
1561
+								$openers[$lastOpener] = $lastOpener;
1562
+							} else {
1563
+								$lastOpener = null;
1564
+							}
1565
+
1566
+							if (PHP_CODESNIFFER_VERBOSITY > 1) {
1567
+								$type = $this->tokens[$oldOpener]['type'];
1568
+								echo str_repeat("\t", ($level + 1));
1569
+								echo "=> Found scope closer for $oldOpener:$type".PHP_EOL;
1570
+							}
1571
+
1572
+							$oldCondition = array_pop($conditions);
1573
+							if (PHP_CODESNIFFER_VERBOSITY > 1) {
1574
+								echo str_repeat("\t", ($level + 1));
1575
+								echo '* token '.Util\Tokens::tokenName($oldCondition).' removed from conditions array *'.PHP_EOL;
1576
+							}
1577
+
1578
+							// Make sure this closer actually belongs to us.
1579
+							// Either the condition also has to think this is the
1580
+							// closer, or it has to allow sharing with us.
1581
+							$condition = $this->tokens[$this->tokens[$i]['scope_condition']]['code'];
1582
+							if ($condition !== $oldCondition) {
1583
+								if (isset($this->scopeOpeners[$oldCondition]['with'][$condition]) === false) {
1584
+									$badToken = $this->tokens[$oldOpener]['scope_condition'];
1585
+
1586
+									if (PHP_CODESNIFFER_VERBOSITY > 1) {
1587
+										$type = Util\Tokens::tokenName($oldCondition);
1588
+										echo str_repeat("\t", ($level + 1));
1589
+										echo "* scope closer was bad, cleaning up $badToken:$type *".PHP_EOL;
1590
+									}
1591
+
1592
+									for ($x = ($oldOpener + 1); $x <= $i; $x++) {
1593
+										$oldConditions = $this->tokens[$x]['conditions'];
1594
+										$oldLevel      = $this->tokens[$x]['level'];
1595
+										$this->tokens[$x]['level']--;
1596
+										unset($this->tokens[$x]['conditions'][$badToken]);
1597
+										if (PHP_CODESNIFFER_VERBOSITY > 1) {
1598
+											$type     = $this->tokens[$x]['type'];
1599
+											$oldConds = '';
1600
+											foreach ($oldConditions as $condition) {
1601
+												$oldConds .= Util\Tokens::tokenName($condition).',';
1602
+											}
1603
+
1604
+											$oldConds = rtrim($oldConds, ',');
1605
+
1606
+											$newConds = '';
1607
+											foreach ($this->tokens[$x]['conditions'] as $condition) {
1608
+												$newConds .= Util\Tokens::tokenName($condition).',';
1609
+											}
1610
+
1611
+											$newConds = rtrim($newConds, ',');
1612
+
1613
+											$newLevel = $this->tokens[$x]['level'];
1614
+											echo str_repeat("\t", ($level + 1));
1615
+											echo "* cleaned $x:$type *".PHP_EOL;
1616
+											echo str_repeat("\t", ($level + 2));
1617
+											echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
1618
+											echo str_repeat("\t", ($level + 2));
1619
+											echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
1620
+										}//end if
1621
+									}//end for
1622
+								}//end if
1623
+							}//end if
1624
+
1625
+							$level--;
1626
+							if (PHP_CODESNIFFER_VERBOSITY > 1) {
1627
+								echo str_repeat("\t", ($level + 2));
1628
+								echo '* level decreased *'.PHP_EOL;
1629
+							}
1630
+
1631
+							$this->tokens[$i]['level']      = $level;
1632
+							$this->tokens[$i]['conditions'] = $conditions;
1633
+						}//end if
1634
+					}//end foreach
1635
+				}//end if
1636
+			}//end if
1637
+		}//end for
1638
+
1639
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
1640
+			echo "\t*** END LEVEL MAP ***".PHP_EOL;
1641
+		}
1642
+
1643
+	}//end createLevelMap()
1644 1644
 
1645 1645
 
1646 1646
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Tokenizers/CSS.php 1 patch
Indentation   +511 added lines, -511 removed lines patch added patch discarded remove patch
@@ -17,521 +17,521 @@
 block discarded – undo
17 17
 {
18 18
 
19 19
 
20
-    /**
21
-     * Initialise the tokenizer.
22
-     *
23
-     * Pre-checks the content to see if it looks minified.
24
-     *
25
-     * @param string                  $content The content to tokenize,
26
-     * @param \PHP_CodeSniffer\Config $config  The config data for the run.
27
-     * @param string                  $eolChar The EOL char used in the content.
28
-     *
29
-     * @return void
30
-     * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the file appears to be minified.
31
-     */
32
-    public function __construct($content, Config $config, $eolChar='\n')
33
-    {
34
-        if ($this->isMinifiedContent($content, $eolChar) === true) {
35
-            throw new TokenizerException('File appears to be minified and cannot be processed');
36
-        }
37
-
38
-        parent::__construct($content, $config, $eolChar);
39
-
40
-    }//end __construct()
41
-
42
-
43
-    /**
44
-     * Creates an array of tokens when given some CSS code.
45
-     *
46
-     * Uses the PHP tokenizer to do all the tricky work
47
-     *
48
-     * @param string $string The string to tokenize.
49
-     *
50
-     * @return array
51
-     */
52
-    public function tokenize($string)
53
-    {
54
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
55
-            echo "\t*** START CSS TOKENIZING 1ST PASS ***".PHP_EOL;
56
-        }
57
-
58
-        // If the content doesn't have an EOL char on the end, add one so
59
-        // the open and close tags we add are parsed correctly.
60
-        $eolAdded = false;
61
-        if (substr($string, (strlen($this->eolChar) * -1)) !== $this->eolChar) {
62
-            $string  .= $this->eolChar;
63
-            $eolAdded = true;
64
-        }
65
-
66
-        $string = str_replace('<?php', '^PHPCS_CSS_T_OPEN_TAG^', $string);
67
-        $string = str_replace('?>', '^PHPCS_CSS_T_CLOSE_TAG^', $string);
68
-        $tokens = parent::tokenize('<?php '.$string.'?>');
69
-
70
-        $finalTokens    = [];
71
-        $finalTokens[0] = [
72
-            'code'    => T_OPEN_TAG,
73
-            'type'    => 'T_OPEN_TAG',
74
-            'content' => '',
75
-        ];
76
-
77
-        $newStackPtr      = 1;
78
-        $numTokens        = count($tokens);
79
-        $multiLineComment = false;
80
-        for ($stackPtr = 1; $stackPtr < $numTokens; $stackPtr++) {
81
-            $token = $tokens[$stackPtr];
82
-
83
-            // CSS files don't have lists, breaks etc, so convert these to
84
-            // standard strings early so they can be converted into T_STYLE
85
-            // tokens and joined with other strings if needed.
86
-            if ($token['code'] === T_BREAK
87
-                || $token['code'] === T_LIST
88
-                || $token['code'] === T_DEFAULT
89
-                || $token['code'] === T_SWITCH
90
-                || $token['code'] === T_FOR
91
-                || $token['code'] === T_FOREACH
92
-                || $token['code'] === T_WHILE
93
-                || $token['code'] === T_DEC
94
-                || $token['code'] === T_NEW
95
-            ) {
96
-                $token['type'] = 'T_STRING';
97
-                $token['code'] = T_STRING;
98
-            }
99
-
100
-            $token['content'] = str_replace('^PHPCS_CSS_T_OPEN_TAG^', '<?php', $token['content']);
101
-            $token['content'] = str_replace('^PHPCS_CSS_T_CLOSE_TAG^', '?>', $token['content']);
102
-
103
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
104
-                $type    = $token['type'];
105
-                $content = Util\Common::prepareForOutput($token['content']);
106
-                echo "\tProcess token $stackPtr: $type => $content".PHP_EOL;
107
-            }
108
-
109
-            if ($token['code'] === T_BITWISE_XOR
110
-                && $tokens[($stackPtr + 1)]['content'] === 'PHPCS_CSS_T_OPEN_TAG'
111
-            ) {
112
-                $content = '<?php';
113
-                for ($stackPtr += 3; $stackPtr < $numTokens; $stackPtr++) {
114
-                    if ($tokens[$stackPtr]['code'] === T_BITWISE_XOR
115
-                        && $tokens[($stackPtr + 1)]['content'] === 'PHPCS_CSS_T_CLOSE_TAG'
116
-                    ) {
117
-                        // Add the end tag and ignore the * we put at the end.
118
-                        $content  .= '?>';
119
-                        $stackPtr += 2;
120
-                        break;
121
-                    } else {
122
-                        $content .= $tokens[$stackPtr]['content'];
123
-                    }
124
-                }
125
-
126
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
127
-                    echo "\t\t=> Found embedded PHP code: ";
128
-                    $cleanContent = Util\Common::prepareForOutput($content);
129
-                    echo $cleanContent.PHP_EOL;
130
-                }
131
-
132
-                $finalTokens[$newStackPtr] = [
133
-                    'type'    => 'T_EMBEDDED_PHP',
134
-                    'code'    => T_EMBEDDED_PHP,
135
-                    'content' => $content,
136
-                ];
137
-
138
-                $newStackPtr++;
139
-                continue;
140
-            }//end if
141
-
142
-            if ($token['code'] === T_GOTO_LABEL) {
143
-                // Convert these back to T_STRING followed by T_COLON so we can
144
-                // more easily process style definitions.
145
-                $finalTokens[$newStackPtr] = [
146
-                    'type'    => 'T_STRING',
147
-                    'code'    => T_STRING,
148
-                    'content' => substr($token['content'], 0, -1),
149
-                ];
150
-                $newStackPtr++;
151
-                $finalTokens[$newStackPtr] = [
152
-                    'type'    => 'T_COLON',
153
-                    'code'    => T_COLON,
154
-                    'content' => ':',
155
-                ];
156
-                $newStackPtr++;
157
-                continue;
158
-            }
159
-
160
-            if ($token['code'] === T_FUNCTION) {
161
-                // There are no functions in CSS, so convert this to a string.
162
-                $finalTokens[$newStackPtr] = [
163
-                    'type'    => 'T_STRING',
164
-                    'code'    => T_STRING,
165
-                    'content' => $token['content'],
166
-                ];
167
-
168
-                $newStackPtr++;
169
-                continue;
170
-            }
171
-
172
-            if ($token['code'] === T_COMMENT
173
-                && substr($token['content'], 0, 2) === '/*'
174
-            ) {
175
-                // Multi-line comment. Record it so we can ignore other
176
-                // comment tags until we get out of this one.
177
-                $multiLineComment = true;
178
-            }
179
-
180
-            if ($token['code'] === T_COMMENT
181
-                && $multiLineComment === false
182
-                && (substr($token['content'], 0, 2) === '//'
183
-                || $token['content']{0} === '#')
184
-            ) {
185
-                $content = ltrim($token['content'], '#/');
186
-
187
-                // Guard against PHP7+ syntax errors by stripping
188
-                // leading zeros so the content doesn't look like an invalid int.
189
-                $leadingZero = false;
190
-                if ($content{0} === '0') {
191
-                    $content     = '1'.$content;
192
-                    $leadingZero = true;
193
-                }
194
-
195
-                $commentTokens = parent::tokenize('<?php '.$content.'?>');
196
-
197
-                // The first and last tokens are the open/close tags.
198
-                array_shift($commentTokens);
199
-                array_pop($commentTokens);
200
-
201
-                if ($leadingZero === true) {
202
-                    $commentTokens[0]['content'] = substr($commentTokens[0]['content'], 1);
203
-                    $content = substr($content, 1);
204
-                }
205
-
206
-                if ($token['content']{0} === '#') {
207
-                    // The # character is not a comment in CSS files, so
208
-                    // determine what it means in this context.
209
-                    $firstContent = $commentTokens[0]['content'];
210
-
211
-                    // If the first content is just a number, it is probably a
212
-                    // colour like 8FB7DB, which PHP splits into 8 and FB7DB.
213
-                    if (($commentTokens[0]['code'] === T_LNUMBER
214
-                        || $commentTokens[0]['code'] === T_DNUMBER)
215
-                        && $commentTokens[1]['code'] === T_STRING
216
-                    ) {
217
-                        $firstContent .= $commentTokens[1]['content'];
218
-                        array_shift($commentTokens);
219
-                    }
220
-
221
-                    // If the first content looks like a colour and not a class
222
-                    // definition, join the tokens together.
223
-                    if (preg_match('/^[ABCDEF0-9]+$/i', $firstContent) === 1
224
-                        && $commentTokens[1]['content'] !== '-'
225
-                    ) {
226
-                        array_shift($commentTokens);
227
-                        // Work out what we trimmed off above and remember to re-add it.
228
-                        $trimmed = substr($token['content'], 0, (strlen($token['content']) - strlen($content)));
229
-                        $finalTokens[$newStackPtr] = [
230
-                            'type'    => 'T_COLOUR',
231
-                            'code'    => T_COLOUR,
232
-                            'content' => $trimmed.$firstContent,
233
-                        ];
234
-                    } else {
235
-                        $finalTokens[$newStackPtr] = [
236
-                            'type'    => 'T_HASH',
237
-                            'code'    => T_HASH,
238
-                            'content' => '#',
239
-                        ];
240
-                    }
241
-                } else {
242
-                    $finalTokens[$newStackPtr] = [
243
-                        'type'    => 'T_STRING',
244
-                        'code'    => T_STRING,
245
-                        'content' => '//',
246
-                    ];
247
-                }//end if
248
-
249
-                $newStackPtr++;
250
-
251
-                array_splice($tokens, $stackPtr, 1, $commentTokens);
252
-                $numTokens = count($tokens);
253
-                $stackPtr--;
254
-                continue;
255
-            }//end if
256
-
257
-            if ($token['code'] === T_COMMENT
258
-                && substr($token['content'], -2) === '*/'
259
-            ) {
260
-                // Multi-line comment is done.
261
-                $multiLineComment = false;
262
-            }
263
-
264
-            $finalTokens[$newStackPtr] = $token;
265
-            $newStackPtr++;
266
-        }//end for
267
-
268
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
269
-            echo "\t*** END CSS TOKENIZING 1ST PASS ***".PHP_EOL;
270
-            echo "\t*** START CSS TOKENIZING 2ND PASS ***".PHP_EOL;
271
-        }
272
-
273
-        // A flag to indicate if we are inside a style definition,
274
-        // which is defined using curly braces.
275
-        $inStyleDef = false;
276
-
277
-        // A flag to indicate if an At-rule like "@media" is used, which will result
278
-        // in nested curly brackets.
279
-        $asperandStart = false;
280
-
281
-        $numTokens = count($finalTokens);
282
-        for ($stackPtr = 0; $stackPtr < $numTokens; $stackPtr++) {
283
-            $token = $finalTokens[$stackPtr];
284
-
285
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
286
-                $type    = $token['type'];
287
-                $content = Util\Common::prepareForOutput($token['content']);
288
-                echo "\tProcess token $stackPtr: $type => $content".PHP_EOL;
289
-            }
290
-
291
-            switch ($token['code']) {
292
-            case T_OPEN_CURLY_BRACKET:
293
-                // Opening curly brackets for an At-rule do not start a style
294
-                // definition. We also reset the asperand flag here because the next
295
-                // opening curly bracket could be indeed the start of a style
296
-                // definition.
297
-                if ($asperandStart === true) {
298
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
299
-                        if ($inStyleDef === true) {
300
-                            echo "\t\t* style definition closed *".PHP_EOL;
301
-                        }
302
-
303
-                        if ($asperandStart === true) {
304
-                            echo "\t\t* at-rule definition closed *".PHP_EOL;
305
-                        }
306
-                    }
307
-
308
-                    $inStyleDef    = false;
309
-                    $asperandStart = false;
310
-                } else {
311
-                    $inStyleDef = true;
312
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
313
-                        echo "\t\t* style definition opened *".PHP_EOL;
314
-                    }
315
-                }
316
-                break;
317
-            case T_CLOSE_CURLY_BRACKET:
318
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
319
-                    if ($inStyleDef === true) {
320
-                        echo "\t\t* style definition closed *".PHP_EOL;
321
-                    }
322
-
323
-                    if ($asperandStart === true) {
324
-                        echo "\t\t* at-rule definition closed *".PHP_EOL;
325
-                    }
326
-                }
327
-
328
-                $inStyleDef    = false;
329
-                $asperandStart = false;
330
-                break;
331
-            case T_MINUS:
332
-                // Minus signs are often used instead of spaces inside
333
-                // class names, IDs and styles.
334
-                if ($finalTokens[($stackPtr + 1)]['code'] === T_STRING) {
335
-                    if ($finalTokens[($stackPtr - 1)]['code'] === T_STRING) {
336
-                        $newContent = $finalTokens[($stackPtr - 1)]['content'].'-'.$finalTokens[($stackPtr + 1)]['content'];
337
-
338
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
339
-                            echo "\t\t* token is a string joiner; ignoring this and previous token".PHP_EOL;
340
-                            $old = Util\Common::prepareForOutput($finalTokens[($stackPtr + 1)]['content']);
341
-                            $new = Util\Common::prepareForOutput($newContent);
342
-                            echo "\t\t=> token ".($stackPtr + 1)." content changed from \"$old\" to \"$new\"".PHP_EOL;
343
-                        }
344
-
345
-                        $finalTokens[($stackPtr + 1)]['content'] = $newContent;
346
-                        unset($finalTokens[$stackPtr]);
347
-                        unset($finalTokens[($stackPtr - 1)]);
348
-                    } else {
349
-                        $newContent = '-'.$finalTokens[($stackPtr + 1)]['content'];
350
-
351
-                        $finalTokens[($stackPtr + 1)]['content'] = $newContent;
352
-                        unset($finalTokens[$stackPtr]);
353
-                    }
354
-                } else if ($finalTokens[($stackPtr + 1)]['code'] === T_LNUMBER) {
355
-                    // They can also be used to provide negative numbers.
356
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
357
-                        echo "\t\t* token is part of a negative number; adding content to next token and ignoring *".PHP_EOL;
358
-                        $content = Util\Common::prepareForOutput($finalTokens[($stackPtr + 1)]['content']);
359
-                        echo "\t\t=> token ".($stackPtr + 1)." content changed from \"$content\" to \"-$content\"".PHP_EOL;
360
-                    }
361
-
362
-                    $finalTokens[($stackPtr + 1)]['content'] = '-'.$finalTokens[($stackPtr + 1)]['content'];
363
-                    unset($finalTokens[$stackPtr]);
364
-                }//end if
365
-                break;
366
-            case T_COLON:
367
-                // Only interested in colons that are defining styles.
368
-                if ($inStyleDef === false) {
369
-                    break;
370
-                }
371
-
372
-                for ($x = ($stackPtr - 1); $x >= 0; $x--) {
373
-                    if (isset(Util\Tokens::$emptyTokens[$finalTokens[$x]['code']]) === false) {
374
-                        break;
375
-                    }
376
-                }
377
-
378
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
379
-                    $type = $finalTokens[$x]['type'];
380
-                    echo "\t\t=> token $x changed from $type to T_STYLE".PHP_EOL;
381
-                }
382
-
383
-                $finalTokens[$x]['type'] = 'T_STYLE';
384
-                $finalTokens[$x]['code'] = T_STYLE;
385
-                break;
386
-            case T_STRING:
387
-                if (strtolower($token['content']) === 'url') {
388
-                    // Find the next content.
389
-                    for ($x = ($stackPtr + 1); $x < $numTokens; $x++) {
390
-                        if (isset(Util\Tokens::$emptyTokens[$finalTokens[$x]['code']]) === false) {
391
-                            break;
392
-                        }
393
-                    }
394
-
395
-                    // Needs to be in the format "url(" for it to be a URL.
396
-                    if ($finalTokens[$x]['code'] !== T_OPEN_PARENTHESIS) {
397
-                        continue 2;
398
-                    }
399
-
400
-                    // Make sure the content isn't empty.
401
-                    for ($y = ($x + 1); $y < $numTokens; $y++) {
402
-                        if (isset(Util\Tokens::$emptyTokens[$finalTokens[$y]['code']]) === false) {
403
-                            break;
404
-                        }
405
-                    }
406
-
407
-                    if ($finalTokens[$y]['code'] === T_CLOSE_PARENTHESIS) {
408
-                        continue 2;
409
-                    }
410
-
411
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
412
-                        for ($i = ($stackPtr + 1); $i <= $y; $i++) {
413
-                            $type    = $finalTokens[$i]['type'];
414
-                            $content = Util\Common::prepareForOutput($finalTokens[$i]['content']);
415
-                            echo "\tProcess token $i: $type => $content".PHP_EOL;
416
-                        }
417
-
418
-                        echo "\t\t* token starts a URL *".PHP_EOL;
419
-                    }
420
-
421
-                    // Join all the content together inside the url() statement.
422
-                    $newContent = '';
423
-                    for ($i = ($x + 2); $i < $numTokens; $i++) {
424
-                        if ($finalTokens[$i]['code'] === T_CLOSE_PARENTHESIS) {
425
-                            break;
426
-                        }
427
-
428
-                        $newContent .= $finalTokens[$i]['content'];
429
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
430
-                            $content = Util\Common::prepareForOutput($finalTokens[$i]['content']);
431
-                            echo "\t\t=> token $i added to URL string and ignored: $content".PHP_EOL;
432
-                        }
433
-
434
-                        unset($finalTokens[$i]);
435
-                    }
436
-
437
-                    $stackPtr = $i;
438
-
439
-                    // If the content inside the "url()" is in double quotes
440
-                    // there will only be one token and so we don't have to do
441
-                    // anything except change its type. If it is not empty,
442
-                    // we need to do some token merging.
443
-                    $finalTokens[($x + 1)]['type'] = 'T_URL';
444
-                    $finalTokens[($x + 1)]['code'] = T_URL;
445
-
446
-                    if ($newContent !== '') {
447
-                        $finalTokens[($x + 1)]['content'] .= $newContent;
448
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
449
-                            $content = Util\Common::prepareForOutput($finalTokens[($x + 1)]['content']);
450
-                            echo "\t\t=> token content changed to: $content".PHP_EOL;
451
-                        }
452
-                    }
453
-                } else if ($finalTokens[$stackPtr]['content'][0] === '-'
454
-                    && $finalTokens[($stackPtr + 1)]['code'] === T_STRING
455
-                ) {
456
-                    if (isset($finalTokens[($stackPtr - 1)]) === true
457
-                        && $finalTokens[($stackPtr - 1)]['code'] === T_STRING
458
-                    ) {
459
-                        $newContent = $finalTokens[($stackPtr - 1)]['content'].$finalTokens[$stackPtr]['content'].$finalTokens[($stackPtr + 1)]['content'];
460
-
461
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
462
-                            echo "\t\t* token is a string joiner; ignoring this and previous token".PHP_EOL;
463
-                            $old = Util\Common::prepareForOutput($finalTokens[($stackPtr + 1)]['content']);
464
-                            $new = Util\Common::prepareForOutput($newContent);
465
-                            echo "\t\t=> token ".($stackPtr + 1)." content changed from \"$old\" to \"$new\"".PHP_EOL;
466
-                        }
467
-
468
-                        $finalTokens[($stackPtr + 1)]['content'] = $newContent;
469
-                        unset($finalTokens[$stackPtr]);
470
-                        unset($finalTokens[($stackPtr - 1)]);
471
-                    } else {
472
-                        $newContent = $finalTokens[$stackPtr]['content'].$finalTokens[($stackPtr + 1)]['content'];
473
-
474
-                        $finalTokens[($stackPtr + 1)]['content'] = $newContent;
475
-                        unset($finalTokens[$stackPtr]);
476
-                    }
477
-                }//end if
478
-                break;
479
-            case T_ASPERAND:
480
-                $asperandStart = true;
481
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
482
-                    echo "\t\t* at-rule definition opened *".PHP_EOL;
483
-                }
484
-                break;
485
-            default:
486
-                // Nothing special to be done with this token.
487
-                break;
488
-            }//end switch
489
-        }//end for
490
-
491
-        // Reset the array keys to avoid gaps.
492
-        $finalTokens = array_values($finalTokens);
493
-        $numTokens   = count($finalTokens);
494
-
495
-        // Blank out the content of the end tag.
496
-        $finalTokens[($numTokens - 1)]['content'] = '';
497
-
498
-        if ($eolAdded === true) {
499
-            // Strip off the extra EOL char we added for tokenizing.
500
-            $finalTokens[($numTokens - 2)]['content'] = substr(
501
-                $finalTokens[($numTokens - 2)]['content'],
502
-                0,
503
-                (strlen($this->eolChar) * -1)
504
-            );
505
-
506
-            if ($finalTokens[($numTokens - 2)]['content'] === '') {
507
-                unset($finalTokens[($numTokens - 2)]);
508
-                $finalTokens = array_values($finalTokens);
509
-                $numTokens   = count($finalTokens);
510
-            }
511
-        }
512
-
513
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
514
-            echo "\t*** END CSS TOKENIZING 2ND PASS ***".PHP_EOL;
515
-        }
516
-
517
-        return $finalTokens;
518
-
519
-    }//end tokenize()
520
-
521
-
522
-    /**
523
-     * Performs additional processing after main tokenizing.
524
-     *
525
-     * @return void
526
-     */
527
-    public function processAdditional()
528
-    {
529
-        /*
20
+	/**
21
+	 * Initialise the tokenizer.
22
+	 *
23
+	 * Pre-checks the content to see if it looks minified.
24
+	 *
25
+	 * @param string                  $content The content to tokenize,
26
+	 * @param \PHP_CodeSniffer\Config $config  The config data for the run.
27
+	 * @param string                  $eolChar The EOL char used in the content.
28
+	 *
29
+	 * @return void
30
+	 * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the file appears to be minified.
31
+	 */
32
+	public function __construct($content, Config $config, $eolChar='\n')
33
+	{
34
+		if ($this->isMinifiedContent($content, $eolChar) === true) {
35
+			throw new TokenizerException('File appears to be minified and cannot be processed');
36
+		}
37
+
38
+		parent::__construct($content, $config, $eolChar);
39
+
40
+	}//end __construct()
41
+
42
+
43
+	/**
44
+	 * Creates an array of tokens when given some CSS code.
45
+	 *
46
+	 * Uses the PHP tokenizer to do all the tricky work
47
+	 *
48
+	 * @param string $string The string to tokenize.
49
+	 *
50
+	 * @return array
51
+	 */
52
+	public function tokenize($string)
53
+	{
54
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
55
+			echo "\t*** START CSS TOKENIZING 1ST PASS ***".PHP_EOL;
56
+		}
57
+
58
+		// If the content doesn't have an EOL char on the end, add one so
59
+		// the open and close tags we add are parsed correctly.
60
+		$eolAdded = false;
61
+		if (substr($string, (strlen($this->eolChar) * -1)) !== $this->eolChar) {
62
+			$string  .= $this->eolChar;
63
+			$eolAdded = true;
64
+		}
65
+
66
+		$string = str_replace('<?php', '^PHPCS_CSS_T_OPEN_TAG^', $string);
67
+		$string = str_replace('?>', '^PHPCS_CSS_T_CLOSE_TAG^', $string);
68
+		$tokens = parent::tokenize('<?php '.$string.'?>');
69
+
70
+		$finalTokens    = [];
71
+		$finalTokens[0] = [
72
+			'code'    => T_OPEN_TAG,
73
+			'type'    => 'T_OPEN_TAG',
74
+			'content' => '',
75
+		];
76
+
77
+		$newStackPtr      = 1;
78
+		$numTokens        = count($tokens);
79
+		$multiLineComment = false;
80
+		for ($stackPtr = 1; $stackPtr < $numTokens; $stackPtr++) {
81
+			$token = $tokens[$stackPtr];
82
+
83
+			// CSS files don't have lists, breaks etc, so convert these to
84
+			// standard strings early so they can be converted into T_STYLE
85
+			// tokens and joined with other strings if needed.
86
+			if ($token['code'] === T_BREAK
87
+				|| $token['code'] === T_LIST
88
+				|| $token['code'] === T_DEFAULT
89
+				|| $token['code'] === T_SWITCH
90
+				|| $token['code'] === T_FOR
91
+				|| $token['code'] === T_FOREACH
92
+				|| $token['code'] === T_WHILE
93
+				|| $token['code'] === T_DEC
94
+				|| $token['code'] === T_NEW
95
+			) {
96
+				$token['type'] = 'T_STRING';
97
+				$token['code'] = T_STRING;
98
+			}
99
+
100
+			$token['content'] = str_replace('^PHPCS_CSS_T_OPEN_TAG^', '<?php', $token['content']);
101
+			$token['content'] = str_replace('^PHPCS_CSS_T_CLOSE_TAG^', '?>', $token['content']);
102
+
103
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
104
+				$type    = $token['type'];
105
+				$content = Util\Common::prepareForOutput($token['content']);
106
+				echo "\tProcess token $stackPtr: $type => $content".PHP_EOL;
107
+			}
108
+
109
+			if ($token['code'] === T_BITWISE_XOR
110
+				&& $tokens[($stackPtr + 1)]['content'] === 'PHPCS_CSS_T_OPEN_TAG'
111
+			) {
112
+				$content = '<?php';
113
+				for ($stackPtr += 3; $stackPtr < $numTokens; $stackPtr++) {
114
+					if ($tokens[$stackPtr]['code'] === T_BITWISE_XOR
115
+						&& $tokens[($stackPtr + 1)]['content'] === 'PHPCS_CSS_T_CLOSE_TAG'
116
+					) {
117
+						// Add the end tag and ignore the * we put at the end.
118
+						$content  .= '?>';
119
+						$stackPtr += 2;
120
+						break;
121
+					} else {
122
+						$content .= $tokens[$stackPtr]['content'];
123
+					}
124
+				}
125
+
126
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
127
+					echo "\t\t=> Found embedded PHP code: ";
128
+					$cleanContent = Util\Common::prepareForOutput($content);
129
+					echo $cleanContent.PHP_EOL;
130
+				}
131
+
132
+				$finalTokens[$newStackPtr] = [
133
+					'type'    => 'T_EMBEDDED_PHP',
134
+					'code'    => T_EMBEDDED_PHP,
135
+					'content' => $content,
136
+				];
137
+
138
+				$newStackPtr++;
139
+				continue;
140
+			}//end if
141
+
142
+			if ($token['code'] === T_GOTO_LABEL) {
143
+				// Convert these back to T_STRING followed by T_COLON so we can
144
+				// more easily process style definitions.
145
+				$finalTokens[$newStackPtr] = [
146
+					'type'    => 'T_STRING',
147
+					'code'    => T_STRING,
148
+					'content' => substr($token['content'], 0, -1),
149
+				];
150
+				$newStackPtr++;
151
+				$finalTokens[$newStackPtr] = [
152
+					'type'    => 'T_COLON',
153
+					'code'    => T_COLON,
154
+					'content' => ':',
155
+				];
156
+				$newStackPtr++;
157
+				continue;
158
+			}
159
+
160
+			if ($token['code'] === T_FUNCTION) {
161
+				// There are no functions in CSS, so convert this to a string.
162
+				$finalTokens[$newStackPtr] = [
163
+					'type'    => 'T_STRING',
164
+					'code'    => T_STRING,
165
+					'content' => $token['content'],
166
+				];
167
+
168
+				$newStackPtr++;
169
+				continue;
170
+			}
171
+
172
+			if ($token['code'] === T_COMMENT
173
+				&& substr($token['content'], 0, 2) === '/*'
174
+			) {
175
+				// Multi-line comment. Record it so we can ignore other
176
+				// comment tags until we get out of this one.
177
+				$multiLineComment = true;
178
+			}
179
+
180
+			if ($token['code'] === T_COMMENT
181
+				&& $multiLineComment === false
182
+				&& (substr($token['content'], 0, 2) === '//'
183
+				|| $token['content']{0} === '#')
184
+			) {
185
+				$content = ltrim($token['content'], '#/');
186
+
187
+				// Guard against PHP7+ syntax errors by stripping
188
+				// leading zeros so the content doesn't look like an invalid int.
189
+				$leadingZero = false;
190
+				if ($content{0} === '0') {
191
+					$content     = '1'.$content;
192
+					$leadingZero = true;
193
+				}
194
+
195
+				$commentTokens = parent::tokenize('<?php '.$content.'?>');
196
+
197
+				// The first and last tokens are the open/close tags.
198
+				array_shift($commentTokens);
199
+				array_pop($commentTokens);
200
+
201
+				if ($leadingZero === true) {
202
+					$commentTokens[0]['content'] = substr($commentTokens[0]['content'], 1);
203
+					$content = substr($content, 1);
204
+				}
205
+
206
+				if ($token['content']{0} === '#') {
207
+					// The # character is not a comment in CSS files, so
208
+					// determine what it means in this context.
209
+					$firstContent = $commentTokens[0]['content'];
210
+
211
+					// If the first content is just a number, it is probably a
212
+					// colour like 8FB7DB, which PHP splits into 8 and FB7DB.
213
+					if (($commentTokens[0]['code'] === T_LNUMBER
214
+						|| $commentTokens[0]['code'] === T_DNUMBER)
215
+						&& $commentTokens[1]['code'] === T_STRING
216
+					) {
217
+						$firstContent .= $commentTokens[1]['content'];
218
+						array_shift($commentTokens);
219
+					}
220
+
221
+					// If the first content looks like a colour and not a class
222
+					// definition, join the tokens together.
223
+					if (preg_match('/^[ABCDEF0-9]+$/i', $firstContent) === 1
224
+						&& $commentTokens[1]['content'] !== '-'
225
+					) {
226
+						array_shift($commentTokens);
227
+						// Work out what we trimmed off above and remember to re-add it.
228
+						$trimmed = substr($token['content'], 0, (strlen($token['content']) - strlen($content)));
229
+						$finalTokens[$newStackPtr] = [
230
+							'type'    => 'T_COLOUR',
231
+							'code'    => T_COLOUR,
232
+							'content' => $trimmed.$firstContent,
233
+						];
234
+					} else {
235
+						$finalTokens[$newStackPtr] = [
236
+							'type'    => 'T_HASH',
237
+							'code'    => T_HASH,
238
+							'content' => '#',
239
+						];
240
+					}
241
+				} else {
242
+					$finalTokens[$newStackPtr] = [
243
+						'type'    => 'T_STRING',
244
+						'code'    => T_STRING,
245
+						'content' => '//',
246
+					];
247
+				}//end if
248
+
249
+				$newStackPtr++;
250
+
251
+				array_splice($tokens, $stackPtr, 1, $commentTokens);
252
+				$numTokens = count($tokens);
253
+				$stackPtr--;
254
+				continue;
255
+			}//end if
256
+
257
+			if ($token['code'] === T_COMMENT
258
+				&& substr($token['content'], -2) === '*/'
259
+			) {
260
+				// Multi-line comment is done.
261
+				$multiLineComment = false;
262
+			}
263
+
264
+			$finalTokens[$newStackPtr] = $token;
265
+			$newStackPtr++;
266
+		}//end for
267
+
268
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
269
+			echo "\t*** END CSS TOKENIZING 1ST PASS ***".PHP_EOL;
270
+			echo "\t*** START CSS TOKENIZING 2ND PASS ***".PHP_EOL;
271
+		}
272
+
273
+		// A flag to indicate if we are inside a style definition,
274
+		// which is defined using curly braces.
275
+		$inStyleDef = false;
276
+
277
+		// A flag to indicate if an At-rule like "@media" is used, which will result
278
+		// in nested curly brackets.
279
+		$asperandStart = false;
280
+
281
+		$numTokens = count($finalTokens);
282
+		for ($stackPtr = 0; $stackPtr < $numTokens; $stackPtr++) {
283
+			$token = $finalTokens[$stackPtr];
284
+
285
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
286
+				$type    = $token['type'];
287
+				$content = Util\Common::prepareForOutput($token['content']);
288
+				echo "\tProcess token $stackPtr: $type => $content".PHP_EOL;
289
+			}
290
+
291
+			switch ($token['code']) {
292
+			case T_OPEN_CURLY_BRACKET:
293
+				// Opening curly brackets for an At-rule do not start a style
294
+				// definition. We also reset the asperand flag here because the next
295
+				// opening curly bracket could be indeed the start of a style
296
+				// definition.
297
+				if ($asperandStart === true) {
298
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
299
+						if ($inStyleDef === true) {
300
+							echo "\t\t* style definition closed *".PHP_EOL;
301
+						}
302
+
303
+						if ($asperandStart === true) {
304
+							echo "\t\t* at-rule definition closed *".PHP_EOL;
305
+						}
306
+					}
307
+
308
+					$inStyleDef    = false;
309
+					$asperandStart = false;
310
+				} else {
311
+					$inStyleDef = true;
312
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
313
+						echo "\t\t* style definition opened *".PHP_EOL;
314
+					}
315
+				}
316
+				break;
317
+			case T_CLOSE_CURLY_BRACKET:
318
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
319
+					if ($inStyleDef === true) {
320
+						echo "\t\t* style definition closed *".PHP_EOL;
321
+					}
322
+
323
+					if ($asperandStart === true) {
324
+						echo "\t\t* at-rule definition closed *".PHP_EOL;
325
+					}
326
+				}
327
+
328
+				$inStyleDef    = false;
329
+				$asperandStart = false;
330
+				break;
331
+			case T_MINUS:
332
+				// Minus signs are often used instead of spaces inside
333
+				// class names, IDs and styles.
334
+				if ($finalTokens[($stackPtr + 1)]['code'] === T_STRING) {
335
+					if ($finalTokens[($stackPtr - 1)]['code'] === T_STRING) {
336
+						$newContent = $finalTokens[($stackPtr - 1)]['content'].'-'.$finalTokens[($stackPtr + 1)]['content'];
337
+
338
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
339
+							echo "\t\t* token is a string joiner; ignoring this and previous token".PHP_EOL;
340
+							$old = Util\Common::prepareForOutput($finalTokens[($stackPtr + 1)]['content']);
341
+							$new = Util\Common::prepareForOutput($newContent);
342
+							echo "\t\t=> token ".($stackPtr + 1)." content changed from \"$old\" to \"$new\"".PHP_EOL;
343
+						}
344
+
345
+						$finalTokens[($stackPtr + 1)]['content'] = $newContent;
346
+						unset($finalTokens[$stackPtr]);
347
+						unset($finalTokens[($stackPtr - 1)]);
348
+					} else {
349
+						$newContent = '-'.$finalTokens[($stackPtr + 1)]['content'];
350
+
351
+						$finalTokens[($stackPtr + 1)]['content'] = $newContent;
352
+						unset($finalTokens[$stackPtr]);
353
+					}
354
+				} else if ($finalTokens[($stackPtr + 1)]['code'] === T_LNUMBER) {
355
+					// They can also be used to provide negative numbers.
356
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
357
+						echo "\t\t* token is part of a negative number; adding content to next token and ignoring *".PHP_EOL;
358
+						$content = Util\Common::prepareForOutput($finalTokens[($stackPtr + 1)]['content']);
359
+						echo "\t\t=> token ".($stackPtr + 1)." content changed from \"$content\" to \"-$content\"".PHP_EOL;
360
+					}
361
+
362
+					$finalTokens[($stackPtr + 1)]['content'] = '-'.$finalTokens[($stackPtr + 1)]['content'];
363
+					unset($finalTokens[$stackPtr]);
364
+				}//end if
365
+				break;
366
+			case T_COLON:
367
+				// Only interested in colons that are defining styles.
368
+				if ($inStyleDef === false) {
369
+					break;
370
+				}
371
+
372
+				for ($x = ($stackPtr - 1); $x >= 0; $x--) {
373
+					if (isset(Util\Tokens::$emptyTokens[$finalTokens[$x]['code']]) === false) {
374
+						break;
375
+					}
376
+				}
377
+
378
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
379
+					$type = $finalTokens[$x]['type'];
380
+					echo "\t\t=> token $x changed from $type to T_STYLE".PHP_EOL;
381
+				}
382
+
383
+				$finalTokens[$x]['type'] = 'T_STYLE';
384
+				$finalTokens[$x]['code'] = T_STYLE;
385
+				break;
386
+			case T_STRING:
387
+				if (strtolower($token['content']) === 'url') {
388
+					// Find the next content.
389
+					for ($x = ($stackPtr + 1); $x < $numTokens; $x++) {
390
+						if (isset(Util\Tokens::$emptyTokens[$finalTokens[$x]['code']]) === false) {
391
+							break;
392
+						}
393
+					}
394
+
395
+					// Needs to be in the format "url(" for it to be a URL.
396
+					if ($finalTokens[$x]['code'] !== T_OPEN_PARENTHESIS) {
397
+						continue 2;
398
+					}
399
+
400
+					// Make sure the content isn't empty.
401
+					for ($y = ($x + 1); $y < $numTokens; $y++) {
402
+						if (isset(Util\Tokens::$emptyTokens[$finalTokens[$y]['code']]) === false) {
403
+							break;
404
+						}
405
+					}
406
+
407
+					if ($finalTokens[$y]['code'] === T_CLOSE_PARENTHESIS) {
408
+						continue 2;
409
+					}
410
+
411
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
412
+						for ($i = ($stackPtr + 1); $i <= $y; $i++) {
413
+							$type    = $finalTokens[$i]['type'];
414
+							$content = Util\Common::prepareForOutput($finalTokens[$i]['content']);
415
+							echo "\tProcess token $i: $type => $content".PHP_EOL;
416
+						}
417
+
418
+						echo "\t\t* token starts a URL *".PHP_EOL;
419
+					}
420
+
421
+					// Join all the content together inside the url() statement.
422
+					$newContent = '';
423
+					for ($i = ($x + 2); $i < $numTokens; $i++) {
424
+						if ($finalTokens[$i]['code'] === T_CLOSE_PARENTHESIS) {
425
+							break;
426
+						}
427
+
428
+						$newContent .= $finalTokens[$i]['content'];
429
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
430
+							$content = Util\Common::prepareForOutput($finalTokens[$i]['content']);
431
+							echo "\t\t=> token $i added to URL string and ignored: $content".PHP_EOL;
432
+						}
433
+
434
+						unset($finalTokens[$i]);
435
+					}
436
+
437
+					$stackPtr = $i;
438
+
439
+					// If the content inside the "url()" is in double quotes
440
+					// there will only be one token and so we don't have to do
441
+					// anything except change its type. If it is not empty,
442
+					// we need to do some token merging.
443
+					$finalTokens[($x + 1)]['type'] = 'T_URL';
444
+					$finalTokens[($x + 1)]['code'] = T_URL;
445
+
446
+					if ($newContent !== '') {
447
+						$finalTokens[($x + 1)]['content'] .= $newContent;
448
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
449
+							$content = Util\Common::prepareForOutput($finalTokens[($x + 1)]['content']);
450
+							echo "\t\t=> token content changed to: $content".PHP_EOL;
451
+						}
452
+					}
453
+				} else if ($finalTokens[$stackPtr]['content'][0] === '-'
454
+					&& $finalTokens[($stackPtr + 1)]['code'] === T_STRING
455
+				) {
456
+					if (isset($finalTokens[($stackPtr - 1)]) === true
457
+						&& $finalTokens[($stackPtr - 1)]['code'] === T_STRING
458
+					) {
459
+						$newContent = $finalTokens[($stackPtr - 1)]['content'].$finalTokens[$stackPtr]['content'].$finalTokens[($stackPtr + 1)]['content'];
460
+
461
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
462
+							echo "\t\t* token is a string joiner; ignoring this and previous token".PHP_EOL;
463
+							$old = Util\Common::prepareForOutput($finalTokens[($stackPtr + 1)]['content']);
464
+							$new = Util\Common::prepareForOutput($newContent);
465
+							echo "\t\t=> token ".($stackPtr + 1)." content changed from \"$old\" to \"$new\"".PHP_EOL;
466
+						}
467
+
468
+						$finalTokens[($stackPtr + 1)]['content'] = $newContent;
469
+						unset($finalTokens[$stackPtr]);
470
+						unset($finalTokens[($stackPtr - 1)]);
471
+					} else {
472
+						$newContent = $finalTokens[$stackPtr]['content'].$finalTokens[($stackPtr + 1)]['content'];
473
+
474
+						$finalTokens[($stackPtr + 1)]['content'] = $newContent;
475
+						unset($finalTokens[$stackPtr]);
476
+					}
477
+				}//end if
478
+				break;
479
+			case T_ASPERAND:
480
+				$asperandStart = true;
481
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
482
+					echo "\t\t* at-rule definition opened *".PHP_EOL;
483
+				}
484
+				break;
485
+			default:
486
+				// Nothing special to be done with this token.
487
+				break;
488
+			}//end switch
489
+		}//end for
490
+
491
+		// Reset the array keys to avoid gaps.
492
+		$finalTokens = array_values($finalTokens);
493
+		$numTokens   = count($finalTokens);
494
+
495
+		// Blank out the content of the end tag.
496
+		$finalTokens[($numTokens - 1)]['content'] = '';
497
+
498
+		if ($eolAdded === true) {
499
+			// Strip off the extra EOL char we added for tokenizing.
500
+			$finalTokens[($numTokens - 2)]['content'] = substr(
501
+				$finalTokens[($numTokens - 2)]['content'],
502
+				0,
503
+				(strlen($this->eolChar) * -1)
504
+			);
505
+
506
+			if ($finalTokens[($numTokens - 2)]['content'] === '') {
507
+				unset($finalTokens[($numTokens - 2)]);
508
+				$finalTokens = array_values($finalTokens);
509
+				$numTokens   = count($finalTokens);
510
+			}
511
+		}
512
+
513
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
514
+			echo "\t*** END CSS TOKENIZING 2ND PASS ***".PHP_EOL;
515
+		}
516
+
517
+		return $finalTokens;
518
+
519
+	}//end tokenize()
520
+
521
+
522
+	/**
523
+	 * Performs additional processing after main tokenizing.
524
+	 *
525
+	 * @return void
526
+	 */
527
+	public function processAdditional()
528
+	{
529
+		/*
530 530
             We override this method because we don't want the PHP version to
531 531
             run during CSS processing because it is wasted processing time.
532 532
         */
533 533
 
534
-    }//end processAdditional()
534
+	}//end processAdditional()
535 535
 
536 536
 
537 537
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Tokenizers/JS.php 1 patch
Indentation   +1220 added lines, -1220 removed lines patch added patch discarded remove patch
@@ -17,1241 +17,1241 @@
 block discarded – undo
17 17
 {
18 18
 
19 19
 
20
-    /**
21
-     * A list of tokens that are allowed to open a scope.
22
-     *
23
-     * This array also contains information about what kind of token the scope
24
-     * opener uses to open and close the scope, if the token strictly requires
25
-     * an opener, if the token can share a scope closer, and who it can be shared
26
-     * with. An example of a token that shares a scope closer is a CASE scope.
27
-     *
28
-     * @var array
29
-     */
30
-    public $scopeOpeners = [
31
-        T_IF       => [
32
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
33
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
34
-            'strict' => false,
35
-            'shared' => false,
36
-            'with'   => [],
37
-        ],
38
-        T_TRY      => [
39
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
40
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
41
-            'strict' => true,
42
-            'shared' => false,
43
-            'with'   => [],
44
-        ],
45
-        T_CATCH    => [
46
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
47
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
48
-            'strict' => true,
49
-            'shared' => false,
50
-            'with'   => [],
51
-        ],
52
-        T_ELSE     => [
53
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
54
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
55
-            'strict' => false,
56
-            'shared' => false,
57
-            'with'   => [],
58
-        ],
59
-        T_FOR      => [
60
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
61
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
62
-            'strict' => false,
63
-            'shared' => false,
64
-            'with'   => [],
65
-        ],
66
-        T_CLASS    => [
67
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
68
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
69
-            'strict' => true,
70
-            'shared' => false,
71
-            'with'   => [],
72
-        ],
73
-        T_FUNCTION => [
74
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
75
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
76
-            'strict' => false,
77
-            'shared' => false,
78
-            'with'   => [],
79
-        ],
80
-        T_WHILE    => [
81
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
82
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
83
-            'strict' => false,
84
-            'shared' => false,
85
-            'with'   => [],
86
-        ],
87
-        T_DO       => [
88
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
89
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
90
-            'strict' => true,
91
-            'shared' => false,
92
-            'with'   => [],
93
-        ],
94
-        T_SWITCH   => [
95
-            'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
96
-            'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
97
-            'strict' => true,
98
-            'shared' => false,
99
-            'with'   => [],
100
-        ],
101
-        T_CASE     => [
102
-            'start'  => [T_COLON => T_COLON],
103
-            'end'    => [
104
-                T_BREAK    => T_BREAK,
105
-                T_RETURN   => T_RETURN,
106
-                T_CONTINUE => T_CONTINUE,
107
-                T_THROW    => T_THROW,
108
-            ],
109
-            'strict' => true,
110
-            'shared' => true,
111
-            'with'   => [
112
-                T_DEFAULT => T_DEFAULT,
113
-                T_CASE    => T_CASE,
114
-                T_SWITCH  => T_SWITCH,
115
-            ],
116
-        ],
117
-        T_DEFAULT  => [
118
-            'start'  => [T_COLON => T_COLON],
119
-            'end'    => [
120
-                T_BREAK    => T_BREAK,
121
-                T_RETURN   => T_RETURN,
122
-                T_CONTINUE => T_CONTINUE,
123
-                T_THROW    => T_THROW,
124
-            ],
125
-            'strict' => true,
126
-            'shared' => true,
127
-            'with'   => [
128
-                T_CASE   => T_CASE,
129
-                T_SWITCH => T_SWITCH,
130
-            ],
131
-        ],
132
-    ];
133
-
134
-    /**
135
-     * A list of tokens that end the scope.
136
-     *
137
-     * This array is just a unique collection of the end tokens
138
-     * from the _scopeOpeners array. The data is duplicated here to
139
-     * save time during parsing of the file.
140
-     *
141
-     * @var array
142
-     */
143
-    public $endScopeTokens = [
144
-        T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
145
-        T_BREAK               => T_BREAK,
146
-    ];
147
-
148
-    /**
149
-     * A list of special JS tokens and their types.
150
-     *
151
-     * @var array
152
-     */
153
-    protected $tokenValues = [
154
-        'class'     => 'T_CLASS',
155
-        'function'  => 'T_FUNCTION',
156
-        'prototype' => 'T_PROTOTYPE',
157
-        'try'       => 'T_TRY',
158
-        'catch'     => 'T_CATCH',
159
-        'return'    => 'T_RETURN',
160
-        'throw'     => 'T_THROW',
161
-        'break'     => 'T_BREAK',
162
-        'switch'    => 'T_SWITCH',
163
-        'continue'  => 'T_CONTINUE',
164
-        'if'        => 'T_IF',
165
-        'else'      => 'T_ELSE',
166
-        'do'        => 'T_DO',
167
-        'while'     => 'T_WHILE',
168
-        'for'       => 'T_FOR',
169
-        'var'       => 'T_VAR',
170
-        'case'      => 'T_CASE',
171
-        'default'   => 'T_DEFAULT',
172
-        'true'      => 'T_TRUE',
173
-        'false'     => 'T_FALSE',
174
-        'null'      => 'T_NULL',
175
-        'this'      => 'T_THIS',
176
-        'typeof'    => 'T_TYPEOF',
177
-        '('         => 'T_OPEN_PARENTHESIS',
178
-        ')'         => 'T_CLOSE_PARENTHESIS',
179
-        '{'         => 'T_OPEN_CURLY_BRACKET',
180
-        '}'         => 'T_CLOSE_CURLY_BRACKET',
181
-        '['         => 'T_OPEN_SQUARE_BRACKET',
182
-        ']'         => 'T_CLOSE_SQUARE_BRACKET',
183
-        '?'         => 'T_INLINE_THEN',
184
-        '.'         => 'T_OBJECT_OPERATOR',
185
-        '+'         => 'T_PLUS',
186
-        '-'         => 'T_MINUS',
187
-        '*'         => 'T_MULTIPLY',
188
-        '%'         => 'T_MODULUS',
189
-        '/'         => 'T_DIVIDE',
190
-        '^'         => 'T_LOGICAL_XOR',
191
-        ','         => 'T_COMMA',
192
-        ';'         => 'T_SEMICOLON',
193
-        ':'         => 'T_COLON',
194
-        '<'         => 'T_LESS_THAN',
195
-        '>'         => 'T_GREATER_THAN',
196
-        '<<'        => 'T_SL',
197
-        '>>'        => 'T_SR',
198
-        '>>>'       => 'T_ZSR',
199
-        '<<='       => 'T_SL_EQUAL',
200
-        '>>='       => 'T_SR_EQUAL',
201
-        '>>>='      => 'T_ZSR_EQUAL',
202
-        '<='        => 'T_IS_SMALLER_OR_EQUAL',
203
-        '>='        => 'T_IS_GREATER_OR_EQUAL',
204
-        '=>'        => 'T_DOUBLE_ARROW',
205
-        '!'         => 'T_BOOLEAN_NOT',
206
-        '||'        => 'T_BOOLEAN_OR',
207
-        '&&'        => 'T_BOOLEAN_AND',
208
-        '|'         => 'T_BITWISE_OR',
209
-        '&'         => 'T_BITWISE_AND',
210
-        '!='        => 'T_IS_NOT_EQUAL',
211
-        '!=='       => 'T_IS_NOT_IDENTICAL',
212
-        '='         => 'T_EQUAL',
213
-        '=='        => 'T_IS_EQUAL',
214
-        '==='       => 'T_IS_IDENTICAL',
215
-        '-='        => 'T_MINUS_EQUAL',
216
-        '+='        => 'T_PLUS_EQUAL',
217
-        '*='        => 'T_MUL_EQUAL',
218
-        '/='        => 'T_DIV_EQUAL',
219
-        '%='        => 'T_MOD_EQUAL',
220
-        '++'        => 'T_INC',
221
-        '--'        => 'T_DEC',
222
-        '//'        => 'T_COMMENT',
223
-        '/*'        => 'T_COMMENT',
224
-        '/**'       => 'T_DOC_COMMENT',
225
-        '*/'        => 'T_COMMENT',
226
-    ];
227
-
228
-    /**
229
-     * A list string delimiters.
230
-     *
231
-     * @var array
232
-     */
233
-    protected $stringTokens = [
234
-        '\'' => '\'',
235
-        '"'  => '"',
236
-    ];
237
-
238
-    /**
239
-     * A list tokens that start and end comments.
240
-     *
241
-     * @var array
242
-     */
243
-    protected $commentTokens = [
244
-        '//'  => null,
245
-        '/*'  => '*/',
246
-        '/**' => '*/',
247
-    ];
248
-
249
-
250
-    /**
251
-     * Initialise the tokenizer.
252
-     *
253
-     * Pre-checks the content to see if it looks minified.
254
-     *
255
-     * @param string                  $content The content to tokenize,
256
-     * @param \PHP_CodeSniffer\Config $config  The config data for the run.
257
-     * @param string                  $eolChar The EOL char used in the content.
258
-     *
259
-     * @return void
260
-     * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the file appears to be minified.
261
-     */
262
-    public function __construct($content, Config $config, $eolChar='\n')
263
-    {
264
-        if ($this->isMinifiedContent($content, $eolChar) === true) {
265
-            throw new TokenizerException('File appears to be minified and cannot be processed');
266
-        }
267
-
268
-        parent::__construct($content, $config, $eolChar);
269
-
270
-    }//end __construct()
271
-
272
-
273
-    /**
274
-     * Creates an array of tokens when given some JS code.
275
-     *
276
-     * @param string $string The string to tokenize.
277
-     *
278
-     * @return array
279
-     */
280
-    public function tokenize($string)
281
-    {
282
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
283
-            echo "\t*** START JS TOKENIZING ***".PHP_EOL;
284
-        }
285
-
286
-        $maxTokenLength = 0;
287
-        foreach ($this->tokenValues as $token => $values) {
288
-            if (strlen($token) > $maxTokenLength) {
289
-                $maxTokenLength = strlen($token);
290
-            }
291
-        }
292
-
293
-        $tokens          = [];
294
-        $inString        = '';
295
-        $stringChar      = null;
296
-        $inComment       = '';
297
-        $buffer          = '';
298
-        $preStringBuffer = '';
299
-        $cleanBuffer     = false;
300
-
301
-        $commentTokenizer = new Comment();
302
-
303
-        $tokens[] = [
304
-            'code'    => T_OPEN_TAG,
305
-            'type'    => 'T_OPEN_TAG',
306
-            'content' => '',
307
-        ];
308
-
309
-        // Convert newlines to single characters for ease of
310
-        // processing. We will change them back later.
311
-        $string = str_replace($this->eolChar, "\n", $string);
312
-
313
-        $chars    = str_split($string);
314
-        $numChars = count($chars);
315
-        for ($i = 0; $i < $numChars; $i++) {
316
-            $char = $chars[$i];
317
-
318
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
319
-                $content       = Util\Common::prepareForOutput($char);
320
-                $bufferContent = Util\Common::prepareForOutput($buffer);
321
-
322
-                if ($inString !== '') {
323
-                    echo "\t";
324
-                }
325
-
326
-                if ($inComment !== '') {
327
-                    echo "\t";
328
-                }
329
-
330
-                echo "\tProcess char $i => $content (buffer: $bufferContent)".PHP_EOL;
331
-            }//end if
332
-
333
-            if ($inString === '' && $inComment === '' && $buffer !== '') {
334
-                // If the buffer only has whitespace and we are about to
335
-                // add a character, store the whitespace first.
336
-                if (trim($char) !== '' && trim($buffer) === '') {
337
-                    $tokens[] = [
338
-                        'code'    => T_WHITESPACE,
339
-                        'type'    => 'T_WHITESPACE',
340
-                        'content' => str_replace("\n", $this->eolChar, $buffer),
341
-                    ];
342
-
343
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
344
-                        $content = Util\Common::prepareForOutput($buffer);
345
-                        echo "\t=> Added token T_WHITESPACE ($content)".PHP_EOL;
346
-                    }
347
-
348
-                    $buffer = '';
349
-                }
350
-
351
-                // If the buffer is not whitespace and we are about to
352
-                // add a whitespace character, store the content first.
353
-                if ($inString === ''
354
-                    && $inComment === ''
355
-                    && trim($char) === ''
356
-                    && trim($buffer) !== ''
357
-                ) {
358
-                    $tokens[] = [
359
-                        'code'    => T_STRING,
360
-                        'type'    => 'T_STRING',
361
-                        'content' => str_replace("\n", $this->eolChar, $buffer),
362
-                    ];
363
-
364
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
365
-                        $content = Util\Common::prepareForOutput($buffer);
366
-                        echo "\t=> Added token T_STRING ($content)".PHP_EOL;
367
-                    }
368
-
369
-                    $buffer = '';
370
-                }
371
-            }//end if
372
-
373
-            // Process strings.
374
-            if ($inComment === '' && isset($this->stringTokens[$char]) === true) {
375
-                if ($inString === $char) {
376
-                    // This could be the end of the string, but make sure it
377
-                    // is not escaped first.
378
-                    $escapes = 0;
379
-                    for ($x = ($i - 1); $x >= 0; $x--) {
380
-                        if ($chars[$x] !== '\\') {
381
-                            break;
382
-                        }
383
-
384
-                        $escapes++;
385
-                    }
386
-
387
-                    if ($escapes === 0 || ($escapes % 2) === 0) {
388
-                        // There is an even number escape chars,
389
-                        // so this is not escaped, it is the end of the string.
390
-                        $tokens[] = [
391
-                            'code'    => T_CONSTANT_ENCAPSED_STRING,
392
-                            'type'    => 'T_CONSTANT_ENCAPSED_STRING',
393
-                            'content' => str_replace("\n", $this->eolChar, $buffer).$char,
394
-                        ];
395
-
396
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
397
-                            echo "\t\t* found end of string *".PHP_EOL;
398
-                            $content = Util\Common::prepareForOutput($buffer.$char);
399
-                            echo "\t=> Added token T_CONSTANT_ENCAPSED_STRING ($content)".PHP_EOL;
400
-                        }
401
-
402
-                        $buffer          = '';
403
-                        $preStringBuffer = '';
404
-                        $inString        = '';
405
-                        $stringChar      = null;
406
-                        continue;
407
-                    }//end if
408
-                } else if ($inString === '') {
409
-                    $inString        = $char;
410
-                    $stringChar      = $i;
411
-                    $preStringBuffer = $buffer;
412
-
413
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
414
-                        echo "\t\t* looking for string closer *".PHP_EOL;
415
-                    }
416
-                }//end if
417
-            }//end if
418
-
419
-            if ($inString !== '' && $char === "\n") {
420
-                // Unless this newline character is escaped, the string did not
421
-                // end before the end of the line, which means it probably
422
-                // wasn't a string at all (maybe a regex).
423
-                if ($chars[($i - 1)] !== '\\') {
424
-                    $i      = $stringChar;
425
-                    $buffer = $preStringBuffer;
426
-                    $preStringBuffer = '';
427
-                    $inString        = '';
428
-                    $stringChar      = null;
429
-                    $char            = $chars[$i];
430
-
431
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
432
-                        echo "\t\t* found newline before end of string, bailing *".PHP_EOL;
433
-                    }
434
-                }
435
-            }
436
-
437
-            $buffer .= $char;
438
-
439
-            // We don't look for special tokens inside strings,
440
-            // so if we are in a string, we can continue here now
441
-            // that the current char is in the buffer.
442
-            if ($inString !== '') {
443
-                continue;
444
-            }
445
-
446
-            // Special case for T_DIVIDE which can actually be
447
-            // the start of a regular expression.
448
-            if ($buffer === $char && $char === '/' && $chars[($i + 1)] !== '*') {
449
-                $regex = $this->getRegexToken($i, $string, $chars, $tokens);
450
-                if ($regex !== null) {
451
-                    $tokens[] = [
452
-                        'code'    => T_REGULAR_EXPRESSION,
453
-                        'type'    => 'T_REGULAR_EXPRESSION',
454
-                        'content' => $regex['content'],
455
-                    ];
456
-
457
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
458
-                        $content = Util\Common::prepareForOutput($regex['content']);
459
-                        echo "\t=> Added token T_REGULAR_EXPRESSION ($content)".PHP_EOL;
460
-                    }
461
-
462
-                    $i           = $regex['end'];
463
-                    $buffer      = '';
464
-                    $cleanBuffer = false;
465
-                    continue;
466
-                }//end if
467
-            }//end if
468
-
469
-            // Check for known tokens, but ignore tokens found that are not at
470
-            // the end of a string, like FOR and this.FORmat.
471
-            if (isset($this->tokenValues[strtolower($buffer)]) === true
472
-                && (preg_match('|[a-zA-z0-9_]|', $char) === 0
473
-                || isset($chars[($i + 1)]) === false
474
-                || preg_match('|[a-zA-z0-9_]|', $chars[($i + 1)]) === 0)
475
-            ) {
476
-                $matchedToken    = false;
477
-                $lookAheadLength = ($maxTokenLength - strlen($buffer));
478
-
479
-                if ($lookAheadLength > 0) {
480
-                    // The buffer contains a token type, but we need
481
-                    // to look ahead at the next chars to see if this is
482
-                    // actually part of a larger token. For example,
483
-                    // FOR and FOREACH.
484
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
485
-                        echo "\t\t* buffer possibly contains token, looking ahead $lookAheadLength chars *".PHP_EOL;
486
-                    }
487
-
488
-                    $charBuffer = $buffer;
489
-                    for ($x = 1; $x <= $lookAheadLength; $x++) {
490
-                        if (isset($chars[($i + $x)]) === false) {
491
-                            break;
492
-                        }
493
-
494
-                        $charBuffer .= $chars[($i + $x)];
495
-
496
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
497
-                            $content = Util\Common::prepareForOutput($charBuffer);
498
-                            echo "\t\t=> Looking ahead $x chars => $content".PHP_EOL;
499
-                        }
500
-
501
-                        if (isset($this->tokenValues[strtolower($charBuffer)]) === true) {
502
-                            // We've found something larger that matches
503
-                            // so we can ignore this char. Except for 1 very specific
504
-                            // case where a comment like /**/ needs to tokenize as
505
-                            // T_COMMENT and not T_DOC_COMMENT.
506
-                            $oldType = $this->tokenValues[strtolower($buffer)];
507
-                            $newType = $this->tokenValues[strtolower($charBuffer)];
508
-                            if ($oldType === 'T_COMMENT'
509
-                                && $newType === 'T_DOC_COMMENT'
510
-                                && $chars[($i + $x + 1)] === '/'
511
-                            ) {
512
-                                if (PHP_CODESNIFFER_VERBOSITY > 1) {
513
-                                    echo "\t\t* look ahead ignored T_DOC_COMMENT, continuing *".PHP_EOL;
514
-                                }
515
-                            } else {
516
-                                if (PHP_CODESNIFFER_VERBOSITY > 1) {
517
-                                    echo "\t\t* look ahead found more specific token ($newType), ignoring $i *".PHP_EOL;
518
-                                }
519
-
520
-                                $matchedToken = true;
521
-                                break;
522
-                            }
523
-                        }//end if
524
-                    }//end for
525
-                }//end if
526
-
527
-                if ($matchedToken === false) {
528
-                    if (PHP_CODESNIFFER_VERBOSITY > 1 && $lookAheadLength > 0) {
529
-                        echo "\t\t* look ahead found nothing *".PHP_EOL;
530
-                    }
531
-
532
-                    $value = $this->tokenValues[strtolower($buffer)];
533
-
534
-                    if ($value === 'T_FUNCTION' && $buffer !== 'function') {
535
-                        // The function keyword needs to be all lowercase or else
536
-                        // it is just a function called "Function".
537
-                        $value = 'T_STRING';
538
-                    }
539
-
540
-                    $tokens[] = [
541
-                        'code'    => constant($value),
542
-                        'type'    => $value,
543
-                        'content' => $buffer,
544
-                    ];
545
-
546
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
547
-                        $content = Util\Common::prepareForOutput($buffer);
548
-                        echo "\t=> Added token $value ($content)".PHP_EOL;
549
-                    }
550
-
551
-                    $cleanBuffer = true;
552
-                }//end if
553
-            } else if (isset($this->tokenValues[strtolower($char)]) === true) {
554
-                // No matter what token we end up using, we don't
555
-                // need the content in the buffer any more because we have
556
-                // found a valid token.
557
-                $newContent = substr(str_replace("\n", $this->eolChar, $buffer), 0, -1);
558
-                if ($newContent !== '') {
559
-                    $tokens[] = [
560
-                        'code'    => T_STRING,
561
-                        'type'    => 'T_STRING',
562
-                        'content' => $newContent,
563
-                    ];
564
-
565
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
566
-                        $content = Util\Common::prepareForOutput(substr($buffer, 0, -1));
567
-                        echo "\t=> Added token T_STRING ($content)".PHP_EOL;
568
-                    }
569
-                }
570
-
571
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
572
-                    echo "\t\t* char is token, looking ahead ".($maxTokenLength - 1).' chars *'.PHP_EOL;
573
-                }
574
-
575
-                // The char is a token type, but we need to look ahead at the
576
-                // next chars to see if this is actually part of a larger token.
577
-                // For example, = and ===.
578
-                $charBuffer   = $char;
579
-                $matchedToken = false;
580
-                for ($x = 1; $x <= $maxTokenLength; $x++) {
581
-                    if (isset($chars[($i + $x)]) === false) {
582
-                        break;
583
-                    }
584
-
585
-                    $charBuffer .= $chars[($i + $x)];
586
-
587
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
588
-                        $content = Util\Common::prepareForOutput($charBuffer);
589
-                        echo "\t\t=> Looking ahead $x chars => $content".PHP_EOL;
590
-                    }
591
-
592
-                    if (isset($this->tokenValues[strtolower($charBuffer)]) === true) {
593
-                        // We've found something larger that matches
594
-                        // so we can ignore this char.
595
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
596
-                            $type = $this->tokenValues[strtolower($charBuffer)];
597
-                            echo "\t\t* look ahead found more specific token ($type), ignoring $i *".PHP_EOL;
598
-                        }
599
-
600
-                        $matchedToken = true;
601
-                        break;
602
-                    }
603
-                }//end for
604
-
605
-                if ($matchedToken === false) {
606
-                    $value    = $this->tokenValues[strtolower($char)];
607
-                    $tokens[] = [
608
-                        'code'    => constant($value),
609
-                        'type'    => $value,
610
-                        'content' => $char,
611
-                    ];
612
-
613
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
614
-                        echo "\t\t* look ahead found nothing *".PHP_EOL;
615
-                        $content = Util\Common::prepareForOutput($char);
616
-                        echo "\t=> Added token $value ($content)".PHP_EOL;
617
-                    }
618
-
619
-                    $cleanBuffer = true;
620
-                } else {
621
-                    $buffer = $char;
622
-                }//end if
623
-            }//end if
624
-
625
-            // Keep track of content inside comments.
626
-            if ($inComment === ''
627
-                && array_key_exists($buffer, $this->commentTokens) === true
628
-            ) {
629
-                // This is not really a comment if the content
630
-                // looks like \// (i.e., it is escaped).
631
-                if (isset($chars[($i - 2)]) === true && $chars[($i - 2)] === '\\') {
632
-                    $lastToken   = array_pop($tokens);
633
-                    $lastContent = $lastToken['content'];
634
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
635
-                        $value   = $this->tokenValues[strtolower($lastContent)];
636
-                        $content = Util\Common::prepareForOutput($lastContent);
637
-                        echo "\t=> Removed token $value ($content)".PHP_EOL;
638
-                    }
639
-
640
-                    $lastChars    = str_split($lastContent);
641
-                    $lastNumChars = count($lastChars);
642
-                    for ($x = 0; $x < $lastNumChars; $x++) {
643
-                        $lastChar = $lastChars[$x];
644
-                        $value    = $this->tokenValues[strtolower($lastChar)];
645
-                        $tokens[] = [
646
-                            'code'    => constant($value),
647
-                            'type'    => $value,
648
-                            'content' => $lastChar,
649
-                        ];
650
-
651
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
652
-                            $content = Util\Common::prepareForOutput($lastChar);
653
-                            echo "\t=> Added token $value ($content)".PHP_EOL;
654
-                        }
655
-                    }
656
-                } else {
657
-                    // We have started a comment.
658
-                    $inComment = $buffer;
659
-
660
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
661
-                        echo "\t\t* looking for end of comment *".PHP_EOL;
662
-                    }
663
-                }//end if
664
-            } else if ($inComment !== '') {
665
-                if ($this->commentTokens[$inComment] === null) {
666
-                    // Comment ends at the next newline.
667
-                    if (strpos($buffer, "\n") !== false) {
668
-                        $inComment = '';
669
-                    }
670
-                } else {
671
-                    if ($this->commentTokens[$inComment] === $buffer) {
672
-                        $inComment = '';
673
-                    }
674
-                }
675
-
676
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
677
-                    if ($inComment === '') {
678
-                        echo "\t\t* found end of comment *".PHP_EOL;
679
-                    }
680
-                }
681
-
682
-                if ($inComment === '' && $cleanBuffer === false) {
683
-                    $tokens[] = [
684
-                        'code'    => T_STRING,
685
-                        'type'    => 'T_STRING',
686
-                        'content' => str_replace("\n", $this->eolChar, $buffer),
687
-                    ];
688
-
689
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
690
-                        $content = Util\Common::prepareForOutput($buffer);
691
-                        echo "\t=> Added token T_STRING ($content)".PHP_EOL;
692
-                    }
693
-
694
-                    $buffer = '';
695
-                }
696
-            }//end if
697
-
698
-            if ($cleanBuffer === true) {
699
-                $buffer      = '';
700
-                $cleanBuffer = false;
701
-            }
702
-        }//end for
703
-
704
-        if (empty($buffer) === false) {
705
-            if ($inString !== '') {
706
-                // The string did not end before the end of the file,
707
-                // which means there was probably a syntax error somewhere.
708
-                $tokens[] = [
709
-                    'code'    => T_STRING,
710
-                    'type'    => 'T_STRING',
711
-                    'content' => str_replace("\n", $this->eolChar, $buffer),
712
-                ];
713
-
714
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
715
-                    $content = Util\Common::prepareForOutput($buffer);
716
-                    echo "\t=> Added token T_STRING ($content)".PHP_EOL;
717
-                }
718
-            } else {
719
-                // Buffer contains whitespace from the end of the file.
720
-                $tokens[] = [
721
-                    'code'    => T_WHITESPACE,
722
-                    'type'    => 'T_WHITESPACE',
723
-                    'content' => str_replace("\n", $this->eolChar, $buffer),
724
-                ];
725
-
726
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
727
-                    $content = Util\Common::prepareForOutput($buffer);
728
-                    echo "\t=> Added token T_WHITESPACE ($content)".PHP_EOL;
729
-                }
730
-            }//end if
731
-        }//end if
732
-
733
-        $tokens[] = [
734
-            'code'    => T_CLOSE_TAG,
735
-            'type'    => 'T_CLOSE_TAG',
736
-            'content' => '',
737
-        ];
738
-
739
-        /*
20
+	/**
21
+	 * A list of tokens that are allowed to open a scope.
22
+	 *
23
+	 * This array also contains information about what kind of token the scope
24
+	 * opener uses to open and close the scope, if the token strictly requires
25
+	 * an opener, if the token can share a scope closer, and who it can be shared
26
+	 * with. An example of a token that shares a scope closer is a CASE scope.
27
+	 *
28
+	 * @var array
29
+	 */
30
+	public $scopeOpeners = [
31
+		T_IF       => [
32
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
33
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
34
+			'strict' => false,
35
+			'shared' => false,
36
+			'with'   => [],
37
+		],
38
+		T_TRY      => [
39
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
40
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
41
+			'strict' => true,
42
+			'shared' => false,
43
+			'with'   => [],
44
+		],
45
+		T_CATCH    => [
46
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
47
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
48
+			'strict' => true,
49
+			'shared' => false,
50
+			'with'   => [],
51
+		],
52
+		T_ELSE     => [
53
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
54
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
55
+			'strict' => false,
56
+			'shared' => false,
57
+			'with'   => [],
58
+		],
59
+		T_FOR      => [
60
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
61
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
62
+			'strict' => false,
63
+			'shared' => false,
64
+			'with'   => [],
65
+		],
66
+		T_CLASS    => [
67
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
68
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
69
+			'strict' => true,
70
+			'shared' => false,
71
+			'with'   => [],
72
+		],
73
+		T_FUNCTION => [
74
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
75
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
76
+			'strict' => false,
77
+			'shared' => false,
78
+			'with'   => [],
79
+		],
80
+		T_WHILE    => [
81
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
82
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
83
+			'strict' => false,
84
+			'shared' => false,
85
+			'with'   => [],
86
+		],
87
+		T_DO       => [
88
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
89
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
90
+			'strict' => true,
91
+			'shared' => false,
92
+			'with'   => [],
93
+		],
94
+		T_SWITCH   => [
95
+			'start'  => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET],
96
+			'end'    => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET],
97
+			'strict' => true,
98
+			'shared' => false,
99
+			'with'   => [],
100
+		],
101
+		T_CASE     => [
102
+			'start'  => [T_COLON => T_COLON],
103
+			'end'    => [
104
+				T_BREAK    => T_BREAK,
105
+				T_RETURN   => T_RETURN,
106
+				T_CONTINUE => T_CONTINUE,
107
+				T_THROW    => T_THROW,
108
+			],
109
+			'strict' => true,
110
+			'shared' => true,
111
+			'with'   => [
112
+				T_DEFAULT => T_DEFAULT,
113
+				T_CASE    => T_CASE,
114
+				T_SWITCH  => T_SWITCH,
115
+			],
116
+		],
117
+		T_DEFAULT  => [
118
+			'start'  => [T_COLON => T_COLON],
119
+			'end'    => [
120
+				T_BREAK    => T_BREAK,
121
+				T_RETURN   => T_RETURN,
122
+				T_CONTINUE => T_CONTINUE,
123
+				T_THROW    => T_THROW,
124
+			],
125
+			'strict' => true,
126
+			'shared' => true,
127
+			'with'   => [
128
+				T_CASE   => T_CASE,
129
+				T_SWITCH => T_SWITCH,
130
+			],
131
+		],
132
+	];
133
+
134
+	/**
135
+	 * A list of tokens that end the scope.
136
+	 *
137
+	 * This array is just a unique collection of the end tokens
138
+	 * from the _scopeOpeners array. The data is duplicated here to
139
+	 * save time during parsing of the file.
140
+	 *
141
+	 * @var array
142
+	 */
143
+	public $endScopeTokens = [
144
+		T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
145
+		T_BREAK               => T_BREAK,
146
+	];
147
+
148
+	/**
149
+	 * A list of special JS tokens and their types.
150
+	 *
151
+	 * @var array
152
+	 */
153
+	protected $tokenValues = [
154
+		'class'     => 'T_CLASS',
155
+		'function'  => 'T_FUNCTION',
156
+		'prototype' => 'T_PROTOTYPE',
157
+		'try'       => 'T_TRY',
158
+		'catch'     => 'T_CATCH',
159
+		'return'    => 'T_RETURN',
160
+		'throw'     => 'T_THROW',
161
+		'break'     => 'T_BREAK',
162
+		'switch'    => 'T_SWITCH',
163
+		'continue'  => 'T_CONTINUE',
164
+		'if'        => 'T_IF',
165
+		'else'      => 'T_ELSE',
166
+		'do'        => 'T_DO',
167
+		'while'     => 'T_WHILE',
168
+		'for'       => 'T_FOR',
169
+		'var'       => 'T_VAR',
170
+		'case'      => 'T_CASE',
171
+		'default'   => 'T_DEFAULT',
172
+		'true'      => 'T_TRUE',
173
+		'false'     => 'T_FALSE',
174
+		'null'      => 'T_NULL',
175
+		'this'      => 'T_THIS',
176
+		'typeof'    => 'T_TYPEOF',
177
+		'('         => 'T_OPEN_PARENTHESIS',
178
+		')'         => 'T_CLOSE_PARENTHESIS',
179
+		'{'         => 'T_OPEN_CURLY_BRACKET',
180
+		'}'         => 'T_CLOSE_CURLY_BRACKET',
181
+		'['         => 'T_OPEN_SQUARE_BRACKET',
182
+		']'         => 'T_CLOSE_SQUARE_BRACKET',
183
+		'?'         => 'T_INLINE_THEN',
184
+		'.'         => 'T_OBJECT_OPERATOR',
185
+		'+'         => 'T_PLUS',
186
+		'-'         => 'T_MINUS',
187
+		'*'         => 'T_MULTIPLY',
188
+		'%'         => 'T_MODULUS',
189
+		'/'         => 'T_DIVIDE',
190
+		'^'         => 'T_LOGICAL_XOR',
191
+		','         => 'T_COMMA',
192
+		';'         => 'T_SEMICOLON',
193
+		':'         => 'T_COLON',
194
+		'<'         => 'T_LESS_THAN',
195
+		'>'         => 'T_GREATER_THAN',
196
+		'<<'        => 'T_SL',
197
+		'>>'        => 'T_SR',
198
+		'>>>'       => 'T_ZSR',
199
+		'<<='       => 'T_SL_EQUAL',
200
+		'>>='       => 'T_SR_EQUAL',
201
+		'>>>='      => 'T_ZSR_EQUAL',
202
+		'<='        => 'T_IS_SMALLER_OR_EQUAL',
203
+		'>='        => 'T_IS_GREATER_OR_EQUAL',
204
+		'=>'        => 'T_DOUBLE_ARROW',
205
+		'!'         => 'T_BOOLEAN_NOT',
206
+		'||'        => 'T_BOOLEAN_OR',
207
+		'&&'        => 'T_BOOLEAN_AND',
208
+		'|'         => 'T_BITWISE_OR',
209
+		'&'         => 'T_BITWISE_AND',
210
+		'!='        => 'T_IS_NOT_EQUAL',
211
+		'!=='       => 'T_IS_NOT_IDENTICAL',
212
+		'='         => 'T_EQUAL',
213
+		'=='        => 'T_IS_EQUAL',
214
+		'==='       => 'T_IS_IDENTICAL',
215
+		'-='        => 'T_MINUS_EQUAL',
216
+		'+='        => 'T_PLUS_EQUAL',
217
+		'*='        => 'T_MUL_EQUAL',
218
+		'/='        => 'T_DIV_EQUAL',
219
+		'%='        => 'T_MOD_EQUAL',
220
+		'++'        => 'T_INC',
221
+		'--'        => 'T_DEC',
222
+		'//'        => 'T_COMMENT',
223
+		'/*'        => 'T_COMMENT',
224
+		'/**'       => 'T_DOC_COMMENT',
225
+		'*/'        => 'T_COMMENT',
226
+	];
227
+
228
+	/**
229
+	 * A list string delimiters.
230
+	 *
231
+	 * @var array
232
+	 */
233
+	protected $stringTokens = [
234
+		'\'' => '\'',
235
+		'"'  => '"',
236
+	];
237
+
238
+	/**
239
+	 * A list tokens that start and end comments.
240
+	 *
241
+	 * @var array
242
+	 */
243
+	protected $commentTokens = [
244
+		'//'  => null,
245
+		'/*'  => '*/',
246
+		'/**' => '*/',
247
+	];
248
+
249
+
250
+	/**
251
+	 * Initialise the tokenizer.
252
+	 *
253
+	 * Pre-checks the content to see if it looks minified.
254
+	 *
255
+	 * @param string                  $content The content to tokenize,
256
+	 * @param \PHP_CodeSniffer\Config $config  The config data for the run.
257
+	 * @param string                  $eolChar The EOL char used in the content.
258
+	 *
259
+	 * @return void
260
+	 * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the file appears to be minified.
261
+	 */
262
+	public function __construct($content, Config $config, $eolChar='\n')
263
+	{
264
+		if ($this->isMinifiedContent($content, $eolChar) === true) {
265
+			throw new TokenizerException('File appears to be minified and cannot be processed');
266
+		}
267
+
268
+		parent::__construct($content, $config, $eolChar);
269
+
270
+	}//end __construct()
271
+
272
+
273
+	/**
274
+	 * Creates an array of tokens when given some JS code.
275
+	 *
276
+	 * @param string $string The string to tokenize.
277
+	 *
278
+	 * @return array
279
+	 */
280
+	public function tokenize($string)
281
+	{
282
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
283
+			echo "\t*** START JS TOKENIZING ***".PHP_EOL;
284
+		}
285
+
286
+		$maxTokenLength = 0;
287
+		foreach ($this->tokenValues as $token => $values) {
288
+			if (strlen($token) > $maxTokenLength) {
289
+				$maxTokenLength = strlen($token);
290
+			}
291
+		}
292
+
293
+		$tokens          = [];
294
+		$inString        = '';
295
+		$stringChar      = null;
296
+		$inComment       = '';
297
+		$buffer          = '';
298
+		$preStringBuffer = '';
299
+		$cleanBuffer     = false;
300
+
301
+		$commentTokenizer = new Comment();
302
+
303
+		$tokens[] = [
304
+			'code'    => T_OPEN_TAG,
305
+			'type'    => 'T_OPEN_TAG',
306
+			'content' => '',
307
+		];
308
+
309
+		// Convert newlines to single characters for ease of
310
+		// processing. We will change them back later.
311
+		$string = str_replace($this->eolChar, "\n", $string);
312
+
313
+		$chars    = str_split($string);
314
+		$numChars = count($chars);
315
+		for ($i = 0; $i < $numChars; $i++) {
316
+			$char = $chars[$i];
317
+
318
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
319
+				$content       = Util\Common::prepareForOutput($char);
320
+				$bufferContent = Util\Common::prepareForOutput($buffer);
321
+
322
+				if ($inString !== '') {
323
+					echo "\t";
324
+				}
325
+
326
+				if ($inComment !== '') {
327
+					echo "\t";
328
+				}
329
+
330
+				echo "\tProcess char $i => $content (buffer: $bufferContent)".PHP_EOL;
331
+			}//end if
332
+
333
+			if ($inString === '' && $inComment === '' && $buffer !== '') {
334
+				// If the buffer only has whitespace and we are about to
335
+				// add a character, store the whitespace first.
336
+				if (trim($char) !== '' && trim($buffer) === '') {
337
+					$tokens[] = [
338
+						'code'    => T_WHITESPACE,
339
+						'type'    => 'T_WHITESPACE',
340
+						'content' => str_replace("\n", $this->eolChar, $buffer),
341
+					];
342
+
343
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
344
+						$content = Util\Common::prepareForOutput($buffer);
345
+						echo "\t=> Added token T_WHITESPACE ($content)".PHP_EOL;
346
+					}
347
+
348
+					$buffer = '';
349
+				}
350
+
351
+				// If the buffer is not whitespace and we are about to
352
+				// add a whitespace character, store the content first.
353
+				if ($inString === ''
354
+					&& $inComment === ''
355
+					&& trim($char) === ''
356
+					&& trim($buffer) !== ''
357
+				) {
358
+					$tokens[] = [
359
+						'code'    => T_STRING,
360
+						'type'    => 'T_STRING',
361
+						'content' => str_replace("\n", $this->eolChar, $buffer),
362
+					];
363
+
364
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
365
+						$content = Util\Common::prepareForOutput($buffer);
366
+						echo "\t=> Added token T_STRING ($content)".PHP_EOL;
367
+					}
368
+
369
+					$buffer = '';
370
+				}
371
+			}//end if
372
+
373
+			// Process strings.
374
+			if ($inComment === '' && isset($this->stringTokens[$char]) === true) {
375
+				if ($inString === $char) {
376
+					// This could be the end of the string, but make sure it
377
+					// is not escaped first.
378
+					$escapes = 0;
379
+					for ($x = ($i - 1); $x >= 0; $x--) {
380
+						if ($chars[$x] !== '\\') {
381
+							break;
382
+						}
383
+
384
+						$escapes++;
385
+					}
386
+
387
+					if ($escapes === 0 || ($escapes % 2) === 0) {
388
+						// There is an even number escape chars,
389
+						// so this is not escaped, it is the end of the string.
390
+						$tokens[] = [
391
+							'code'    => T_CONSTANT_ENCAPSED_STRING,
392
+							'type'    => 'T_CONSTANT_ENCAPSED_STRING',
393
+							'content' => str_replace("\n", $this->eolChar, $buffer).$char,
394
+						];
395
+
396
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
397
+							echo "\t\t* found end of string *".PHP_EOL;
398
+							$content = Util\Common::prepareForOutput($buffer.$char);
399
+							echo "\t=> Added token T_CONSTANT_ENCAPSED_STRING ($content)".PHP_EOL;
400
+						}
401
+
402
+						$buffer          = '';
403
+						$preStringBuffer = '';
404
+						$inString        = '';
405
+						$stringChar      = null;
406
+						continue;
407
+					}//end if
408
+				} else if ($inString === '') {
409
+					$inString        = $char;
410
+					$stringChar      = $i;
411
+					$preStringBuffer = $buffer;
412
+
413
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
414
+						echo "\t\t* looking for string closer *".PHP_EOL;
415
+					}
416
+				}//end if
417
+			}//end if
418
+
419
+			if ($inString !== '' && $char === "\n") {
420
+				// Unless this newline character is escaped, the string did not
421
+				// end before the end of the line, which means it probably
422
+				// wasn't a string at all (maybe a regex).
423
+				if ($chars[($i - 1)] !== '\\') {
424
+					$i      = $stringChar;
425
+					$buffer = $preStringBuffer;
426
+					$preStringBuffer = '';
427
+					$inString        = '';
428
+					$stringChar      = null;
429
+					$char            = $chars[$i];
430
+
431
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
432
+						echo "\t\t* found newline before end of string, bailing *".PHP_EOL;
433
+					}
434
+				}
435
+			}
436
+
437
+			$buffer .= $char;
438
+
439
+			// We don't look for special tokens inside strings,
440
+			// so if we are in a string, we can continue here now
441
+			// that the current char is in the buffer.
442
+			if ($inString !== '') {
443
+				continue;
444
+			}
445
+
446
+			// Special case for T_DIVIDE which can actually be
447
+			// the start of a regular expression.
448
+			if ($buffer === $char && $char === '/' && $chars[($i + 1)] !== '*') {
449
+				$regex = $this->getRegexToken($i, $string, $chars, $tokens);
450
+				if ($regex !== null) {
451
+					$tokens[] = [
452
+						'code'    => T_REGULAR_EXPRESSION,
453
+						'type'    => 'T_REGULAR_EXPRESSION',
454
+						'content' => $regex['content'],
455
+					];
456
+
457
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
458
+						$content = Util\Common::prepareForOutput($regex['content']);
459
+						echo "\t=> Added token T_REGULAR_EXPRESSION ($content)".PHP_EOL;
460
+					}
461
+
462
+					$i           = $regex['end'];
463
+					$buffer      = '';
464
+					$cleanBuffer = false;
465
+					continue;
466
+				}//end if
467
+			}//end if
468
+
469
+			// Check for known tokens, but ignore tokens found that are not at
470
+			// the end of a string, like FOR and this.FORmat.
471
+			if (isset($this->tokenValues[strtolower($buffer)]) === true
472
+				&& (preg_match('|[a-zA-z0-9_]|', $char) === 0
473
+				|| isset($chars[($i + 1)]) === false
474
+				|| preg_match('|[a-zA-z0-9_]|', $chars[($i + 1)]) === 0)
475
+			) {
476
+				$matchedToken    = false;
477
+				$lookAheadLength = ($maxTokenLength - strlen($buffer));
478
+
479
+				if ($lookAheadLength > 0) {
480
+					// The buffer contains a token type, but we need
481
+					// to look ahead at the next chars to see if this is
482
+					// actually part of a larger token. For example,
483
+					// FOR and FOREACH.
484
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
485
+						echo "\t\t* buffer possibly contains token, looking ahead $lookAheadLength chars *".PHP_EOL;
486
+					}
487
+
488
+					$charBuffer = $buffer;
489
+					for ($x = 1; $x <= $lookAheadLength; $x++) {
490
+						if (isset($chars[($i + $x)]) === false) {
491
+							break;
492
+						}
493
+
494
+						$charBuffer .= $chars[($i + $x)];
495
+
496
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
497
+							$content = Util\Common::prepareForOutput($charBuffer);
498
+							echo "\t\t=> Looking ahead $x chars => $content".PHP_EOL;
499
+						}
500
+
501
+						if (isset($this->tokenValues[strtolower($charBuffer)]) === true) {
502
+							// We've found something larger that matches
503
+							// so we can ignore this char. Except for 1 very specific
504
+							// case where a comment like /**/ needs to tokenize as
505
+							// T_COMMENT and not T_DOC_COMMENT.
506
+							$oldType = $this->tokenValues[strtolower($buffer)];
507
+							$newType = $this->tokenValues[strtolower($charBuffer)];
508
+							if ($oldType === 'T_COMMENT'
509
+								&& $newType === 'T_DOC_COMMENT'
510
+								&& $chars[($i + $x + 1)] === '/'
511
+							) {
512
+								if (PHP_CODESNIFFER_VERBOSITY > 1) {
513
+									echo "\t\t* look ahead ignored T_DOC_COMMENT, continuing *".PHP_EOL;
514
+								}
515
+							} else {
516
+								if (PHP_CODESNIFFER_VERBOSITY > 1) {
517
+									echo "\t\t* look ahead found more specific token ($newType), ignoring $i *".PHP_EOL;
518
+								}
519
+
520
+								$matchedToken = true;
521
+								break;
522
+							}
523
+						}//end if
524
+					}//end for
525
+				}//end if
526
+
527
+				if ($matchedToken === false) {
528
+					if (PHP_CODESNIFFER_VERBOSITY > 1 && $lookAheadLength > 0) {
529
+						echo "\t\t* look ahead found nothing *".PHP_EOL;
530
+					}
531
+
532
+					$value = $this->tokenValues[strtolower($buffer)];
533
+
534
+					if ($value === 'T_FUNCTION' && $buffer !== 'function') {
535
+						// The function keyword needs to be all lowercase or else
536
+						// it is just a function called "Function".
537
+						$value = 'T_STRING';
538
+					}
539
+
540
+					$tokens[] = [
541
+						'code'    => constant($value),
542
+						'type'    => $value,
543
+						'content' => $buffer,
544
+					];
545
+
546
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
547
+						$content = Util\Common::prepareForOutput($buffer);
548
+						echo "\t=> Added token $value ($content)".PHP_EOL;
549
+					}
550
+
551
+					$cleanBuffer = true;
552
+				}//end if
553
+			} else if (isset($this->tokenValues[strtolower($char)]) === true) {
554
+				// No matter what token we end up using, we don't
555
+				// need the content in the buffer any more because we have
556
+				// found a valid token.
557
+				$newContent = substr(str_replace("\n", $this->eolChar, $buffer), 0, -1);
558
+				if ($newContent !== '') {
559
+					$tokens[] = [
560
+						'code'    => T_STRING,
561
+						'type'    => 'T_STRING',
562
+						'content' => $newContent,
563
+					];
564
+
565
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
566
+						$content = Util\Common::prepareForOutput(substr($buffer, 0, -1));
567
+						echo "\t=> Added token T_STRING ($content)".PHP_EOL;
568
+					}
569
+				}
570
+
571
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
572
+					echo "\t\t* char is token, looking ahead ".($maxTokenLength - 1).' chars *'.PHP_EOL;
573
+				}
574
+
575
+				// The char is a token type, but we need to look ahead at the
576
+				// next chars to see if this is actually part of a larger token.
577
+				// For example, = and ===.
578
+				$charBuffer   = $char;
579
+				$matchedToken = false;
580
+				for ($x = 1; $x <= $maxTokenLength; $x++) {
581
+					if (isset($chars[($i + $x)]) === false) {
582
+						break;
583
+					}
584
+
585
+					$charBuffer .= $chars[($i + $x)];
586
+
587
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
588
+						$content = Util\Common::prepareForOutput($charBuffer);
589
+						echo "\t\t=> Looking ahead $x chars => $content".PHP_EOL;
590
+					}
591
+
592
+					if (isset($this->tokenValues[strtolower($charBuffer)]) === true) {
593
+						// We've found something larger that matches
594
+						// so we can ignore this char.
595
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
596
+							$type = $this->tokenValues[strtolower($charBuffer)];
597
+							echo "\t\t* look ahead found more specific token ($type), ignoring $i *".PHP_EOL;
598
+						}
599
+
600
+						$matchedToken = true;
601
+						break;
602
+					}
603
+				}//end for
604
+
605
+				if ($matchedToken === false) {
606
+					$value    = $this->tokenValues[strtolower($char)];
607
+					$tokens[] = [
608
+						'code'    => constant($value),
609
+						'type'    => $value,
610
+						'content' => $char,
611
+					];
612
+
613
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
614
+						echo "\t\t* look ahead found nothing *".PHP_EOL;
615
+						$content = Util\Common::prepareForOutput($char);
616
+						echo "\t=> Added token $value ($content)".PHP_EOL;
617
+					}
618
+
619
+					$cleanBuffer = true;
620
+				} else {
621
+					$buffer = $char;
622
+				}//end if
623
+			}//end if
624
+
625
+			// Keep track of content inside comments.
626
+			if ($inComment === ''
627
+				&& array_key_exists($buffer, $this->commentTokens) === true
628
+			) {
629
+				// This is not really a comment if the content
630
+				// looks like \// (i.e., it is escaped).
631
+				if (isset($chars[($i - 2)]) === true && $chars[($i - 2)] === '\\') {
632
+					$lastToken   = array_pop($tokens);
633
+					$lastContent = $lastToken['content'];
634
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
635
+						$value   = $this->tokenValues[strtolower($lastContent)];
636
+						$content = Util\Common::prepareForOutput($lastContent);
637
+						echo "\t=> Removed token $value ($content)".PHP_EOL;
638
+					}
639
+
640
+					$lastChars    = str_split($lastContent);
641
+					$lastNumChars = count($lastChars);
642
+					for ($x = 0; $x < $lastNumChars; $x++) {
643
+						$lastChar = $lastChars[$x];
644
+						$value    = $this->tokenValues[strtolower($lastChar)];
645
+						$tokens[] = [
646
+							'code'    => constant($value),
647
+							'type'    => $value,
648
+							'content' => $lastChar,
649
+						];
650
+
651
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
652
+							$content = Util\Common::prepareForOutput($lastChar);
653
+							echo "\t=> Added token $value ($content)".PHP_EOL;
654
+						}
655
+					}
656
+				} else {
657
+					// We have started a comment.
658
+					$inComment = $buffer;
659
+
660
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
661
+						echo "\t\t* looking for end of comment *".PHP_EOL;
662
+					}
663
+				}//end if
664
+			} else if ($inComment !== '') {
665
+				if ($this->commentTokens[$inComment] === null) {
666
+					// Comment ends at the next newline.
667
+					if (strpos($buffer, "\n") !== false) {
668
+						$inComment = '';
669
+					}
670
+				} else {
671
+					if ($this->commentTokens[$inComment] === $buffer) {
672
+						$inComment = '';
673
+					}
674
+				}
675
+
676
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
677
+					if ($inComment === '') {
678
+						echo "\t\t* found end of comment *".PHP_EOL;
679
+					}
680
+				}
681
+
682
+				if ($inComment === '' && $cleanBuffer === false) {
683
+					$tokens[] = [
684
+						'code'    => T_STRING,
685
+						'type'    => 'T_STRING',
686
+						'content' => str_replace("\n", $this->eolChar, $buffer),
687
+					];
688
+
689
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
690
+						$content = Util\Common::prepareForOutput($buffer);
691
+						echo "\t=> Added token T_STRING ($content)".PHP_EOL;
692
+					}
693
+
694
+					$buffer = '';
695
+				}
696
+			}//end if
697
+
698
+			if ($cleanBuffer === true) {
699
+				$buffer      = '';
700
+				$cleanBuffer = false;
701
+			}
702
+		}//end for
703
+
704
+		if (empty($buffer) === false) {
705
+			if ($inString !== '') {
706
+				// The string did not end before the end of the file,
707
+				// which means there was probably a syntax error somewhere.
708
+				$tokens[] = [
709
+					'code'    => T_STRING,
710
+					'type'    => 'T_STRING',
711
+					'content' => str_replace("\n", $this->eolChar, $buffer),
712
+				];
713
+
714
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
715
+					$content = Util\Common::prepareForOutput($buffer);
716
+					echo "\t=> Added token T_STRING ($content)".PHP_EOL;
717
+				}
718
+			} else {
719
+				// Buffer contains whitespace from the end of the file.
720
+				$tokens[] = [
721
+					'code'    => T_WHITESPACE,
722
+					'type'    => 'T_WHITESPACE',
723
+					'content' => str_replace("\n", $this->eolChar, $buffer),
724
+				];
725
+
726
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
727
+					$content = Util\Common::prepareForOutput($buffer);
728
+					echo "\t=> Added token T_WHITESPACE ($content)".PHP_EOL;
729
+				}
730
+			}//end if
731
+		}//end if
732
+
733
+		$tokens[] = [
734
+			'code'    => T_CLOSE_TAG,
735
+			'type'    => 'T_CLOSE_TAG',
736
+			'content' => '',
737
+		];
738
+
739
+		/*
740 740
             Now that we have done some basic tokenizing, we need to
741 741
             modify the tokens to join some together and split some apart
742 742
             so they match what the PHP tokenizer does.
743 743
         */
744 744
 
745
-        $finalTokens = [];
746
-        $newStackPtr = 0;
747
-        $numTokens   = count($tokens);
748
-        for ($stackPtr = 0; $stackPtr < $numTokens; $stackPtr++) {
749
-            $token = $tokens[$stackPtr];
745
+		$finalTokens = [];
746
+		$newStackPtr = 0;
747
+		$numTokens   = count($tokens);
748
+		for ($stackPtr = 0; $stackPtr < $numTokens; $stackPtr++) {
749
+			$token = $tokens[$stackPtr];
750 750
 
751
-            /*
751
+			/*
752 752
                 Look for comments and join the tokens together.
753 753
             */
754 754
 
755
-            if ($token['code'] === T_COMMENT || $token['code'] === T_DOC_COMMENT) {
756
-                $newContent   = '';
757
-                $tokenContent = $token['content'];
758
-
759
-                $endContent = null;
760
-                if (isset($this->commentTokens[$tokenContent]) === true) {
761
-                    $endContent = $this->commentTokens[$tokenContent];
762
-                }
763
-
764
-                while ($tokenContent !== $endContent) {
765
-                    if ($endContent === null
766
-                        && strpos($tokenContent, $this->eolChar) !== false
767
-                    ) {
768
-                        // A null end token means the comment ends at the end of
769
-                        // the line so we look for newlines and split the token.
770
-                        $tokens[$stackPtr]['content'] = substr(
771
-                            $tokenContent,
772
-                            (strpos($tokenContent, $this->eolChar) + strlen($this->eolChar))
773
-                        );
774
-
775
-                        $tokenContent = substr(
776
-                            $tokenContent,
777
-                            0,
778
-                            (strpos($tokenContent, $this->eolChar) + strlen($this->eolChar))
779
-                        );
780
-
781
-                        // If the substr failed, skip the token as the content
782
-                        // will now be blank.
783
-                        if ($tokens[$stackPtr]['content'] !== false
784
-                            && $tokens[$stackPtr]['content'] !== ''
785
-                        ) {
786
-                            $stackPtr--;
787
-                        }
788
-
789
-                        break;
790
-                    }//end if
791
-
792
-                    $stackPtr++;
793
-                    $newContent .= $tokenContent;
794
-                    if (isset($tokens[$stackPtr]) === false) {
795
-                        break;
796
-                    }
797
-
798
-                    $tokenContent = $tokens[$stackPtr]['content'];
799
-                }//end while
800
-
801
-                if ($token['code'] === T_DOC_COMMENT) {
802
-                    $commentTokens = $commentTokenizer->tokenizeString($newContent.$tokenContent, $this->eolChar, $newStackPtr);
803
-                    foreach ($commentTokens as $commentToken) {
804
-                        $finalTokens[$newStackPtr] = $commentToken;
805
-                        $newStackPtr++;
806
-                    }
807
-
808
-                    continue;
809
-                } else {
810
-                    // Save the new content in the current token so
811
-                    // the code below can chop it up on newlines.
812
-                    $token['content'] = $newContent.$tokenContent;
813
-                }
814
-            }//end if
815
-
816
-            /*
755
+			if ($token['code'] === T_COMMENT || $token['code'] === T_DOC_COMMENT) {
756
+				$newContent   = '';
757
+				$tokenContent = $token['content'];
758
+
759
+				$endContent = null;
760
+				if (isset($this->commentTokens[$tokenContent]) === true) {
761
+					$endContent = $this->commentTokens[$tokenContent];
762
+				}
763
+
764
+				while ($tokenContent !== $endContent) {
765
+					if ($endContent === null
766
+						&& strpos($tokenContent, $this->eolChar) !== false
767
+					) {
768
+						// A null end token means the comment ends at the end of
769
+						// the line so we look for newlines and split the token.
770
+						$tokens[$stackPtr]['content'] = substr(
771
+							$tokenContent,
772
+							(strpos($tokenContent, $this->eolChar) + strlen($this->eolChar))
773
+						);
774
+
775
+						$tokenContent = substr(
776
+							$tokenContent,
777
+							0,
778
+							(strpos($tokenContent, $this->eolChar) + strlen($this->eolChar))
779
+						);
780
+
781
+						// If the substr failed, skip the token as the content
782
+						// will now be blank.
783
+						if ($tokens[$stackPtr]['content'] !== false
784
+							&& $tokens[$stackPtr]['content'] !== ''
785
+						) {
786
+							$stackPtr--;
787
+						}
788
+
789
+						break;
790
+					}//end if
791
+
792
+					$stackPtr++;
793
+					$newContent .= $tokenContent;
794
+					if (isset($tokens[$stackPtr]) === false) {
795
+						break;
796
+					}
797
+
798
+					$tokenContent = $tokens[$stackPtr]['content'];
799
+				}//end while
800
+
801
+				if ($token['code'] === T_DOC_COMMENT) {
802
+					$commentTokens = $commentTokenizer->tokenizeString($newContent.$tokenContent, $this->eolChar, $newStackPtr);
803
+					foreach ($commentTokens as $commentToken) {
804
+						$finalTokens[$newStackPtr] = $commentToken;
805
+						$newStackPtr++;
806
+					}
807
+
808
+					continue;
809
+				} else {
810
+					// Save the new content in the current token so
811
+					// the code below can chop it up on newlines.
812
+					$token['content'] = $newContent.$tokenContent;
813
+				}
814
+			}//end if
815
+
816
+			/*
817 817
                 If this token has newlines in its content, split each line up
818 818
                 and create a new token for each line. We do this so it's easier
819 819
                 to ascertain where errors occur on a line.
820 820
                 Note that $token[1] is the token's content.
821 821
             */
822 822
 
823
-            if (strpos($token['content'], $this->eolChar) !== false) {
824
-                $tokenLines = explode($this->eolChar, $token['content']);
825
-                $numLines   = count($tokenLines);
826
-
827
-                for ($i = 0; $i < $numLines; $i++) {
828
-                    $newToken = ['content' => $tokenLines[$i]];
829
-                    if ($i === ($numLines - 1)) {
830
-                        if ($tokenLines[$i] === '') {
831
-                            break;
832
-                        }
833
-                    } else {
834
-                        $newToken['content'] .= $this->eolChar;
835
-                    }
836
-
837
-                    $newToken['type']          = $token['type'];
838
-                    $newToken['code']          = $token['code'];
839
-                    $finalTokens[$newStackPtr] = $newToken;
840
-                    $newStackPtr++;
841
-                }
842
-            } else {
843
-                $finalTokens[$newStackPtr] = $token;
844
-                $newStackPtr++;
845
-            }//end if
846
-
847
-            // Convert numbers, including decimals.
848
-            if ($token['code'] === T_STRING
849
-                || $token['code'] === T_OBJECT_OPERATOR
850
-            ) {
851
-                $newContent  = '';
852
-                $oldStackPtr = $stackPtr;
853
-                while (preg_match('|^[0-9\.]+$|', $tokens[$stackPtr]['content']) !== 0) {
854
-                    $newContent .= $tokens[$stackPtr]['content'];
855
-                    $stackPtr++;
856
-                }
857
-
858
-                if ($newContent !== '' && $newContent !== '.') {
859
-                    $finalTokens[($newStackPtr - 1)]['content'] = $newContent;
860
-                    if (ctype_digit($newContent) === true) {
861
-                        $finalTokens[($newStackPtr - 1)]['code'] = constant('T_LNUMBER');
862
-                        $finalTokens[($newStackPtr - 1)]['type'] = 'T_LNUMBER';
863
-                    } else {
864
-                        $finalTokens[($newStackPtr - 1)]['code'] = constant('T_DNUMBER');
865
-                        $finalTokens[($newStackPtr - 1)]['type'] = 'T_DNUMBER';
866
-                    }
867
-
868
-                    $stackPtr--;
869
-                    continue;
870
-                } else {
871
-                    $stackPtr = $oldStackPtr;
872
-                }
873
-            }//end if
874
-
875
-            // Convert the token after an object operator into a string, in most cases.
876
-            if ($token['code'] === T_OBJECT_OPERATOR) {
877
-                for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
878
-                    if (isset(Util\Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
879
-                        continue;
880
-                    }
881
-
882
-                    if ($tokens[$i]['code'] !== T_PROTOTYPE
883
-                        && $tokens[$i]['code'] !== T_LNUMBER
884
-                        && $tokens[$i]['code'] !== T_DNUMBER
885
-                    ) {
886
-                        $tokens[$i]['code'] = T_STRING;
887
-                        $tokens[$i]['type'] = 'T_STRING';
888
-                    }
889
-
890
-                    break;
891
-                }
892
-            }
893
-        }//end for
894
-
895
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
896
-            echo "\t*** END TOKENIZING ***".PHP_EOL;
897
-        }
898
-
899
-        return $finalTokens;
900
-
901
-    }//end tokenize()
902
-
903
-
904
-    /**
905
-     * Tokenizes a regular expression if one is found.
906
-     *
907
-     * If a regular expression is not found, NULL is returned.
908
-     *
909
-     * @param string $char   The index of the possible regex start character.
910
-     * @param string $string The complete content of the string being tokenized.
911
-     * @param string $chars  An array of characters being tokenized.
912
-     * @param string $tokens The current array of tokens found in the string.
913
-     *
914
-     * @return array<string, string>|null
915
-     */
916
-    public function getRegexToken($char, $string, $chars, $tokens)
917
-    {
918
-        $beforeTokens = [
919
-            T_EQUAL               => true,
920
-            T_IS_NOT_EQUAL        => true,
921
-            T_IS_IDENTICAL        => true,
922
-            T_IS_NOT_IDENTICAL    => true,
923
-            T_OPEN_PARENTHESIS    => true,
924
-            T_OPEN_SQUARE_BRACKET => true,
925
-            T_RETURN              => true,
926
-            T_BOOLEAN_OR          => true,
927
-            T_BOOLEAN_AND         => true,
928
-            T_BOOLEAN_NOT         => true,
929
-            T_BITWISE_OR          => true,
930
-            T_BITWISE_AND         => true,
931
-            T_COMMA               => true,
932
-            T_COLON               => true,
933
-            T_TYPEOF              => true,
934
-            T_INLINE_THEN         => true,
935
-            T_INLINE_ELSE         => true,
936
-        ];
937
-
938
-        $afterTokens = [
939
-            ','            => true,
940
-            ')'            => true,
941
-            ']'            => true,
942
-            ';'            => true,
943
-            ' '            => true,
944
-            '.'            => true,
945
-            ':'            => true,
946
-            $this->eolChar => true,
947
-        ];
948
-
949
-        // Find the last non-whitespace token that was added
950
-        // to the tokens array.
951
-        $numTokens = count($tokens);
952
-        for ($prev = ($numTokens - 1); $prev >= 0; $prev--) {
953
-            if (isset(Util\Tokens::$emptyTokens[$tokens[$prev]['code']]) === false) {
954
-                break;
955
-            }
956
-        }
957
-
958
-        if (isset($beforeTokens[$tokens[$prev]['code']]) === false) {
959
-            return null;
960
-        }
961
-
962
-        // This is probably a regular expression, so look for the end of it.
963
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
964
-            echo "\t* token possibly starts a regular expression *".PHP_EOL;
965
-        }
966
-
967
-        $numChars = count($chars);
968
-        for ($next = ($char + 1); $next < $numChars; $next++) {
969
-            if ($chars[$next] === '/') {
970
-                // Just make sure this is not escaped first.
971
-                if ($chars[($next - 1)] !== '\\') {
972
-                    // In the simple form: /.../ so we found the end.
973
-                    break;
974
-                } else if ($chars[($next - 2)] === '\\') {
975
-                    // In the form: /...\\/ so we found the end.
976
-                    break;
977
-                }
978
-            } else {
979
-                $possibleEolChar = substr($string, $next, strlen($this->eolChar));
980
-                if ($possibleEolChar === $this->eolChar) {
981
-                    // This is the last token on the line and regular
982
-                    // expressions need to be defined on a single line,
983
-                    // so this is not a regular expression.
984
-                    break;
985
-                }
986
-            }
987
-        }
988
-
989
-        if ($chars[$next] !== '/') {
990
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
991
-                echo "\t* could not find end of regular expression *".PHP_EOL;
992
-            }
993
-
994
-            return null;
995
-        }
996
-
997
-        while (preg_match('|[a-zA-Z]|', $chars[($next + 1)]) !== 0) {
998
-            // The token directly after the end of the regex can
999
-            // be modifiers like global and case insensitive
1000
-            // (.e.g, /pattern/gi).
1001
-            $next++;
1002
-        }
1003
-
1004
-        $regexEnd = $next;
1005
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1006
-            echo "\t* found end of regular expression at token $regexEnd *".PHP_EOL;
1007
-        }
1008
-
1009
-        for ($next += 1; $next < $numChars; $next++) {
1010
-            if ($chars[$next] !== ' ') {
1011
-                break;
1012
-            } else {
1013
-                $possibleEolChar = substr($string, $next, strlen($this->eolChar));
1014
-                if ($possibleEolChar === $this->eolChar) {
1015
-                    // This is the last token on the line.
1016
-                    break;
1017
-                }
1018
-            }
1019
-        }
1020
-
1021
-        if (isset($afterTokens[$chars[$next]]) === false) {
1022
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1023
-                echo "\t* tokens after regular expression do not look correct *".PHP_EOL;
1024
-            }
1025
-
1026
-            return null;
1027
-        }
1028
-
1029
-        // This is a regular expression, so join all the tokens together.
1030
-        $content = '';
1031
-        for ($x = $char; $x <= $regexEnd; $x++) {
1032
-            $content .= $chars[$x];
1033
-        }
1034
-
1035
-        $token = [
1036
-            'start'   => $char,
1037
-            'end'     => $regexEnd,
1038
-            'content' => $content,
1039
-        ];
1040
-
1041
-        return $token;
1042
-
1043
-    }//end getRegexToken()
1044
-
1045
-
1046
-    /**
1047
-     * Performs additional processing after main tokenizing.
1048
-     *
1049
-     * This additional processing looks for properties, closures, labels and objects.
1050
-     *
1051
-     * @return void
1052
-     */
1053
-    public function processAdditional()
1054
-    {
1055
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1056
-            echo "\t*** START ADDITIONAL JS PROCESSING ***".PHP_EOL;
1057
-        }
1058
-
1059
-        $numTokens  = count($this->tokens);
1060
-        $classStack = [];
1061
-
1062
-        for ($i = 0; $i < $numTokens; $i++) {
1063
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1064
-                $type    = $this->tokens[$i]['type'];
1065
-                $content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
1066
-
1067
-                echo str_repeat("\t", count($classStack));
1068
-                echo "\tProcess token $i: $type => $content".PHP_EOL;
1069
-            }
1070
-
1071
-            // Looking for functions that are actually closures.
1072
-            if ($this->tokens[$i]['code'] === T_FUNCTION && isset($this->tokens[$i]['scope_opener']) === true) {
1073
-                for ($x = ($i + 1); $x < $numTokens; $x++) {
1074
-                    if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1075
-                        break;
1076
-                    }
1077
-                }
1078
-
1079
-                if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS) {
1080
-                    $this->tokens[$i]['code'] = T_CLOSURE;
1081
-                    $this->tokens[$i]['type'] = 'T_CLOSURE';
1082
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1083
-                        $line = $this->tokens[$i]['line'];
1084
-                        echo str_repeat("\t", count($classStack));
1085
-                        echo "\t* token $i on line $line changed from T_FUNCTION to T_CLOSURE *".PHP_EOL;
1086
-                    }
1087
-
1088
-                    for ($x = ($this->tokens[$i]['scope_opener'] + 1); $x < $this->tokens[$i]['scope_closer']; $x++) {
1089
-                        if (isset($this->tokens[$x]['conditions'][$i]) === false) {
1090
-                            continue;
1091
-                        }
1092
-
1093
-                        $this->tokens[$x]['conditions'][$i] = T_CLOSURE;
1094
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1095
-                            $type = $this->tokens[$x]['type'];
1096
-                            echo str_repeat("\t", count($classStack));
1097
-                            echo "\t\t* cleaned $x ($type) *".PHP_EOL;
1098
-                        }
1099
-                    }
1100
-                }//end if
1101
-
1102
-                continue;
1103
-            } else if ($this->tokens[$i]['code'] === T_OPEN_CURLY_BRACKET
1104
-                && isset($this->tokens[$i]['scope_condition']) === false
1105
-                && isset($this->tokens[$i]['bracket_closer']) === true
1106
-            ) {
1107
-                $condition = $this->tokens[$i]['conditions'];
1108
-                $condition = end($condition);
1109
-                if ($condition === T_CLASS) {
1110
-                    // Possibly an ES6 method. To be classified as one, the previous
1111
-                    // non-empty tokens need to be a set of parenthesis, and then a string
1112
-                    // (the method name).
1113
-                    for ($parenCloser = ($i - 1); $parenCloser > 0; $parenCloser--) {
1114
-                        if (isset(Util\Tokens::$emptyTokens[$this->tokens[$parenCloser]['code']]) === false) {
1115
-                            break;
1116
-                        }
1117
-                    }
1118
-
1119
-                    if ($this->tokens[$parenCloser]['code'] === T_CLOSE_PARENTHESIS) {
1120
-                        $parenOpener = $this->tokens[$parenCloser]['parenthesis_opener'];
1121
-                        for ($name = ($parenOpener - 1); $name > 0; $name--) {
1122
-                            if (isset(Util\Tokens::$emptyTokens[$this->tokens[$name]['code']]) === false) {
1123
-                                break;
1124
-                            }
1125
-                        }
1126
-
1127
-                        if ($this->tokens[$name]['code'] === T_STRING) {
1128
-                            // We found a method name.
1129
-                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
1130
-                                $line = $this->tokens[$name]['line'];
1131
-                                echo str_repeat("\t", count($classStack));
1132
-                                echo "\t* token $name on line $line changed from T_STRING to T_FUNCTION *".PHP_EOL;
1133
-                            }
1134
-
1135
-                            $closer = $this->tokens[$i]['bracket_closer'];
1136
-
1137
-                            $this->tokens[$name]['code'] = T_FUNCTION;
1138
-                            $this->tokens[$name]['type'] = 'T_FUNCTION';
1139
-
1140
-                            foreach ([$name, $i, $closer] as $token) {
1141
-                                $this->tokens[$token]['scope_condition']    = $name;
1142
-                                $this->tokens[$token]['scope_opener']       = $i;
1143
-                                $this->tokens[$token]['scope_closer']       = $closer;
1144
-                                $this->tokens[$token]['parenthesis_opener'] = $parenOpener;
1145
-                                $this->tokens[$token]['parenthesis_closer'] = $parenCloser;
1146
-                                $this->tokens[$token]['parenthesis_owner']  = $name;
1147
-                            }
1148
-
1149
-                            $this->tokens[$parenOpener]['parenthesis_owner'] = $name;
1150
-                            $this->tokens[$parenCloser]['parenthesis_owner'] = $name;
1151
-
1152
-                            for ($x = ($i + 1); $x < $closer; $x++) {
1153
-                                $this->tokens[$x]['conditions'][$name] = T_FUNCTION;
1154
-                                ksort($this->tokens[$x]['conditions'], SORT_NUMERIC);
1155
-                                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1156
-                                    $type = $this->tokens[$x]['type'];
1157
-                                    echo str_repeat("\t", count($classStack));
1158
-                                    echo "\t\t* added T_FUNCTION condition to $x ($type) *".PHP_EOL;
1159
-                                }
1160
-                            }
1161
-
1162
-                            continue;
1163
-                        }//end if
1164
-                    }//end if
1165
-                }//end if
1166
-
1167
-                $classStack[] = $i;
1168
-
1169
-                $closer = $this->tokens[$i]['bracket_closer'];
1170
-                $this->tokens[$i]['code']      = T_OBJECT;
1171
-                $this->tokens[$i]['type']      = 'T_OBJECT';
1172
-                $this->tokens[$closer]['code'] = T_CLOSE_OBJECT;
1173
-                $this->tokens[$closer]['type'] = 'T_CLOSE_OBJECT';
1174
-
1175
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1176
-                    echo str_repeat("\t", count($classStack));
1177
-                    echo "\t* token $i converted from T_OPEN_CURLY_BRACKET to T_OBJECT *".PHP_EOL;
1178
-                    echo str_repeat("\t", count($classStack));
1179
-                    echo "\t* token $closer converted from T_CLOSE_CURLY_BRACKET to T_CLOSE_OBJECT *".PHP_EOL;
1180
-                }
1181
-
1182
-                for ($x = ($i + 1); $x < $closer; $x++) {
1183
-                    $this->tokens[$x]['conditions'][$i] = T_OBJECT;
1184
-                    ksort($this->tokens[$x]['conditions'], SORT_NUMERIC);
1185
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1186
-                        $type = $this->tokens[$x]['type'];
1187
-                        echo str_repeat("\t", count($classStack));
1188
-                        echo "\t\t* added T_OBJECT condition to $x ($type) *".PHP_EOL;
1189
-                    }
1190
-                }
1191
-            } else if ($this->tokens[$i]['code'] === T_CLOSE_OBJECT) {
1192
-                $opener = array_pop($classStack);
1193
-            } else if ($this->tokens[$i]['code'] === T_COLON) {
1194
-                // If it is a scope opener, it belongs to a
1195
-                // DEFAULT or CASE statement.
1196
-                if (isset($this->tokens[$i]['scope_condition']) === true) {
1197
-                    continue;
1198
-                }
1199
-
1200
-                // Make sure this is not part of an inline IF statement.
1201
-                for ($x = ($i - 1); $x >= 0; $x--) {
1202
-                    if ($this->tokens[$x]['code'] === T_INLINE_THEN) {
1203
-                        $this->tokens[$i]['code'] = T_INLINE_ELSE;
1204
-                        $this->tokens[$i]['type'] = 'T_INLINE_ELSE';
1205
-
1206
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1207
-                            echo str_repeat("\t", count($classStack));
1208
-                            echo "\t* token $i converted from T_COLON to T_INLINE_THEN *".PHP_EOL;
1209
-                        }
1210
-
1211
-                        continue(2);
1212
-                    } else if ($this->tokens[$x]['line'] < $this->tokens[$i]['line']) {
1213
-                        break;
1214
-                    }
1215
-                }
1216
-
1217
-                // The string to the left of the colon is either a property or label.
1218
-                for ($label = ($i - 1); $label >= 0; $label--) {
1219
-                    if (isset(Util\Tokens::$emptyTokens[$this->tokens[$label]['code']]) === false) {
1220
-                        break;
1221
-                    }
1222
-                }
1223
-
1224
-                if ($this->tokens[$label]['code'] !== T_STRING
1225
-                    && $this->tokens[$label]['code'] !== T_CONSTANT_ENCAPSED_STRING
1226
-                ) {
1227
-                    continue;
1228
-                }
1229
-
1230
-                if (empty($classStack) === false) {
1231
-                    $this->tokens[$label]['code'] = T_PROPERTY;
1232
-                    $this->tokens[$label]['type'] = 'T_PROPERTY';
1233
-
1234
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1235
-                        echo str_repeat("\t", count($classStack));
1236
-                        echo "\t* token $label converted from T_STRING to T_PROPERTY *".PHP_EOL;
1237
-                    }
1238
-                } else {
1239
-                    $this->tokens[$label]['code'] = T_LABEL;
1240
-                    $this->tokens[$label]['type'] = 'T_LABEL';
1241
-
1242
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
1243
-                        echo str_repeat("\t", count($classStack));
1244
-                        echo "\t* token $label converted from T_STRING to T_LABEL *".PHP_EOL;
1245
-                    }
1246
-                }//end if
1247
-            }//end if
1248
-        }//end for
1249
-
1250
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1251
-            echo "\t*** END ADDITIONAL JS PROCESSING ***".PHP_EOL;
1252
-        }
1253
-
1254
-    }//end processAdditional()
823
+			if (strpos($token['content'], $this->eolChar) !== false) {
824
+				$tokenLines = explode($this->eolChar, $token['content']);
825
+				$numLines   = count($tokenLines);
826
+
827
+				for ($i = 0; $i < $numLines; $i++) {
828
+					$newToken = ['content' => $tokenLines[$i]];
829
+					if ($i === ($numLines - 1)) {
830
+						if ($tokenLines[$i] === '') {
831
+							break;
832
+						}
833
+					} else {
834
+						$newToken['content'] .= $this->eolChar;
835
+					}
836
+
837
+					$newToken['type']          = $token['type'];
838
+					$newToken['code']          = $token['code'];
839
+					$finalTokens[$newStackPtr] = $newToken;
840
+					$newStackPtr++;
841
+				}
842
+			} else {
843
+				$finalTokens[$newStackPtr] = $token;
844
+				$newStackPtr++;
845
+			}//end if
846
+
847
+			// Convert numbers, including decimals.
848
+			if ($token['code'] === T_STRING
849
+				|| $token['code'] === T_OBJECT_OPERATOR
850
+			) {
851
+				$newContent  = '';
852
+				$oldStackPtr = $stackPtr;
853
+				while (preg_match('|^[0-9\.]+$|', $tokens[$stackPtr]['content']) !== 0) {
854
+					$newContent .= $tokens[$stackPtr]['content'];
855
+					$stackPtr++;
856
+				}
857
+
858
+				if ($newContent !== '' && $newContent !== '.') {
859
+					$finalTokens[($newStackPtr - 1)]['content'] = $newContent;
860
+					if (ctype_digit($newContent) === true) {
861
+						$finalTokens[($newStackPtr - 1)]['code'] = constant('T_LNUMBER');
862
+						$finalTokens[($newStackPtr - 1)]['type'] = 'T_LNUMBER';
863
+					} else {
864
+						$finalTokens[($newStackPtr - 1)]['code'] = constant('T_DNUMBER');
865
+						$finalTokens[($newStackPtr - 1)]['type'] = 'T_DNUMBER';
866
+					}
867
+
868
+					$stackPtr--;
869
+					continue;
870
+				} else {
871
+					$stackPtr = $oldStackPtr;
872
+				}
873
+			}//end if
874
+
875
+			// Convert the token after an object operator into a string, in most cases.
876
+			if ($token['code'] === T_OBJECT_OPERATOR) {
877
+				for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
878
+					if (isset(Util\Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
879
+						continue;
880
+					}
881
+
882
+					if ($tokens[$i]['code'] !== T_PROTOTYPE
883
+						&& $tokens[$i]['code'] !== T_LNUMBER
884
+						&& $tokens[$i]['code'] !== T_DNUMBER
885
+					) {
886
+						$tokens[$i]['code'] = T_STRING;
887
+						$tokens[$i]['type'] = 'T_STRING';
888
+					}
889
+
890
+					break;
891
+				}
892
+			}
893
+		}//end for
894
+
895
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
896
+			echo "\t*** END TOKENIZING ***".PHP_EOL;
897
+		}
898
+
899
+		return $finalTokens;
900
+
901
+	}//end tokenize()
902
+
903
+
904
+	/**
905
+	 * Tokenizes a regular expression if one is found.
906
+	 *
907
+	 * If a regular expression is not found, NULL is returned.
908
+	 *
909
+	 * @param string $char   The index of the possible regex start character.
910
+	 * @param string $string The complete content of the string being tokenized.
911
+	 * @param string $chars  An array of characters being tokenized.
912
+	 * @param string $tokens The current array of tokens found in the string.
913
+	 *
914
+	 * @return array<string, string>|null
915
+	 */
916
+	public function getRegexToken($char, $string, $chars, $tokens)
917
+	{
918
+		$beforeTokens = [
919
+			T_EQUAL               => true,
920
+			T_IS_NOT_EQUAL        => true,
921
+			T_IS_IDENTICAL        => true,
922
+			T_IS_NOT_IDENTICAL    => true,
923
+			T_OPEN_PARENTHESIS    => true,
924
+			T_OPEN_SQUARE_BRACKET => true,
925
+			T_RETURN              => true,
926
+			T_BOOLEAN_OR          => true,
927
+			T_BOOLEAN_AND         => true,
928
+			T_BOOLEAN_NOT         => true,
929
+			T_BITWISE_OR          => true,
930
+			T_BITWISE_AND         => true,
931
+			T_COMMA               => true,
932
+			T_COLON               => true,
933
+			T_TYPEOF              => true,
934
+			T_INLINE_THEN         => true,
935
+			T_INLINE_ELSE         => true,
936
+		];
937
+
938
+		$afterTokens = [
939
+			','            => true,
940
+			')'            => true,
941
+			']'            => true,
942
+			';'            => true,
943
+			' '            => true,
944
+			'.'            => true,
945
+			':'            => true,
946
+			$this->eolChar => true,
947
+		];
948
+
949
+		// Find the last non-whitespace token that was added
950
+		// to the tokens array.
951
+		$numTokens = count($tokens);
952
+		for ($prev = ($numTokens - 1); $prev >= 0; $prev--) {
953
+			if (isset(Util\Tokens::$emptyTokens[$tokens[$prev]['code']]) === false) {
954
+				break;
955
+			}
956
+		}
957
+
958
+		if (isset($beforeTokens[$tokens[$prev]['code']]) === false) {
959
+			return null;
960
+		}
961
+
962
+		// This is probably a regular expression, so look for the end of it.
963
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
964
+			echo "\t* token possibly starts a regular expression *".PHP_EOL;
965
+		}
966
+
967
+		$numChars = count($chars);
968
+		for ($next = ($char + 1); $next < $numChars; $next++) {
969
+			if ($chars[$next] === '/') {
970
+				// Just make sure this is not escaped first.
971
+				if ($chars[($next - 1)] !== '\\') {
972
+					// In the simple form: /.../ so we found the end.
973
+					break;
974
+				} else if ($chars[($next - 2)] === '\\') {
975
+					// In the form: /...\\/ so we found the end.
976
+					break;
977
+				}
978
+			} else {
979
+				$possibleEolChar = substr($string, $next, strlen($this->eolChar));
980
+				if ($possibleEolChar === $this->eolChar) {
981
+					// This is the last token on the line and regular
982
+					// expressions need to be defined on a single line,
983
+					// so this is not a regular expression.
984
+					break;
985
+				}
986
+			}
987
+		}
988
+
989
+		if ($chars[$next] !== '/') {
990
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
991
+				echo "\t* could not find end of regular expression *".PHP_EOL;
992
+			}
993
+
994
+			return null;
995
+		}
996
+
997
+		while (preg_match('|[a-zA-Z]|', $chars[($next + 1)]) !== 0) {
998
+			// The token directly after the end of the regex can
999
+			// be modifiers like global and case insensitive
1000
+			// (.e.g, /pattern/gi).
1001
+			$next++;
1002
+		}
1003
+
1004
+		$regexEnd = $next;
1005
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
1006
+			echo "\t* found end of regular expression at token $regexEnd *".PHP_EOL;
1007
+		}
1008
+
1009
+		for ($next += 1; $next < $numChars; $next++) {
1010
+			if ($chars[$next] !== ' ') {
1011
+				break;
1012
+			} else {
1013
+				$possibleEolChar = substr($string, $next, strlen($this->eolChar));
1014
+				if ($possibleEolChar === $this->eolChar) {
1015
+					// This is the last token on the line.
1016
+					break;
1017
+				}
1018
+			}
1019
+		}
1020
+
1021
+		if (isset($afterTokens[$chars[$next]]) === false) {
1022
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
1023
+				echo "\t* tokens after regular expression do not look correct *".PHP_EOL;
1024
+			}
1025
+
1026
+			return null;
1027
+		}
1028
+
1029
+		// This is a regular expression, so join all the tokens together.
1030
+		$content = '';
1031
+		for ($x = $char; $x <= $regexEnd; $x++) {
1032
+			$content .= $chars[$x];
1033
+		}
1034
+
1035
+		$token = [
1036
+			'start'   => $char,
1037
+			'end'     => $regexEnd,
1038
+			'content' => $content,
1039
+		];
1040
+
1041
+		return $token;
1042
+
1043
+	}//end getRegexToken()
1044
+
1045
+
1046
+	/**
1047
+	 * Performs additional processing after main tokenizing.
1048
+	 *
1049
+	 * This additional processing looks for properties, closures, labels and objects.
1050
+	 *
1051
+	 * @return void
1052
+	 */
1053
+	public function processAdditional()
1054
+	{
1055
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
1056
+			echo "\t*** START ADDITIONAL JS PROCESSING ***".PHP_EOL;
1057
+		}
1058
+
1059
+		$numTokens  = count($this->tokens);
1060
+		$classStack = [];
1061
+
1062
+		for ($i = 0; $i < $numTokens; $i++) {
1063
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
1064
+				$type    = $this->tokens[$i]['type'];
1065
+				$content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
1066
+
1067
+				echo str_repeat("\t", count($classStack));
1068
+				echo "\tProcess token $i: $type => $content".PHP_EOL;
1069
+			}
1070
+
1071
+			// Looking for functions that are actually closures.
1072
+			if ($this->tokens[$i]['code'] === T_FUNCTION && isset($this->tokens[$i]['scope_opener']) === true) {
1073
+				for ($x = ($i + 1); $x < $numTokens; $x++) {
1074
+					if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1075
+						break;
1076
+					}
1077
+				}
1078
+
1079
+				if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS) {
1080
+					$this->tokens[$i]['code'] = T_CLOSURE;
1081
+					$this->tokens[$i]['type'] = 'T_CLOSURE';
1082
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1083
+						$line = $this->tokens[$i]['line'];
1084
+						echo str_repeat("\t", count($classStack));
1085
+						echo "\t* token $i on line $line changed from T_FUNCTION to T_CLOSURE *".PHP_EOL;
1086
+					}
1087
+
1088
+					for ($x = ($this->tokens[$i]['scope_opener'] + 1); $x < $this->tokens[$i]['scope_closer']; $x++) {
1089
+						if (isset($this->tokens[$x]['conditions'][$i]) === false) {
1090
+							continue;
1091
+						}
1092
+
1093
+						$this->tokens[$x]['conditions'][$i] = T_CLOSURE;
1094
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1095
+							$type = $this->tokens[$x]['type'];
1096
+							echo str_repeat("\t", count($classStack));
1097
+							echo "\t\t* cleaned $x ($type) *".PHP_EOL;
1098
+						}
1099
+					}
1100
+				}//end if
1101
+
1102
+				continue;
1103
+			} else if ($this->tokens[$i]['code'] === T_OPEN_CURLY_BRACKET
1104
+				&& isset($this->tokens[$i]['scope_condition']) === false
1105
+				&& isset($this->tokens[$i]['bracket_closer']) === true
1106
+			) {
1107
+				$condition = $this->tokens[$i]['conditions'];
1108
+				$condition = end($condition);
1109
+				if ($condition === T_CLASS) {
1110
+					// Possibly an ES6 method. To be classified as one, the previous
1111
+					// non-empty tokens need to be a set of parenthesis, and then a string
1112
+					// (the method name).
1113
+					for ($parenCloser = ($i - 1); $parenCloser > 0; $parenCloser--) {
1114
+						if (isset(Util\Tokens::$emptyTokens[$this->tokens[$parenCloser]['code']]) === false) {
1115
+							break;
1116
+						}
1117
+					}
1118
+
1119
+					if ($this->tokens[$parenCloser]['code'] === T_CLOSE_PARENTHESIS) {
1120
+						$parenOpener = $this->tokens[$parenCloser]['parenthesis_opener'];
1121
+						for ($name = ($parenOpener - 1); $name > 0; $name--) {
1122
+							if (isset(Util\Tokens::$emptyTokens[$this->tokens[$name]['code']]) === false) {
1123
+								break;
1124
+							}
1125
+						}
1126
+
1127
+						if ($this->tokens[$name]['code'] === T_STRING) {
1128
+							// We found a method name.
1129
+							if (PHP_CODESNIFFER_VERBOSITY > 1) {
1130
+								$line = $this->tokens[$name]['line'];
1131
+								echo str_repeat("\t", count($classStack));
1132
+								echo "\t* token $name on line $line changed from T_STRING to T_FUNCTION *".PHP_EOL;
1133
+							}
1134
+
1135
+							$closer = $this->tokens[$i]['bracket_closer'];
1136
+
1137
+							$this->tokens[$name]['code'] = T_FUNCTION;
1138
+							$this->tokens[$name]['type'] = 'T_FUNCTION';
1139
+
1140
+							foreach ([$name, $i, $closer] as $token) {
1141
+								$this->tokens[$token]['scope_condition']    = $name;
1142
+								$this->tokens[$token]['scope_opener']       = $i;
1143
+								$this->tokens[$token]['scope_closer']       = $closer;
1144
+								$this->tokens[$token]['parenthesis_opener'] = $parenOpener;
1145
+								$this->tokens[$token]['parenthesis_closer'] = $parenCloser;
1146
+								$this->tokens[$token]['parenthesis_owner']  = $name;
1147
+							}
1148
+
1149
+							$this->tokens[$parenOpener]['parenthesis_owner'] = $name;
1150
+							$this->tokens[$parenCloser]['parenthesis_owner'] = $name;
1151
+
1152
+							for ($x = ($i + 1); $x < $closer; $x++) {
1153
+								$this->tokens[$x]['conditions'][$name] = T_FUNCTION;
1154
+								ksort($this->tokens[$x]['conditions'], SORT_NUMERIC);
1155
+								if (PHP_CODESNIFFER_VERBOSITY > 1) {
1156
+									$type = $this->tokens[$x]['type'];
1157
+									echo str_repeat("\t", count($classStack));
1158
+									echo "\t\t* added T_FUNCTION condition to $x ($type) *".PHP_EOL;
1159
+								}
1160
+							}
1161
+
1162
+							continue;
1163
+						}//end if
1164
+					}//end if
1165
+				}//end if
1166
+
1167
+				$classStack[] = $i;
1168
+
1169
+				$closer = $this->tokens[$i]['bracket_closer'];
1170
+				$this->tokens[$i]['code']      = T_OBJECT;
1171
+				$this->tokens[$i]['type']      = 'T_OBJECT';
1172
+				$this->tokens[$closer]['code'] = T_CLOSE_OBJECT;
1173
+				$this->tokens[$closer]['type'] = 'T_CLOSE_OBJECT';
1174
+
1175
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
1176
+					echo str_repeat("\t", count($classStack));
1177
+					echo "\t* token $i converted from T_OPEN_CURLY_BRACKET to T_OBJECT *".PHP_EOL;
1178
+					echo str_repeat("\t", count($classStack));
1179
+					echo "\t* token $closer converted from T_CLOSE_CURLY_BRACKET to T_CLOSE_OBJECT *".PHP_EOL;
1180
+				}
1181
+
1182
+				for ($x = ($i + 1); $x < $closer; $x++) {
1183
+					$this->tokens[$x]['conditions'][$i] = T_OBJECT;
1184
+					ksort($this->tokens[$x]['conditions'], SORT_NUMERIC);
1185
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1186
+						$type = $this->tokens[$x]['type'];
1187
+						echo str_repeat("\t", count($classStack));
1188
+						echo "\t\t* added T_OBJECT condition to $x ($type) *".PHP_EOL;
1189
+					}
1190
+				}
1191
+			} else if ($this->tokens[$i]['code'] === T_CLOSE_OBJECT) {
1192
+				$opener = array_pop($classStack);
1193
+			} else if ($this->tokens[$i]['code'] === T_COLON) {
1194
+				// If it is a scope opener, it belongs to a
1195
+				// DEFAULT or CASE statement.
1196
+				if (isset($this->tokens[$i]['scope_condition']) === true) {
1197
+					continue;
1198
+				}
1199
+
1200
+				// Make sure this is not part of an inline IF statement.
1201
+				for ($x = ($i - 1); $x >= 0; $x--) {
1202
+					if ($this->tokens[$x]['code'] === T_INLINE_THEN) {
1203
+						$this->tokens[$i]['code'] = T_INLINE_ELSE;
1204
+						$this->tokens[$i]['type'] = 'T_INLINE_ELSE';
1205
+
1206
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1207
+							echo str_repeat("\t", count($classStack));
1208
+							echo "\t* token $i converted from T_COLON to T_INLINE_THEN *".PHP_EOL;
1209
+						}
1210
+
1211
+						continue(2);
1212
+					} else if ($this->tokens[$x]['line'] < $this->tokens[$i]['line']) {
1213
+						break;
1214
+					}
1215
+				}
1216
+
1217
+				// The string to the left of the colon is either a property or label.
1218
+				for ($label = ($i - 1); $label >= 0; $label--) {
1219
+					if (isset(Util\Tokens::$emptyTokens[$this->tokens[$label]['code']]) === false) {
1220
+						break;
1221
+					}
1222
+				}
1223
+
1224
+				if ($this->tokens[$label]['code'] !== T_STRING
1225
+					&& $this->tokens[$label]['code'] !== T_CONSTANT_ENCAPSED_STRING
1226
+				) {
1227
+					continue;
1228
+				}
1229
+
1230
+				if (empty($classStack) === false) {
1231
+					$this->tokens[$label]['code'] = T_PROPERTY;
1232
+					$this->tokens[$label]['type'] = 'T_PROPERTY';
1233
+
1234
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1235
+						echo str_repeat("\t", count($classStack));
1236
+						echo "\t* token $label converted from T_STRING to T_PROPERTY *".PHP_EOL;
1237
+					}
1238
+				} else {
1239
+					$this->tokens[$label]['code'] = T_LABEL;
1240
+					$this->tokens[$label]['type'] = 'T_LABEL';
1241
+
1242
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
1243
+						echo str_repeat("\t", count($classStack));
1244
+						echo "\t* token $label converted from T_STRING to T_LABEL *".PHP_EOL;
1245
+					}
1246
+				}//end if
1247
+			}//end if
1248
+		}//end for
1249
+
1250
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
1251
+			echo "\t*** END ADDITIONAL JS PROCESSING ***".PHP_EOL;
1252
+		}
1253
+
1254
+	}//end processAdditional()
1255 1255
 
1256 1256
 
1257 1257
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Tokenizers/Comment.php 1 patch
Indentation   +238 added lines, -238 removed lines patch added patch discarded remove patch
@@ -15,263 +15,263 @@
 block discarded – undo
15 15
 {
16 16
 
17 17
 
18
-    /**
19
-     * Creates an array of tokens when given some PHP code.
20
-     *
21
-     * Starts by using token_get_all() but does a lot of extra processing
22
-     * to insert information about the context of the token.
23
-     *
24
-     * @param string $string   The string to tokenize.
25
-     * @param string $eolChar  The EOL character to use for splitting strings.
26
-     * @param int    $stackPtr The position of the first token in the file.
27
-     *
28
-     * @return array
29
-     */
30
-    public function tokenizeString($string, $eolChar, $stackPtr)
31
-    {
32
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
33
-            echo "\t\t*** START COMMENT TOKENIZING ***".PHP_EOL;
34
-        }
35
-
36
-        $tokens   = [];
37
-        $numChars = strlen($string);
38
-
39
-        /*
18
+	/**
19
+	 * Creates an array of tokens when given some PHP code.
20
+	 *
21
+	 * Starts by using token_get_all() but does a lot of extra processing
22
+	 * to insert information about the context of the token.
23
+	 *
24
+	 * @param string $string   The string to tokenize.
25
+	 * @param string $eolChar  The EOL character to use for splitting strings.
26
+	 * @param int    $stackPtr The position of the first token in the file.
27
+	 *
28
+	 * @return array
29
+	 */
30
+	public function tokenizeString($string, $eolChar, $stackPtr)
31
+	{
32
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
33
+			echo "\t\t*** START COMMENT TOKENIZING ***".PHP_EOL;
34
+		}
35
+
36
+		$tokens   = [];
37
+		$numChars = strlen($string);
38
+
39
+		/*
40 40
             Doc block comments start with /*, but typically contain an
41 41
             extra star when they are used for function and class comments.
42 42
         */
43 43
 
44
-        $char    = ($numChars - strlen(ltrim($string, '/*')));
45
-        $openTag = substr($string, 0, $char);
46
-        $string  = ltrim($string, '/*');
44
+		$char    = ($numChars - strlen(ltrim($string, '/*')));
45
+		$openTag = substr($string, 0, $char);
46
+		$string  = ltrim($string, '/*');
47 47
 
48
-        $tokens[$stackPtr] = [
49
-            'content'      => $openTag,
50
-            'code'         => T_DOC_COMMENT_OPEN_TAG,
51
-            'type'         => 'T_DOC_COMMENT_OPEN_TAG',
52
-            'comment_tags' => [],
53
-        ];
48
+		$tokens[$stackPtr] = [
49
+			'content'      => $openTag,
50
+			'code'         => T_DOC_COMMENT_OPEN_TAG,
51
+			'type'         => 'T_DOC_COMMENT_OPEN_TAG',
52
+			'comment_tags' => [],
53
+		];
54 54
 
55
-        $openPtr = $stackPtr;
56
-        $stackPtr++;
55
+		$openPtr = $stackPtr;
56
+		$stackPtr++;
57 57
 
58
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
59
-            $content = Util\Common::prepareForOutput($openTag);
60
-            echo "\t\tCreate comment token: T_DOC_COMMENT_OPEN_TAG => $content".PHP_EOL;
61
-        }
58
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
59
+			$content = Util\Common::prepareForOutput($openTag);
60
+			echo "\t\tCreate comment token: T_DOC_COMMENT_OPEN_TAG => $content".PHP_EOL;
61
+		}
62 62
 
63
-        /*
63
+		/*
64 64
             Strip off the close tag so it doesn't interfere with any
65 65
             of our comment line processing. The token will be added to the
66 66
             stack just before we return it.
67 67
         */
68 68
 
69
-        $closeTag = [
70
-            'content'        => substr($string, strlen(rtrim($string, '/*'))),
71
-            'code'           => T_DOC_COMMENT_CLOSE_TAG,
72
-            'type'           => 'T_DOC_COMMENT_CLOSE_TAG',
73
-            'comment_opener' => $openPtr,
74
-        ];
69
+		$closeTag = [
70
+			'content'        => substr($string, strlen(rtrim($string, '/*'))),
71
+			'code'           => T_DOC_COMMENT_CLOSE_TAG,
72
+			'type'           => 'T_DOC_COMMENT_CLOSE_TAG',
73
+			'comment_opener' => $openPtr,
74
+		];
75 75
 
76
-        if ($closeTag['content'] === false) {
77
-            $closeTag['content'] = '';
78
-        }
76
+		if ($closeTag['content'] === false) {
77
+			$closeTag['content'] = '';
78
+		}
79 79
 
80
-        $string = rtrim($string, '/*');
80
+		$string = rtrim($string, '/*');
81 81
 
82
-        /*
82
+		/*
83 83
             Process each line of the comment.
84 84
         */
85 85
 
86
-        $lines    = explode($eolChar, $string);
87
-        $numLines = count($lines);
88
-        foreach ($lines as $lineNum => $string) {
89
-            if ($lineNum !== ($numLines - 1)) {
90
-                $string .= $eolChar;
91
-            }
92
-
93
-            $char     = 0;
94
-            $numChars = strlen($string);
95
-
96
-            // We've started a new line, so process the indent.
97
-            $space = $this->collectWhitespace($string, $char, $numChars);
98
-            if ($space !== null) {
99
-                $tokens[$stackPtr] = $space;
100
-                $stackPtr++;
101
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
102
-                    $content = Util\Common::prepareForOutput($space['content']);
103
-                    echo "\t\tCreate comment token: T_DOC_COMMENT_WHITESPACE => $content".PHP_EOL;
104
-                }
105
-
106
-                $char += strlen($space['content']);
107
-                if ($char === $numChars) {
108
-                    break;
109
-                }
110
-            }
111
-
112
-            if ($string === '') {
113
-                continue;
114
-            }
115
-
116
-            if ($lineNum > 0 && $string[$char] === '*') {
117
-                // This is a function or class doc block line.
118
-                $char++;
119
-                $tokens[$stackPtr] = [
120
-                    'content' => '*',
121
-                    'code'    => T_DOC_COMMENT_STAR,
122
-                    'type'    => 'T_DOC_COMMENT_STAR',
123
-                ];
124
-
125
-                $stackPtr++;
126
-
127
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
128
-                    echo "\t\tCreate comment token: T_DOC_COMMENT_STAR => *".PHP_EOL;
129
-                }
130
-            }
131
-
132
-            // Now we are ready to process the actual content of the line.
133
-            $lineTokens = $this->processLine($string, $eolChar, $char, $numChars);
134
-            foreach ($lineTokens as $lineToken) {
135
-                $tokens[$stackPtr] = $lineToken;
136
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
137
-                    $content = Util\Common::prepareForOutput($lineToken['content']);
138
-                    $type    = $lineToken['type'];
139
-                    echo "\t\tCreate comment token: $type => $content".PHP_EOL;
140
-                }
141
-
142
-                if ($lineToken['code'] === T_DOC_COMMENT_TAG) {
143
-                    $tokens[$openPtr]['comment_tags'][] = $stackPtr;
144
-                }
145
-
146
-                $stackPtr++;
147
-            }
148
-        }//end foreach
149
-
150
-        $tokens[$stackPtr] = $closeTag;
151
-        $tokens[$openPtr]['comment_closer'] = $stackPtr;
152
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
153
-            $content = Util\Common::prepareForOutput($closeTag['content']);
154
-            echo "\t\tCreate comment token: T_DOC_COMMENT_CLOSE_TAG => $content".PHP_EOL;
155
-        }
156
-
157
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
158
-            echo "\t\t*** END COMMENT TOKENIZING ***".PHP_EOL;
159
-        }
160
-
161
-        return $tokens;
162
-
163
-    }//end tokenizeString()
164
-
165
-
166
-    /**
167
-     * Process a single line of a comment.
168
-     *
169
-     * @param string $string  The comment string being tokenized.
170
-     * @param string $eolChar The EOL character to use for splitting strings.
171
-     * @param int    $start   The position in the string to start processing.
172
-     * @param int    $end     The position in the string to end processing.
173
-     *
174
-     * @return array
175
-     */
176
-    private function processLine($string, $eolChar, $start, $end)
177
-    {
178
-        $tokens = [];
179
-
180
-        // Collect content padding.
181
-        $space = $this->collectWhitespace($string, $start, $end);
182
-        if ($space !== null) {
183
-            $tokens[] = $space;
184
-            $start   += strlen($space['content']);
185
-        }
186
-
187
-        if (isset($string[$start]) === false) {
188
-            return $tokens;
189
-        }
190
-
191
-        if ($string[$start] === '@') {
192
-            // The content up until the first whitespace is the tag name.
193
-            $matches = [];
194
-            preg_match('/@[^\s]+/', $string, $matches, 0, $start);
195
-            if (isset($matches[0]) === true
196
-                && substr(strtolower($matches[0]), 0, 7) !== '@phpcs:'
197
-            ) {
198
-                $tagName  = $matches[0];
199
-                $start   += strlen($tagName);
200
-                $tokens[] = [
201
-                    'content' => $tagName,
202
-                    'code'    => T_DOC_COMMENT_TAG,
203
-                    'type'    => 'T_DOC_COMMENT_TAG',
204
-                ];
205
-
206
-                // Then there will be some whitespace.
207
-                $space = $this->collectWhitespace($string, $start, $end);
208
-                if ($space !== null) {
209
-                    $tokens[] = $space;
210
-                    $start   += strlen($space['content']);
211
-                }
212
-            }
213
-        }//end if
214
-
215
-        // Process the rest of the line.
216
-        $eol = strpos($string, $eolChar, $start);
217
-        if ($eol === false) {
218
-            $eol = $end;
219
-        }
220
-
221
-        if ($eol > $start) {
222
-            $tokens[] = [
223
-                'content' => substr($string, $start, ($eol - $start)),
224
-                'code'    => T_DOC_COMMENT_STRING,
225
-                'type'    => 'T_DOC_COMMENT_STRING',
226
-            ];
227
-        }
228
-
229
-        if ($eol !== $end) {
230
-            $tokens[] = [
231
-                'content' => substr($string, $eol, strlen($eolChar)),
232
-                'code'    => T_DOC_COMMENT_WHITESPACE,
233
-                'type'    => 'T_DOC_COMMENT_WHITESPACE',
234
-            ];
235
-        }
236
-
237
-        return $tokens;
238
-
239
-    }//end processLine()
240
-
241
-
242
-    /**
243
-     * Collect consecutive whitespace into a single token.
244
-     *
245
-     * @param string $string The comment string being tokenized.
246
-     * @param int    $start  The position in the string to start processing.
247
-     * @param int    $end    The position in the string to end processing.
248
-     *
249
-     * @return array|null
250
-     */
251
-    private function collectWhitespace($string, $start, $end)
252
-    {
253
-        $space = '';
254
-        for ($start; $start < $end; $start++) {
255
-            if ($string[$start] !== ' ' && $string[$start] !== "\t") {
256
-                break;
257
-            }
258
-
259
-            $space .= $string[$start];
260
-        }
261
-
262
-        if ($space === '') {
263
-            return null;
264
-        }
265
-
266
-        $token = [
267
-            'content' => $space,
268
-            'code'    => T_DOC_COMMENT_WHITESPACE,
269
-            'type'    => 'T_DOC_COMMENT_WHITESPACE',
270
-        ];
271
-
272
-        return $token;
273
-
274
-    }//end collectWhitespace()
86
+		$lines    = explode($eolChar, $string);
87
+		$numLines = count($lines);
88
+		foreach ($lines as $lineNum => $string) {
89
+			if ($lineNum !== ($numLines - 1)) {
90
+				$string .= $eolChar;
91
+			}
92
+
93
+			$char     = 0;
94
+			$numChars = strlen($string);
95
+
96
+			// We've started a new line, so process the indent.
97
+			$space = $this->collectWhitespace($string, $char, $numChars);
98
+			if ($space !== null) {
99
+				$tokens[$stackPtr] = $space;
100
+				$stackPtr++;
101
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
102
+					$content = Util\Common::prepareForOutput($space['content']);
103
+					echo "\t\tCreate comment token: T_DOC_COMMENT_WHITESPACE => $content".PHP_EOL;
104
+				}
105
+
106
+				$char += strlen($space['content']);
107
+				if ($char === $numChars) {
108
+					break;
109
+				}
110
+			}
111
+
112
+			if ($string === '') {
113
+				continue;
114
+			}
115
+
116
+			if ($lineNum > 0 && $string[$char] === '*') {
117
+				// This is a function or class doc block line.
118
+				$char++;
119
+				$tokens[$stackPtr] = [
120
+					'content' => '*',
121
+					'code'    => T_DOC_COMMENT_STAR,
122
+					'type'    => 'T_DOC_COMMENT_STAR',
123
+				];
124
+
125
+				$stackPtr++;
126
+
127
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
128
+					echo "\t\tCreate comment token: T_DOC_COMMENT_STAR => *".PHP_EOL;
129
+				}
130
+			}
131
+
132
+			// Now we are ready to process the actual content of the line.
133
+			$lineTokens = $this->processLine($string, $eolChar, $char, $numChars);
134
+			foreach ($lineTokens as $lineToken) {
135
+				$tokens[$stackPtr] = $lineToken;
136
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
137
+					$content = Util\Common::prepareForOutput($lineToken['content']);
138
+					$type    = $lineToken['type'];
139
+					echo "\t\tCreate comment token: $type => $content".PHP_EOL;
140
+				}
141
+
142
+				if ($lineToken['code'] === T_DOC_COMMENT_TAG) {
143
+					$tokens[$openPtr]['comment_tags'][] = $stackPtr;
144
+				}
145
+
146
+				$stackPtr++;
147
+			}
148
+		}//end foreach
149
+
150
+		$tokens[$stackPtr] = $closeTag;
151
+		$tokens[$openPtr]['comment_closer'] = $stackPtr;
152
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
153
+			$content = Util\Common::prepareForOutput($closeTag['content']);
154
+			echo "\t\tCreate comment token: T_DOC_COMMENT_CLOSE_TAG => $content".PHP_EOL;
155
+		}
156
+
157
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
158
+			echo "\t\t*** END COMMENT TOKENIZING ***".PHP_EOL;
159
+		}
160
+
161
+		return $tokens;
162
+
163
+	}//end tokenizeString()
164
+
165
+
166
+	/**
167
+	 * Process a single line of a comment.
168
+	 *
169
+	 * @param string $string  The comment string being tokenized.
170
+	 * @param string $eolChar The EOL character to use for splitting strings.
171
+	 * @param int    $start   The position in the string to start processing.
172
+	 * @param int    $end     The position in the string to end processing.
173
+	 *
174
+	 * @return array
175
+	 */
176
+	private function processLine($string, $eolChar, $start, $end)
177
+	{
178
+		$tokens = [];
179
+
180
+		// Collect content padding.
181
+		$space = $this->collectWhitespace($string, $start, $end);
182
+		if ($space !== null) {
183
+			$tokens[] = $space;
184
+			$start   += strlen($space['content']);
185
+		}
186
+
187
+		if (isset($string[$start]) === false) {
188
+			return $tokens;
189
+		}
190
+
191
+		if ($string[$start] === '@') {
192
+			// The content up until the first whitespace is the tag name.
193
+			$matches = [];
194
+			preg_match('/@[^\s]+/', $string, $matches, 0, $start);
195
+			if (isset($matches[0]) === true
196
+				&& substr(strtolower($matches[0]), 0, 7) !== '@phpcs:'
197
+			) {
198
+				$tagName  = $matches[0];
199
+				$start   += strlen($tagName);
200
+				$tokens[] = [
201
+					'content' => $tagName,
202
+					'code'    => T_DOC_COMMENT_TAG,
203
+					'type'    => 'T_DOC_COMMENT_TAG',
204
+				];
205
+
206
+				// Then there will be some whitespace.
207
+				$space = $this->collectWhitespace($string, $start, $end);
208
+				if ($space !== null) {
209
+					$tokens[] = $space;
210
+					$start   += strlen($space['content']);
211
+				}
212
+			}
213
+		}//end if
214
+
215
+		// Process the rest of the line.
216
+		$eol = strpos($string, $eolChar, $start);
217
+		if ($eol === false) {
218
+			$eol = $end;
219
+		}
220
+
221
+		if ($eol > $start) {
222
+			$tokens[] = [
223
+				'content' => substr($string, $start, ($eol - $start)),
224
+				'code'    => T_DOC_COMMENT_STRING,
225
+				'type'    => 'T_DOC_COMMENT_STRING',
226
+			];
227
+		}
228
+
229
+		if ($eol !== $end) {
230
+			$tokens[] = [
231
+				'content' => substr($string, $eol, strlen($eolChar)),
232
+				'code'    => T_DOC_COMMENT_WHITESPACE,
233
+				'type'    => 'T_DOC_COMMENT_WHITESPACE',
234
+			];
235
+		}
236
+
237
+		return $tokens;
238
+
239
+	}//end processLine()
240
+
241
+
242
+	/**
243
+	 * Collect consecutive whitespace into a single token.
244
+	 *
245
+	 * @param string $string The comment string being tokenized.
246
+	 * @param int    $start  The position in the string to start processing.
247
+	 * @param int    $end    The position in the string to end processing.
248
+	 *
249
+	 * @return array|null
250
+	 */
251
+	private function collectWhitespace($string, $start, $end)
252
+	{
253
+		$space = '';
254
+		for ($start; $start < $end; $start++) {
255
+			if ($string[$start] !== ' ' && $string[$start] !== "\t") {
256
+				break;
257
+			}
258
+
259
+			$space .= $string[$start];
260
+		}
261
+
262
+		if ($space === '') {
263
+			return null;
264
+		}
265
+
266
+		$token = [
267
+			'content' => $space,
268
+			'code'    => T_DOC_COMMENT_WHITESPACE,
269
+			'type'    => 'T_DOC_COMMENT_WHITESPACE',
270
+		];
271
+
272
+		return $token;
273
+
274
+	}//end collectWhitespace()
275 275
 
276 276
 
277 277
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Files/File.php 1 patch
Indentation   +2461 added lines, -2461 removed lines patch added patch discarded remove patch
@@ -19,2467 +19,2467 @@
 block discarded – undo
19 19
 class File
20 20
 {
21 21
 
22
-    /**
23
-     * The absolute path to the file associated with this object.
24
-     *
25
-     * @var string
26
-     */
27
-    public $path = '';
28
-
29
-    /**
30
-     * The content of the file.
31
-     *
32
-     * @var string
33
-     */
34
-    protected $content = '';
35
-
36
-    /**
37
-     * The config data for the run.
38
-     *
39
-     * @var \PHP_CodeSniffer\Config
40
-     */
41
-    public $config = null;
42
-
43
-    /**
44
-     * The ruleset used for the run.
45
-     *
46
-     * @var \PHP_CodeSniffer\Ruleset
47
-     */
48
-    public $ruleset = null;
49
-
50
-    /**
51
-     * If TRUE, the entire file is being ignored.
52
-     *
53
-     * @var boolean
54
-     */
55
-    public $ignored = false;
56
-
57
-    /**
58
-     * The EOL character this file uses.
59
-     *
60
-     * @var string
61
-     */
62
-    public $eolChar = '';
63
-
64
-    /**
65
-     * The Fixer object to control fixing errors.
66
-     *
67
-     * @var \PHP_CodeSniffer\Fixer
68
-     */
69
-    public $fixer = null;
70
-
71
-    /**
72
-     * The tokenizer being used for this file.
73
-     *
74
-     * @var \PHP_CodeSniffer\Tokenizers\Tokenizer
75
-     */
76
-    public $tokenizer = null;
77
-
78
-    /**
79
-     * The name of the tokenizer being used for this file.
80
-     *
81
-     * @var string
82
-     */
83
-    public $tokenizerType = 'PHP';
84
-
85
-    /**
86
-     * Was the file loaded from cache?
87
-     *
88
-     * If TRUE, the file was loaded from a local cache.
89
-     * If FALSE, the file was tokenized and processed fully.
90
-     *
91
-     * @var boolean
92
-     */
93
-    public $fromCache = false;
94
-
95
-    /**
96
-     * The number of tokens in this file.
97
-     *
98
-     * Stored here to save calling count() everywhere.
99
-     *
100
-     * @var integer
101
-     */
102
-    public $numTokens = 0;
103
-
104
-    /**
105
-     * The tokens stack map.
106
-     *
107
-     * @var array
108
-     */
109
-    protected $tokens = [];
110
-
111
-    /**
112
-     * The errors raised from sniffs.
113
-     *
114
-     * @var array
115
-     * @see getErrors()
116
-     */
117
-    protected $errors = [];
118
-
119
-    /**
120
-     * The warnings raised from sniffs.
121
-     *
122
-     * @var array
123
-     * @see getWarnings()
124
-     */
125
-    protected $warnings = [];
126
-
127
-    /**
128
-     * The metrics recorded by sniffs.
129
-     *
130
-     * @var array
131
-     * @see getMetrics()
132
-     */
133
-    protected $metrics = [];
134
-
135
-    /**
136
-     * The metrics recorded for each token.
137
-     *
138
-     * Stops the same metric being recorded for the same token twice.
139
-     *
140
-     * @var array
141
-     * @see getMetrics()
142
-     */
143
-    private $metricTokens = [];
144
-
145
-    /**
146
-     * The total number of errors raised.
147
-     *
148
-     * @var integer
149
-     */
150
-    protected $errorCount = 0;
151
-
152
-    /**
153
-     * The total number of warnings raised.
154
-     *
155
-     * @var integer
156
-     */
157
-    protected $warningCount = 0;
158
-
159
-    /**
160
-     * The total number of errors and warnings that can be fixed.
161
-     *
162
-     * @var integer
163
-     */
164
-    protected $fixableCount = 0;
165
-
166
-    /**
167
-     * The total number of errors and warnings that were fixed.
168
-     *
169
-     * @var integer
170
-     */
171
-    protected $fixedCount = 0;
172
-
173
-    /**
174
-     * An array of sniffs that are being ignored.
175
-     *
176
-     * @var array
177
-     */
178
-    protected $ignoredListeners = [];
179
-
180
-    /**
181
-     * An array of message codes that are being ignored.
182
-     *
183
-     * @var array
184
-     */
185
-    protected $ignoredCodes = [];
186
-
187
-    /**
188
-     * An array of sniffs listening to this file's processing.
189
-     *
190
-     * @var \PHP_CodeSniffer\Sniffs\Sniff[]
191
-     */
192
-    protected $listeners = [];
193
-
194
-    /**
195
-     * The class name of the sniff currently processing the file.
196
-     *
197
-     * @var string
198
-     */
199
-    protected $activeListener = '';
200
-
201
-    /**
202
-     * An array of sniffs being processed and how long they took.
203
-     *
204
-     * @var array
205
-     */
206
-    protected $listenerTimes = [];
207
-
208
-    /**
209
-     * A cache of often used config settings to improve performance.
210
-     *
211
-     * Storing them here saves 10k+ calls to __get() in the Config class.
212
-     *
213
-     * @var array
214
-     */
215
-    protected $configCache = [];
216
-
217
-
218
-    /**
219
-     * Constructs a file.
220
-     *
221
-     * @param string                   $path    The absolute path to the file to process.
222
-     * @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
223
-     * @param \PHP_CodeSniffer\Config  $config  The config data for the run.
224
-     *
225
-     * @return void
226
-     */
227
-    public function __construct($path, Ruleset $ruleset, Config $config)
228
-    {
229
-        $this->path    = $path;
230
-        $this->ruleset = $ruleset;
231
-        $this->config  = $config;
232
-        $this->fixer   = new Fixer();
233
-
234
-        $parts     = explode('.', $path);
235
-        $extension = array_pop($parts);
236
-        if (isset($config->extensions[$extension]) === true) {
237
-            $this->tokenizerType = $config->extensions[$extension];
238
-        } else {
239
-            // Revert to default.
240
-            $this->tokenizerType = 'PHP';
241
-        }
242
-
243
-        $this->configCache['cache']           = $this->config->cache;
244
-        $this->configCache['sniffs']          = array_map('strtolower', $this->config->sniffs);
245
-        $this->configCache['exclude']         = array_map('strtolower', $this->config->exclude);
246
-        $this->configCache['errorSeverity']   = $this->config->errorSeverity;
247
-        $this->configCache['warningSeverity'] = $this->config->warningSeverity;
248
-        $this->configCache['recordErrors']    = $this->config->recordErrors;
249
-        $this->configCache['ignorePatterns']  = $this->ruleset->ignorePatterns;
250
-        $this->configCache['includePatterns'] = $this->ruleset->includePatterns;
251
-
252
-    }//end __construct()
253
-
254
-
255
-    /**
256
-     * Set the content of the file.
257
-     *
258
-     * Setting the content also calculates the EOL char being used.
259
-     *
260
-     * @param string $content The file content.
261
-     *
262
-     * @return void
263
-     */
264
-    public function setContent($content)
265
-    {
266
-        $this->content = $content;
267
-        $this->tokens  = [];
268
-
269
-        try {
270
-            $this->eolChar = Util\Common::detectLineEndings($content);
271
-        } catch (RuntimeException $e) {
272
-            $this->addWarningOnLine($e->getMessage(), 1, 'Internal.DetectLineEndings');
273
-            return;
274
-        }
275
-
276
-    }//end setContent()
277
-
278
-
279
-    /**
280
-     * Reloads the content of the file.
281
-     *
282
-     * By default, we have no idea where our content comes from,
283
-     * so we can't do anything.
284
-     *
285
-     * @return void
286
-     */
287
-    public function reloadContent()
288
-    {
289
-
290
-    }//end reloadContent()
291
-
292
-
293
-    /**
294
-     * Disables caching of this file.
295
-     *
296
-     * @return void
297
-     */
298
-    public function disableCaching()
299
-    {
300
-        $this->configCache['cache'] = false;
301
-
302
-    }//end disableCaching()
303
-
304
-
305
-    /**
306
-     * Starts the stack traversal and tells listeners when tokens are found.
307
-     *
308
-     * @return void
309
-     */
310
-    public function process()
311
-    {
312
-        if ($this->ignored === true) {
313
-            return;
314
-        }
315
-
316
-        $this->errors       = [];
317
-        $this->warnings     = [];
318
-        $this->errorCount   = 0;
319
-        $this->warningCount = 0;
320
-        $this->fixableCount = 0;
321
-
322
-        $this->parse();
323
-
324
-        // Check if tokenizer errors cause this file to be ignored.
325
-        if ($this->ignored === true) {
326
-            return;
327
-        }
328
-
329
-        $this->fixer->startFile($this);
330
-
331
-        if (PHP_CODESNIFFER_VERBOSITY > 2) {
332
-            echo "\t*** START TOKEN PROCESSING ***".PHP_EOL;
333
-        }
334
-
335
-        $foundCode        = false;
336
-        $listenerIgnoreTo = [];
337
-        $inTests          = defined('PHP_CODESNIFFER_IN_TESTS');
338
-        $checkAnnotations = $this->config->annotations;
339
-
340
-        // Foreach of the listeners that have registered to listen for this
341
-        // token, get them to process it.
342
-        foreach ($this->tokens as $stackPtr => $token) {
343
-            // Check for ignored lines.
344
-            if ($checkAnnotations === true
345
-                && ($token['code'] === T_COMMENT
346
-                || $token['code'] === T_PHPCS_IGNORE_FILE
347
-                || $token['code'] === T_PHPCS_SET
348
-                || $token['code'] === T_DOC_COMMENT_STRING
349
-                || $token['code'] === T_DOC_COMMENT_TAG
350
-                || ($inTests === true && $token['code'] === T_INLINE_HTML))
351
-            ) {
352
-                $commentText      = ltrim($this->tokens[$stackPtr]['content'], ' /*');
353
-                $commentTextLower = strtolower($commentText);
354
-                if (strpos($commentText, '@codingStandards') !== false) {
355
-                    if (strpos($commentText, '@codingStandardsIgnoreFile') !== false) {
356
-                        // Ignoring the whole file, just a little late.
357
-                        $this->errors       = [];
358
-                        $this->warnings     = [];
359
-                        $this->errorCount   = 0;
360
-                        $this->warningCount = 0;
361
-                        $this->fixableCount = 0;
362
-                        return;
363
-                    } else if (strpos($commentText, '@codingStandardsChangeSetting') !== false) {
364
-                        $start   = strpos($commentText, '@codingStandardsChangeSetting');
365
-                        $comment = substr($commentText, ($start + 30));
366
-                        $parts   = explode(' ', $comment);
367
-                        if (count($parts) >= 2) {
368
-                            $sniffParts = explode('.', $parts[0]);
369
-                            if (count($sniffParts) >= 3) {
370
-                                // If the sniff code is not known to us, it has not been registered in this run.
371
-                                // But don't throw an error as it could be there for a different standard to use.
372
-                                if (isset($this->ruleset->sniffCodes[$parts[0]]) === true) {
373
-                                    $listenerCode  = array_shift($parts);
374
-                                    $propertyCode  = array_shift($parts);
375
-                                    $propertyValue = rtrim(implode(' ', $parts), " */\r\n");
376
-                                    $listenerClass = $this->ruleset->sniffCodes[$listenerCode];
377
-                                    $this->ruleset->setSniffProperty($listenerClass, $propertyCode, $propertyValue);
378
-                                }
379
-                            }
380
-                        }
381
-                    }//end if
382
-                } else if (substr($commentTextLower, 0, 16) === 'phpcs:ignorefile'
383
-                    || substr($commentTextLower, 0, 17) === '@phpcs:ignorefile'
384
-                ) {
385
-                    // Ignoring the whole file, just a little late.
386
-                    $this->errors       = [];
387
-                    $this->warnings     = [];
388
-                    $this->errorCount   = 0;
389
-                    $this->warningCount = 0;
390
-                    $this->fixableCount = 0;
391
-                    return;
392
-                } else if (substr($commentTextLower, 0, 9) === 'phpcs:set'
393
-                    || substr($commentTextLower, 0, 10) === '@phpcs:set'
394
-                ) {
395
-                    if (isset($token['sniffCode']) === true) {
396
-                        $listenerCode = $token['sniffCode'];
397
-                        if (isset($this->ruleset->sniffCodes[$listenerCode]) === true) {
398
-                            $propertyCode  = $token['sniffProperty'];
399
-                            $propertyValue = $token['sniffPropertyValue'];
400
-                            $listenerClass = $this->ruleset->sniffCodes[$listenerCode];
401
-                            $this->ruleset->setSniffProperty($listenerClass, $propertyCode, $propertyValue);
402
-                        }
403
-                    }
404
-                }//end if
405
-            }//end if
406
-
407
-            if (PHP_CODESNIFFER_VERBOSITY > 2) {
408
-                $type    = $token['type'];
409
-                $content = Util\Common::prepareForOutput($token['content']);
410
-                echo "\t\tProcess token $stackPtr: $type => $content".PHP_EOL;
411
-            }
412
-
413
-            if ($token['code'] !== T_INLINE_HTML) {
414
-                $foundCode = true;
415
-            }
416
-
417
-            if (isset($this->ruleset->tokenListeners[$token['code']]) === false) {
418
-                continue;
419
-            }
420
-
421
-            foreach ($this->ruleset->tokenListeners[$token['code']] as $listenerData) {
422
-                if (isset($this->ignoredListeners[$listenerData['class']]) === true
423
-                    || (isset($listenerIgnoreTo[$listenerData['class']]) === true
424
-                    && $listenerIgnoreTo[$listenerData['class']] > $stackPtr)
425
-                ) {
426
-                    // This sniff is ignoring past this token, or the whole file.
427
-                    continue;
428
-                }
429
-
430
-                // Make sure this sniff supports the tokenizer
431
-                // we are currently using.
432
-                $class = $listenerData['class'];
433
-
434
-                if (isset($listenerData['tokenizers'][$this->tokenizerType]) === false) {
435
-                    continue;
436
-                }
437
-
438
-                // If the file path matches one of our ignore patterns, skip it.
439
-                // While there is support for a type of each pattern
440
-                // (absolute or relative) we don't actually support it here.
441
-                foreach ($listenerData['ignore'] as $pattern) {
442
-                    // We assume a / directory separator, as do the exclude rules
443
-                    // most developers write, so we need a special case for any system
444
-                    // that is different.
445
-                    if (DIRECTORY_SEPARATOR === '\\') {
446
-                        $pattern = str_replace('/', '\\\\', $pattern);
447
-                    }
448
-
449
-                    $pattern = '`'.$pattern.'`i';
450
-                    if (preg_match($pattern, $this->path) === 1) {
451
-                        $this->ignoredListeners[$class] = true;
452
-                        continue(2);
453
-                    }
454
-                }
455
-
456
-                // If the file path does not match one of our include patterns, skip it.
457
-                // While there is support for a type of each pattern
458
-                // (absolute or relative) we don't actually support it here.
459
-                if (empty($listenerData['include']) === false) {
460
-                    $included = false;
461
-                    foreach ($listenerData['include'] as $pattern) {
462
-                        // We assume a / directory separator, as do the exclude rules
463
-                        // most developers write, so we need a special case for any system
464
-                        // that is different.
465
-                        if (DIRECTORY_SEPARATOR === '\\') {
466
-                            $pattern = str_replace('/', '\\\\', $pattern);
467
-                        }
468
-
469
-                        $pattern = '`'.$pattern.'`i';
470
-                        if (preg_match($pattern, $this->path) === 1) {
471
-                            $included = true;
472
-                            break;
473
-                        }
474
-                    }
475
-
476
-                    if ($included === false) {
477
-                        $this->ignoredListeners[$class] = true;
478
-                        continue;
479
-                    }
480
-                }//end if
481
-
482
-                $this->activeListener = $class;
483
-
484
-                if (PHP_CODESNIFFER_VERBOSITY > 2) {
485
-                    $startTime = microtime(true);
486
-                    echo "\t\t\tProcessing ".$this->activeListener.'... ';
487
-                }
488
-
489
-                $ignoreTo = $this->ruleset->sniffs[$class]->process($this, $stackPtr);
490
-                if ($ignoreTo !== null) {
491
-                    $listenerIgnoreTo[$this->activeListener] = $ignoreTo;
492
-                }
493
-
494
-                if (PHP_CODESNIFFER_VERBOSITY > 2) {
495
-                    $timeTaken = (microtime(true) - $startTime);
496
-                    if (isset($this->listenerTimes[$this->activeListener]) === false) {
497
-                        $this->listenerTimes[$this->activeListener] = 0;
498
-                    }
499
-
500
-                    $this->listenerTimes[$this->activeListener] += $timeTaken;
501
-
502
-                    $timeTaken = round(($timeTaken), 4);
503
-                    echo "DONE in $timeTaken seconds".PHP_EOL;
504
-                }
505
-
506
-                $this->activeListener = '';
507
-            }//end foreach
508
-        }//end foreach
509
-
510
-        // If short open tags are off but the file being checked uses
511
-        // short open tags, the whole content will be inline HTML
512
-        // and nothing will be checked. So try and handle this case.
513
-        // We don't show this error for STDIN because we can't be sure the content
514
-        // actually came directly from the user. It could be something like
515
-        // refs from a Git pre-push hook.
516
-        if ($foundCode === false && $this->tokenizerType === 'PHP' && $this->path !== 'STDIN') {
517
-            $shortTags = (bool) ini_get('short_open_tag');
518
-            if ($shortTags === false) {
519
-                $error = 'No PHP code was found in this file and short open tags are not allowed by this install of PHP. This file may be using short open tags but PHP does not allow them.';
520
-                $this->addWarning($error, null, 'Internal.NoCodeFound');
521
-            }
522
-        }
523
-
524
-        if (PHP_CODESNIFFER_VERBOSITY > 2) {
525
-            echo "\t*** END TOKEN PROCESSING ***".PHP_EOL;
526
-            echo "\t*** START SNIFF PROCESSING REPORT ***".PHP_EOL;
527
-
528
-            asort($this->listenerTimes, SORT_NUMERIC);
529
-            $this->listenerTimes = array_reverse($this->listenerTimes, true);
530
-            foreach ($this->listenerTimes as $listener => $timeTaken) {
531
-                echo "\t$listener: ".round(($timeTaken), 4).' secs'.PHP_EOL;
532
-            }
533
-
534
-            echo "\t*** END SNIFF PROCESSING REPORT ***".PHP_EOL;
535
-        }
536
-
537
-        $this->fixedCount += $this->fixer->getFixCount();
538
-
539
-    }//end process()
540
-
541
-
542
-    /**
543
-     * Tokenizes the file and prepares it for the test run.
544
-     *
545
-     * @return void
546
-     */
547
-    public function parse()
548
-    {
549
-        if (empty($this->tokens) === false) {
550
-            // File has already been parsed.
551
-            return;
552
-        }
553
-
554
-        try {
555
-            $tokenizerClass  = 'PHP_CodeSniffer\Tokenizers\\'.$this->tokenizerType;
556
-            $this->tokenizer = new $tokenizerClass($this->content, $this->config, $this->eolChar);
557
-            $this->tokens    = $this->tokenizer->getTokens();
558
-        } catch (TokenizerException $e) {
559
-            $this->ignored = true;
560
-            $this->addWarning($e->getMessage(), null, 'Internal.Tokenizer.Exception');
561
-            if (PHP_CODESNIFFER_VERBOSITY > 0) {
562
-                echo "[$this->tokenizerType => tokenizer error]... ";
563
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
564
-                    echo PHP_EOL;
565
-                }
566
-            }
567
-
568
-            return;
569
-        }
570
-
571
-        $this->numTokens = count($this->tokens);
572
-
573
-        // Check for mixed line endings as these can cause tokenizer errors and we
574
-        // should let the user know that the results they get may be incorrect.
575
-        // This is done by removing all backslashes, removing the newline char we
576
-        // detected, then converting newlines chars into text. If any backslashes
577
-        // are left at the end, we have additional newline chars in use.
578
-        $contents = str_replace('\\', '', $this->content);
579
-        $contents = str_replace($this->eolChar, '', $contents);
580
-        $contents = str_replace("\n", '\n', $contents);
581
-        $contents = str_replace("\r", '\r', $contents);
582
-        if (strpos($contents, '\\') !== false) {
583
-            $error = 'File has mixed line endings; this may cause incorrect results';
584
-            $this->addWarningOnLine($error, 1, 'Internal.LineEndings.Mixed');
585
-        }
586
-
587
-        if (PHP_CODESNIFFER_VERBOSITY > 0) {
588
-            if ($this->numTokens === 0) {
589
-                $numLines = 0;
590
-            } else {
591
-                $numLines = $this->tokens[($this->numTokens - 1)]['line'];
592
-            }
593
-
594
-            echo "[$this->tokenizerType => $this->numTokens tokens in $numLines lines]... ";
595
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
596
-                echo PHP_EOL;
597
-            }
598
-        }
599
-
600
-    }//end parse()
601
-
602
-
603
-    /**
604
-     * Returns the token stack for this file.
605
-     *
606
-     * @return array
607
-     */
608
-    public function getTokens()
609
-    {
610
-        return $this->tokens;
611
-
612
-    }//end getTokens()
613
-
614
-
615
-    /**
616
-     * Remove vars stored in this file that are no longer required.
617
-     *
618
-     * @return void
619
-     */
620
-    public function cleanUp()
621
-    {
622
-        $this->listenerTimes = null;
623
-        $this->content       = null;
624
-        $this->tokens        = null;
625
-        $this->metricTokens  = null;
626
-        $this->tokenizer     = null;
627
-        $this->fixer         = null;
628
-        $this->config        = null;
629
-        $this->ruleset       = null;
630
-
631
-    }//end cleanUp()
632
-
633
-
634
-    /**
635
-     * Records an error against a specific token in the file.
636
-     *
637
-     * @param string  $error    The error message.
638
-     * @param int     $stackPtr The stack position where the error occurred.
639
-     * @param string  $code     A violation code unique to the sniff message.
640
-     * @param array   $data     Replacements for the error message.
641
-     * @param int     $severity The severity level for this error. A value of 0
642
-     *                          will be converted into the default severity level.
643
-     * @param boolean $fixable  Can the error be fixed by the sniff?
644
-     *
645
-     * @return boolean
646
-     */
647
-    public function addError(
648
-        $error,
649
-        $stackPtr,
650
-        $code,
651
-        $data=[],
652
-        $severity=0,
653
-        $fixable=false
654
-    ) {
655
-        if ($stackPtr === null) {
656
-            $line   = 1;
657
-            $column = 1;
658
-        } else {
659
-            $line   = $this->tokens[$stackPtr]['line'];
660
-            $column = $this->tokens[$stackPtr]['column'];
661
-        }
662
-
663
-        return $this->addMessage(true, $error, $line, $column, $code, $data, $severity, $fixable);
664
-
665
-    }//end addError()
666
-
667
-
668
-    /**
669
-     * Records a warning against a specific token in the file.
670
-     *
671
-     * @param string  $warning  The error message.
672
-     * @param int     $stackPtr The stack position where the error occurred.
673
-     * @param string  $code     A violation code unique to the sniff message.
674
-     * @param array   $data     Replacements for the warning message.
675
-     * @param int     $severity The severity level for this warning. A value of 0
676
-     *                          will be converted into the default severity level.
677
-     * @param boolean $fixable  Can the warning be fixed by the sniff?
678
-     *
679
-     * @return boolean
680
-     */
681
-    public function addWarning(
682
-        $warning,
683
-        $stackPtr,
684
-        $code,
685
-        $data=[],
686
-        $severity=0,
687
-        $fixable=false
688
-    ) {
689
-        if ($stackPtr === null) {
690
-            $line   = 1;
691
-            $column = 1;
692
-        } else {
693
-            $line   = $this->tokens[$stackPtr]['line'];
694
-            $column = $this->tokens[$stackPtr]['column'];
695
-        }
696
-
697
-        return $this->addMessage(false, $warning, $line, $column, $code, $data, $severity, $fixable);
698
-
699
-    }//end addWarning()
700
-
701
-
702
-    /**
703
-     * Records an error against a specific line in the file.
704
-     *
705
-     * @param string $error    The error message.
706
-     * @param int    $line     The line on which the error occurred.
707
-     * @param string $code     A violation code unique to the sniff message.
708
-     * @param array  $data     Replacements for the error message.
709
-     * @param int    $severity The severity level for this error. A value of 0
710
-     *                         will be converted into the default severity level.
711
-     *
712
-     * @return boolean
713
-     */
714
-    public function addErrorOnLine(
715
-        $error,
716
-        $line,
717
-        $code,
718
-        $data=[],
719
-        $severity=0
720
-    ) {
721
-        return $this->addMessage(true, $error, $line, 1, $code, $data, $severity, false);
722
-
723
-    }//end addErrorOnLine()
724
-
725
-
726
-    /**
727
-     * Records a warning against a specific token in the file.
728
-     *
729
-     * @param string $warning  The error message.
730
-     * @param int    $line     The line on which the warning occurred.
731
-     * @param string $code     A violation code unique to the sniff message.
732
-     * @param array  $data     Replacements for the warning message.
733
-     * @param int    $severity The severity level for this warning. A value of 0 will
734
-     *                         will be converted into the default severity level.
735
-     *
736
-     * @return boolean
737
-     */
738
-    public function addWarningOnLine(
739
-        $warning,
740
-        $line,
741
-        $code,
742
-        $data=[],
743
-        $severity=0
744
-    ) {
745
-        return $this->addMessage(false, $warning, $line, 1, $code, $data, $severity, false);
746
-
747
-    }//end addWarningOnLine()
748
-
749
-
750
-    /**
751
-     * Records a fixable error against a specific token in the file.
752
-     *
753
-     * Returns true if the error was recorded and should be fixed.
754
-     *
755
-     * @param string $error    The error message.
756
-     * @param int    $stackPtr The stack position where the error occurred.
757
-     * @param string $code     A violation code unique to the sniff message.
758
-     * @param array  $data     Replacements for the error message.
759
-     * @param int    $severity The severity level for this error. A value of 0
760
-     *                         will be converted into the default severity level.
761
-     *
762
-     * @return boolean
763
-     */
764
-    public function addFixableError(
765
-        $error,
766
-        $stackPtr,
767
-        $code,
768
-        $data=[],
769
-        $severity=0
770
-    ) {
771
-        $recorded = $this->addError($error, $stackPtr, $code, $data, $severity, true);
772
-        if ($recorded === true && $this->fixer->enabled === true) {
773
-            return true;
774
-        }
775
-
776
-        return false;
777
-
778
-    }//end addFixableError()
779
-
780
-
781
-    /**
782
-     * Records a fixable warning against a specific token in the file.
783
-     *
784
-     * Returns true if the warning was recorded and should be fixed.
785
-     *
786
-     * @param string $warning  The error message.
787
-     * @param int    $stackPtr The stack position where the error occurred.
788
-     * @param string $code     A violation code unique to the sniff message.
789
-     * @param array  $data     Replacements for the warning message.
790
-     * @param int    $severity The severity level for this warning. A value of 0
791
-     *                         will be converted into the default severity level.
792
-     *
793
-     * @return boolean
794
-     */
795
-    public function addFixableWarning(
796
-        $warning,
797
-        $stackPtr,
798
-        $code,
799
-        $data=[],
800
-        $severity=0
801
-    ) {
802
-        $recorded = $this->addWarning($warning, $stackPtr, $code, $data, $severity, true);
803
-        if ($recorded === true && $this->fixer->enabled === true) {
804
-            return true;
805
-        }
806
-
807
-        return false;
808
-
809
-    }//end addFixableWarning()
810
-
811
-
812
-    /**
813
-     * Adds an error to the error stack.
814
-     *
815
-     * @param boolean $error    Is this an error message?
816
-     * @param string  $message  The text of the message.
817
-     * @param int     $line     The line on which the message occurred.
818
-     * @param int     $column   The column at which the message occurred.
819
-     * @param string  $code     A violation code unique to the sniff message.
820
-     * @param array   $data     Replacements for the message.
821
-     * @param int     $severity The severity level for this message. A value of 0
822
-     *                          will be converted into the default severity level.
823
-     * @param boolean $fixable  Can the problem be fixed by the sniff?
824
-     *
825
-     * @return boolean
826
-     */
827
-    protected function addMessage($error, $message, $line, $column, $code, $data, $severity, $fixable)
828
-    {
829
-        // Check if this line is ignoring all message codes.
830
-        if (isset($this->tokenizer->ignoredLines[$line]['.all']) === true) {
831
-            return false;
832
-        }
833
-
834
-        // Work out which sniff generated the message.
835
-        $parts = explode('.', $code);
836
-        if ($parts[0] === 'Internal') {
837
-            // An internal message.
838
-            $listenerCode = Util\Common::getSniffCode($this->activeListener);
839
-            $sniffCode    = $code;
840
-            $checkCodes   = [$sniffCode];
841
-        } else {
842
-            if ($parts[0] !== $code) {
843
-                // The full message code has been passed in.
844
-                $sniffCode    = $code;
845
-                $listenerCode = substr($sniffCode, 0, strrpos($sniffCode, '.'));
846
-            } else {
847
-                $listenerCode = Util\Common::getSniffCode($this->activeListener);
848
-                $sniffCode    = $listenerCode.'.'.$code;
849
-                $parts        = explode('.', $sniffCode);
850
-            }
851
-
852
-            $checkCodes = [
853
-                $sniffCode,
854
-                $parts[0].'.'.$parts[1].'.'.$parts[2],
855
-                $parts[0].'.'.$parts[1],
856
-                $parts[0],
857
-            ];
858
-        }//end if
859
-
860
-        if (isset($this->tokenizer->ignoredLines[$line]) === true) {
861
-            // Check if this line is ignoring this specific message.
862
-            $ignored = false;
863
-            foreach ($checkCodes as $checkCode) {
864
-                if (isset($this->tokenizer->ignoredLines[$line][$checkCode]) === true) {
865
-                    $ignored = true;
866
-                    break;
867
-                }
868
-            }
869
-
870
-            // If it is ignored, make sure it's not whitelisted.
871
-            if ($ignored === true
872
-                && isset($this->tokenizer->ignoredLines[$line]['.except']) === true
873
-            ) {
874
-                foreach ($checkCodes as $checkCode) {
875
-                    if (isset($this->tokenizer->ignoredLines[$line]['.except'][$checkCode]) === true) {
876
-                        $ignored = false;
877
-                        break;
878
-                    }
879
-                }
880
-            }
881
-
882
-            if ($ignored === true) {
883
-                return false;
884
-            }
885
-        }//end if
886
-
887
-        $includeAll = true;
888
-        if ($this->configCache['cache'] === false
889
-            || $this->configCache['recordErrors'] === false
890
-        ) {
891
-            $includeAll = false;
892
-        }
893
-
894
-        // Filter out any messages for sniffs that shouldn't have run
895
-        // due to the use of the --sniffs command line argument.
896
-        if ($includeAll === false
897
-            && ((empty($this->configCache['sniffs']) === false
898
-            && in_array(strtolower($listenerCode), $this->configCache['sniffs'], true) === false)
899
-            || (empty($this->configCache['exclude']) === false
900
-            && in_array(strtolower($listenerCode), $this->configCache['exclude'], true) === true))
901
-        ) {
902
-            return false;
903
-        }
904
-
905
-        // If we know this sniff code is being ignored for this file, return early.
906
-        foreach ($checkCodes as $checkCode) {
907
-            if (isset($this->ignoredCodes[$checkCode]) === true) {
908
-                return false;
909
-            }
910
-        }
911
-
912
-        $oppositeType = 'warning';
913
-        if ($error === false) {
914
-            $oppositeType = 'error';
915
-        }
916
-
917
-        foreach ($checkCodes as $checkCode) {
918
-            // Make sure this message type has not been set to the opposite message type.
919
-            if (isset($this->ruleset->ruleset[$checkCode]['type']) === true
920
-                && $this->ruleset->ruleset[$checkCode]['type'] === $oppositeType
921
-            ) {
922
-                $error = !$error;
923
-                break;
924
-            }
925
-        }
926
-
927
-        if ($error === true) {
928
-            $configSeverity = $this->configCache['errorSeverity'];
929
-            $messageCount   = &$this->errorCount;
930
-            $messages       = &$this->errors;
931
-        } else {
932
-            $configSeverity = $this->configCache['warningSeverity'];
933
-            $messageCount   = &$this->warningCount;
934
-            $messages       = &$this->warnings;
935
-        }
936
-
937
-        if ($includeAll === false && $configSeverity === 0) {
938
-            // Don't bother doing any processing as these messages are just going to
939
-            // be hidden in the reports anyway.
940
-            return false;
941
-        }
942
-
943
-        if ($severity === 0) {
944
-            $severity = 5;
945
-        }
946
-
947
-        foreach ($checkCodes as $checkCode) {
948
-            // Make sure we are interested in this severity level.
949
-            if (isset($this->ruleset->ruleset[$checkCode]['severity']) === true) {
950
-                $severity = $this->ruleset->ruleset[$checkCode]['severity'];
951
-                break;
952
-            }
953
-        }
954
-
955
-        if ($includeAll === false && $configSeverity > $severity) {
956
-            return false;
957
-        }
958
-
959
-        // Make sure we are not ignoring this file.
960
-        $included = null;
961
-        foreach ($checkCodes as $checkCode) {
962
-            $patterns = null;
963
-
964
-            if (isset($this->configCache['includePatterns'][$checkCode]) === true) {
965
-                $patterns  = $this->configCache['includePatterns'][$checkCode];
966
-                $excluding = false;
967
-            } else if (isset($this->configCache['ignorePatterns'][$checkCode]) === true) {
968
-                $patterns  = $this->configCache['ignorePatterns'][$checkCode];
969
-                $excluding = true;
970
-            }
971
-
972
-            if ($patterns === null) {
973
-                continue;
974
-            }
975
-
976
-            foreach ($patterns as $pattern => $type) {
977
-                // While there is support for a type of each pattern
978
-                // (absolute or relative) we don't actually support it here.
979
-                $replacements = [
980
-                    '\\,' => ',',
981
-                    '*'   => '.*',
982
-                ];
983
-
984
-                // We assume a / directory separator, as do the exclude rules
985
-                // most developers write, so we need a special case for any system
986
-                // that is different.
987
-                if (DIRECTORY_SEPARATOR === '\\') {
988
-                    $replacements['/'] = '\\\\';
989
-                }
990
-
991
-                $pattern = '`'.strtr($pattern, $replacements).'`i';
992
-                $matched = preg_match($pattern, $this->path);
993
-
994
-                if ($matched === 0) {
995
-                    if ($excluding === false && $included === null) {
996
-                        // This file path is not being included.
997
-                        $included = false;
998
-                    }
999
-
1000
-                    continue;
1001
-                }
1002
-
1003
-                if ($excluding === true) {
1004
-                    // This file path is being excluded.
1005
-                    $this->ignoredCodes[$checkCode] = true;
1006
-                    return false;
1007
-                }
1008
-
1009
-                // This file path is being included.
1010
-                $included = true;
1011
-                break;
1012
-            }//end foreach
1013
-        }//end foreach
1014
-
1015
-        if ($included === false) {
1016
-            // There were include rules set, but this file
1017
-            // path didn't match any of them.
1018
-            return false;
1019
-        }
1020
-
1021
-        $messageCount++;
1022
-        if ($fixable === true) {
1023
-            $this->fixableCount++;
1024
-        }
1025
-
1026
-        if ($this->configCache['recordErrors'] === false
1027
-            && $includeAll === false
1028
-        ) {
1029
-            return true;
1030
-        }
1031
-
1032
-        // Work out the error message.
1033
-        if (isset($this->ruleset->ruleset[$sniffCode]['message']) === true) {
1034
-            $message = $this->ruleset->ruleset[$sniffCode]['message'];
1035
-        }
1036
-
1037
-        if (empty($data) === false) {
1038
-            $message = vsprintf($message, $data);
1039
-        }
1040
-
1041
-        if (isset($messages[$line]) === false) {
1042
-            $messages[$line] = [];
1043
-        }
1044
-
1045
-        if (isset($messages[$line][$column]) === false) {
1046
-            $messages[$line][$column] = [];
1047
-        }
1048
-
1049
-        $messages[$line][$column][] = [
1050
-            'message'  => $message,
1051
-            'source'   => $sniffCode,
1052
-            'listener' => $this->activeListener,
1053
-            'severity' => $severity,
1054
-            'fixable'  => $fixable,
1055
-        ];
1056
-
1057
-        if (PHP_CODESNIFFER_VERBOSITY > 1
1058
-            && $this->fixer->enabled === true
1059
-            && $fixable === true
1060
-        ) {
1061
-            @ob_end_clean();
1062
-            echo "\tE: [Line $line] $message ($sniffCode)".PHP_EOL;
1063
-            ob_start();
1064
-        }
1065
-
1066
-        return true;
1067
-
1068
-    }//end addMessage()
1069
-
1070
-
1071
-    /**
1072
-     * Record a metric about the file being examined.
1073
-     *
1074
-     * @param int    $stackPtr The stack position where the metric was recorded.
1075
-     * @param string $metric   The name of the metric being recorded.
1076
-     * @param string $value    The value of the metric being recorded.
1077
-     *
1078
-     * @return boolean
1079
-     */
1080
-    public function recordMetric($stackPtr, $metric, $value)
1081
-    {
1082
-        if (isset($this->metrics[$metric]) === false) {
1083
-            $this->metrics[$metric] = ['values' => [$value => 1]];
1084
-            $this->metricTokens[$metric][$stackPtr] = true;
1085
-        } else if (isset($this->metricTokens[$metric][$stackPtr]) === false) {
1086
-            $this->metricTokens[$metric][$stackPtr] = true;
1087
-            if (isset($this->metrics[$metric]['values'][$value]) === false) {
1088
-                $this->metrics[$metric]['values'][$value] = 1;
1089
-            } else {
1090
-                $this->metrics[$metric]['values'][$value]++;
1091
-            }
1092
-        }
1093
-
1094
-        return true;
1095
-
1096
-    }//end recordMetric()
1097
-
1098
-
1099
-    /**
1100
-     * Returns the number of errors raised.
1101
-     *
1102
-     * @return int
1103
-     */
1104
-    public function getErrorCount()
1105
-    {
1106
-        return $this->errorCount;
1107
-
1108
-    }//end getErrorCount()
1109
-
1110
-
1111
-    /**
1112
-     * Returns the number of warnings raised.
1113
-     *
1114
-     * @return int
1115
-     */
1116
-    public function getWarningCount()
1117
-    {
1118
-        return $this->warningCount;
1119
-
1120
-    }//end getWarningCount()
1121
-
1122
-
1123
-    /**
1124
-     * Returns the number of fixable errors/warnings raised.
1125
-     *
1126
-     * @return int
1127
-     */
1128
-    public function getFixableCount()
1129
-    {
1130
-        return $this->fixableCount;
1131
-
1132
-    }//end getFixableCount()
1133
-
1134
-
1135
-    /**
1136
-     * Returns the number of fixed errors/warnings.
1137
-     *
1138
-     * @return int
1139
-     */
1140
-    public function getFixedCount()
1141
-    {
1142
-        return $this->fixedCount;
1143
-
1144
-    }//end getFixedCount()
1145
-
1146
-
1147
-    /**
1148
-     * Returns the list of ignored lines.
1149
-     *
1150
-     * @return array
1151
-     */
1152
-    public function getIgnoredLines()
1153
-    {
1154
-        return $this->tokenizer->ignoredLines;
1155
-
1156
-    }//end getIgnoredLines()
1157
-
1158
-
1159
-    /**
1160
-     * Returns the errors raised from processing this file.
1161
-     *
1162
-     * @return array
1163
-     */
1164
-    public function getErrors()
1165
-    {
1166
-        return $this->errors;
1167
-
1168
-    }//end getErrors()
1169
-
1170
-
1171
-    /**
1172
-     * Returns the warnings raised from processing this file.
1173
-     *
1174
-     * @return array
1175
-     */
1176
-    public function getWarnings()
1177
-    {
1178
-        return $this->warnings;
1179
-
1180
-    }//end getWarnings()
1181
-
1182
-
1183
-    /**
1184
-     * Returns the metrics found while processing this file.
1185
-     *
1186
-     * @return array
1187
-     */
1188
-    public function getMetrics()
1189
-    {
1190
-        return $this->metrics;
1191
-
1192
-    }//end getMetrics()
1193
-
1194
-
1195
-    /**
1196
-     * Returns the absolute filename of this file.
1197
-     *
1198
-     * @return string
1199
-     */
1200
-    public function getFilename()
1201
-    {
1202
-        return $this->path;
1203
-
1204
-    }//end getFilename()
1205
-
1206
-
1207
-    /**
1208
-     * Returns the declaration names for classes, interfaces, traits, and functions.
1209
-     *
1210
-     * @param int $stackPtr The position of the declaration token which
1211
-     *                      declared the class, interface, trait, or function.
1212
-     *
1213
-     * @return string|null The name of the class, interface, trait, or function;
1214
-     *                     or NULL if the function or class is anonymous.
1215
-     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified token is not of type
1216
-     *                                                      T_FUNCTION, T_CLASS, T_ANON_CLASS,
1217
-     *                                                      T_CLOSURE, T_TRAIT, or T_INTERFACE.
1218
-     */
1219
-    public function getDeclarationName($stackPtr)
1220
-    {
1221
-        $tokenCode = $this->tokens[$stackPtr]['code'];
1222
-
1223
-        if ($tokenCode === T_ANON_CLASS || $tokenCode === T_CLOSURE) {
1224
-            return null;
1225
-        }
1226
-
1227
-        if ($tokenCode !== T_FUNCTION
1228
-            && $tokenCode !== T_CLASS
1229
-            && $tokenCode !== T_INTERFACE
1230
-            && $tokenCode !== T_TRAIT
1231
-        ) {
1232
-            throw new RuntimeException('Token type "'.$this->tokens[$stackPtr]['type'].'" is not T_FUNCTION, T_CLASS, T_INTERFACE or T_TRAIT');
1233
-        }
1234
-
1235
-        if ($tokenCode === T_FUNCTION
1236
-            && strtolower($this->tokens[$stackPtr]['content']) !== 'function'
1237
-        ) {
1238
-            // This is a function declared without the "function" keyword.
1239
-            // So this token is the function name.
1240
-            return $this->tokens[$stackPtr]['content'];
1241
-        }
1242
-
1243
-        $content = null;
1244
-        for ($i = $stackPtr; $i < $this->numTokens; $i++) {
1245
-            if ($this->tokens[$i]['code'] === T_STRING) {
1246
-                $content = $this->tokens[$i]['content'];
1247
-                break;
1248
-            }
1249
-        }
1250
-
1251
-        return $content;
1252
-
1253
-    }//end getDeclarationName()
1254
-
1255
-
1256
-    /**
1257
-     * Returns the method parameters for the specified function token.
1258
-     *
1259
-     * Each parameter is in the following format:
1260
-     *
1261
-     * <code>
1262
-     *   0 => array(
1263
-     *         'name'              => '$var',  // The variable name.
1264
-     *         'token'             => integer, // The stack pointer to the variable name.
1265
-     *         'content'           => string,  // The full content of the variable definition.
1266
-     *         'pass_by_reference' => boolean, // Is the variable passed by reference?
1267
-     *         'variable_length'   => boolean, // Is the param of variable length through use of `...` ?
1268
-     *         'type_hint'         => string,  // The type hint for the variable.
1269
-     *         'type_hint_token'   => integer, // The stack pointer to the type hint
1270
-     *                                         // or false if there is no type hint.
1271
-     *         'nullable_type'     => boolean, // Is the variable using a nullable type?
1272
-     *        )
1273
-     * </code>
1274
-     *
1275
-     * Parameters with default values have an additional array index of
1276
-     * 'default' with the value of the default as a string.
1277
-     *
1278
-     * @param int $stackPtr The position in the stack of the function token
1279
-     *                      to acquire the parameters for.
1280
-     *
1281
-     * @return array
1282
-     * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the specified $stackPtr is not of
1283
-     *                                                        type T_FUNCTION or T_CLOSURE.
1284
-     */
1285
-    public function getMethodParameters($stackPtr)
1286
-    {
1287
-        if ($this->tokens[$stackPtr]['code'] !== T_FUNCTION
1288
-            && $this->tokens[$stackPtr]['code'] !== T_CLOSURE
1289
-        ) {
1290
-            throw new TokenizerException('$stackPtr must be of type T_FUNCTION or T_CLOSURE');
1291
-        }
1292
-
1293
-        $opener = $this->tokens[$stackPtr]['parenthesis_opener'];
1294
-        $closer = $this->tokens[$stackPtr]['parenthesis_closer'];
1295
-
1296
-        $vars            = [];
1297
-        $currVar         = null;
1298
-        $paramStart      = ($opener + 1);
1299
-        $defaultStart    = null;
1300
-        $paramCount      = 0;
1301
-        $passByReference = false;
1302
-        $variableLength  = false;
1303
-        $typeHint        = '';
1304
-        $typeHintToken   = false;
1305
-        $nullableType    = false;
1306
-
1307
-        for ($i = $paramStart; $i <= $closer; $i++) {
1308
-            // Check to see if this token has a parenthesis or bracket opener. If it does
1309
-            // it's likely to be an array which might have arguments in it. This
1310
-            // could cause problems in our parsing below, so lets just skip to the
1311
-            // end of it.
1312
-            if (isset($this->tokens[$i]['parenthesis_opener']) === true) {
1313
-                // Don't do this if it's the close parenthesis for the method.
1314
-                if ($i !== $this->tokens[$i]['parenthesis_closer']) {
1315
-                    $i = ($this->tokens[$i]['parenthesis_closer'] + 1);
1316
-                }
1317
-            }
1318
-
1319
-            if (isset($this->tokens[$i]['bracket_opener']) === true) {
1320
-                // Don't do this if it's the close parenthesis for the method.
1321
-                if ($i !== $this->tokens[$i]['bracket_closer']) {
1322
-                    $i = ($this->tokens[$i]['bracket_closer'] + 1);
1323
-                }
1324
-            }
1325
-
1326
-            switch ($this->tokens[$i]['code']) {
1327
-            case T_BITWISE_AND:
1328
-                if ($defaultStart === null) {
1329
-                    $passByReference = true;
1330
-                }
1331
-                break;
1332
-            case T_VARIABLE:
1333
-                $currVar = $i;
1334
-                break;
1335
-            case T_ELLIPSIS:
1336
-                $variableLength = true;
1337
-                break;
1338
-            case T_CALLABLE:
1339
-                if ($typeHintToken === false) {
1340
-                    $typeHintToken = $i;
1341
-                }
1342
-
1343
-                $typeHint .= $this->tokens[$i]['content'];
1344
-                break;
1345
-            case T_SELF:
1346
-            case T_PARENT:
1347
-            case T_STATIC:
1348
-                // Self and parent are valid, static invalid, but was probably intended as type hint.
1349
-                if (isset($defaultStart) === false) {
1350
-                    if ($typeHintToken === false) {
1351
-                        $typeHintToken = $i;
1352
-                    }
1353
-
1354
-                    $typeHint .= $this->tokens[$i]['content'];
1355
-                }
1356
-                break;
1357
-            case T_STRING:
1358
-                // This is a string, so it may be a type hint, but it could
1359
-                // also be a constant used as a default value.
1360
-                $prevComma = false;
1361
-                for ($t = $i; $t >= $opener; $t--) {
1362
-                    if ($this->tokens[$t]['code'] === T_COMMA) {
1363
-                        $prevComma = $t;
1364
-                        break;
1365
-                    }
1366
-                }
1367
-
1368
-                if ($prevComma !== false) {
1369
-                    $nextEquals = false;
1370
-                    for ($t = $prevComma; $t < $i; $t++) {
1371
-                        if ($this->tokens[$t]['code'] === T_EQUAL) {
1372
-                            $nextEquals = $t;
1373
-                            break;
1374
-                        }
1375
-                    }
1376
-
1377
-                    if ($nextEquals !== false) {
1378
-                        break;
1379
-                    }
1380
-                }
1381
-
1382
-                if ($defaultStart === null) {
1383
-                    if ($typeHintToken === false) {
1384
-                        $typeHintToken = $i;
1385
-                    }
1386
-
1387
-                    $typeHint .= $this->tokens[$i]['content'];
1388
-                }
1389
-                break;
1390
-            case T_NS_SEPARATOR:
1391
-                // Part of a type hint or default value.
1392
-                if ($defaultStart === null) {
1393
-                    if ($typeHintToken === false) {
1394
-                        $typeHintToken = $i;
1395
-                    }
1396
-
1397
-                    $typeHint .= $this->tokens[$i]['content'];
1398
-                }
1399
-                break;
1400
-            case T_NULLABLE:
1401
-                if ($defaultStart === null) {
1402
-                    $nullableType = true;
1403
-                    $typeHint    .= $this->tokens[$i]['content'];
1404
-                }
1405
-                break;
1406
-            case T_CLOSE_PARENTHESIS:
1407
-            case T_COMMA:
1408
-                // If it's null, then there must be no parameters for this
1409
-                // method.
1410
-                if ($currVar === null) {
1411
-                    continue 2;
1412
-                }
1413
-
1414
-                $vars[$paramCount]            = [];
1415
-                $vars[$paramCount]['token']   = $currVar;
1416
-                $vars[$paramCount]['name']    = $this->tokens[$currVar]['content'];
1417
-                $vars[$paramCount]['content'] = trim($this->getTokensAsString($paramStart, ($i - $paramStart)));
1418
-
1419
-                if ($defaultStart !== null) {
1420
-                    $vars[$paramCount]['default'] = trim($this->getTokensAsString($defaultStart, ($i - $defaultStart)));
1421
-                }
1422
-
1423
-                $vars[$paramCount]['pass_by_reference'] = $passByReference;
1424
-                $vars[$paramCount]['variable_length']   = $variableLength;
1425
-                $vars[$paramCount]['type_hint']         = $typeHint;
1426
-                $vars[$paramCount]['type_hint_token']   = $typeHintToken;
1427
-                $vars[$paramCount]['nullable_type']     = $nullableType;
1428
-
1429
-                // Reset the vars, as we are about to process the next parameter.
1430
-                $defaultStart    = null;
1431
-                $paramStart      = ($i + 1);
1432
-                $passByReference = false;
1433
-                $variableLength  = false;
1434
-                $typeHint        = '';
1435
-                $typeHintToken   = false;
1436
-                $nullableType    = false;
1437
-
1438
-                $paramCount++;
1439
-                break;
1440
-            case T_EQUAL:
1441
-                $defaultStart = ($i + 1);
1442
-                break;
1443
-            }//end switch
1444
-        }//end for
1445
-
1446
-        return $vars;
1447
-
1448
-    }//end getMethodParameters()
1449
-
1450
-
1451
-    /**
1452
-     * Returns the visibility and implementation properties of a method.
1453
-     *
1454
-     * The format of the array is:
1455
-     * <code>
1456
-     *   array(
1457
-     *    'scope'                => 'public', // public protected or protected
1458
-     *    'scope_specified'      => true,     // true is scope keyword was found.
1459
-     *    'return_type'          => '',       // the return type of the method.
1460
-     *    'return_type_token'    => integer,  // The stack pointer to the start of the return type
1461
-     *                                        // or false if there is no return type.
1462
-     *    'nullable_return_type' => false,    // true if the return type is nullable.
1463
-     *    'is_abstract'          => false,    // true if the abstract keyword was found.
1464
-     *    'is_final'             => false,    // true if the final keyword was found.
1465
-     *    'is_static'            => false,    // true if the static keyword was found.
1466
-     *    'has_body'             => false,    // true if the method has a body
1467
-     *   );
1468
-     * </code>
1469
-     *
1470
-     * @param int $stackPtr The position in the stack of the function token to
1471
-     *                      acquire the properties for.
1472
-     *
1473
-     * @return array
1474
-     * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the specified position is not a
1475
-     *                                                        T_FUNCTION token.
1476
-     */
1477
-    public function getMethodProperties($stackPtr)
1478
-    {
1479
-        if ($this->tokens[$stackPtr]['code'] !== T_FUNCTION
1480
-            && $this->tokens[$stackPtr]['code'] !== T_CLOSURE
1481
-        ) {
1482
-            throw new TokenizerException('$stackPtr must be of type T_FUNCTION or T_CLOSURE');
1483
-        }
1484
-
1485
-        if ($this->tokens[$stackPtr]['code'] === T_FUNCTION) {
1486
-            $valid = [
1487
-                T_PUBLIC      => T_PUBLIC,
1488
-                T_PRIVATE     => T_PRIVATE,
1489
-                T_PROTECTED   => T_PROTECTED,
1490
-                T_STATIC      => T_STATIC,
1491
-                T_FINAL       => T_FINAL,
1492
-                T_ABSTRACT    => T_ABSTRACT,
1493
-                T_WHITESPACE  => T_WHITESPACE,
1494
-                T_COMMENT     => T_COMMENT,
1495
-                T_DOC_COMMENT => T_DOC_COMMENT,
1496
-            ];
1497
-        } else {
1498
-            $valid = [
1499
-                T_STATIC      => T_STATIC,
1500
-                T_WHITESPACE  => T_WHITESPACE,
1501
-                T_COMMENT     => T_COMMENT,
1502
-                T_DOC_COMMENT => T_DOC_COMMENT,
1503
-            ];
1504
-        }
1505
-
1506
-        $scope          = 'public';
1507
-        $scopeSpecified = false;
1508
-        $isAbstract     = false;
1509
-        $isFinal        = false;
1510
-        $isStatic       = false;
1511
-
1512
-        for ($i = ($stackPtr - 1); $i > 0; $i--) {
1513
-            if (isset($valid[$this->tokens[$i]['code']]) === false) {
1514
-                break;
1515
-            }
1516
-
1517
-            switch ($this->tokens[$i]['code']) {
1518
-            case T_PUBLIC:
1519
-                $scope          = 'public';
1520
-                $scopeSpecified = true;
1521
-                break;
1522
-            case T_PRIVATE:
1523
-                $scope          = 'private';
1524
-                $scopeSpecified = true;
1525
-                break;
1526
-            case T_PROTECTED:
1527
-                $scope          = 'protected';
1528
-                $scopeSpecified = true;
1529
-                break;
1530
-            case T_ABSTRACT:
1531
-                $isAbstract = true;
1532
-                break;
1533
-            case T_FINAL:
1534
-                $isFinal = true;
1535
-                break;
1536
-            case T_STATIC:
1537
-                $isStatic = true;
1538
-                break;
1539
-            }//end switch
1540
-        }//end for
1541
-
1542
-        $returnType         = '';
1543
-        $returnTypeToken    = false;
1544
-        $nullableReturnType = false;
1545
-        $hasBody            = true;
1546
-
1547
-        if (isset($this->tokens[$stackPtr]['parenthesis_closer']) === true) {
1548
-            $scopeOpener = null;
1549
-            if (isset($this->tokens[$stackPtr]['scope_opener']) === true) {
1550
-                $scopeOpener = $this->tokens[$stackPtr]['scope_opener'];
1551
-            }
1552
-
1553
-            $valid = [
1554
-                T_STRING       => T_STRING,
1555
-                T_CALLABLE     => T_CALLABLE,
1556
-                T_SELF         => T_SELF,
1557
-                T_PARENT       => T_PARENT,
1558
-                T_NS_SEPARATOR => T_NS_SEPARATOR,
1559
-            ];
1560
-
1561
-            for ($i = $this->tokens[$stackPtr]['parenthesis_closer']; $i < $this->numTokens; $i++) {
1562
-                if (($scopeOpener === null && $this->tokens[$i]['code'] === T_SEMICOLON)
1563
-                    || ($scopeOpener !== null && $i === $scopeOpener)
1564
-                ) {
1565
-                    // End of function definition.
1566
-                    break;
1567
-                }
1568
-
1569
-                if ($this->tokens[$i]['code'] === T_NULLABLE) {
1570
-                    $nullableReturnType = true;
1571
-                }
1572
-
1573
-                if (isset($valid[$this->tokens[$i]['code']]) === true) {
1574
-                    if ($returnTypeToken === false) {
1575
-                        $returnTypeToken = $i;
1576
-                    }
1577
-
1578
-                    $returnType .= $this->tokens[$i]['content'];
1579
-                }
1580
-            }
1581
-
1582
-            $end     = $this->findNext([T_OPEN_CURLY_BRACKET, T_SEMICOLON], $this->tokens[$stackPtr]['parenthesis_closer']);
1583
-            $hasBody = $this->tokens[$end]['code'] === T_OPEN_CURLY_BRACKET;
1584
-        }//end if
1585
-
1586
-        if ($returnType !== '' && $nullableReturnType === true) {
1587
-            $returnType = '?'.$returnType;
1588
-        }
1589
-
1590
-        return [
1591
-            'scope'                => $scope,
1592
-            'scope_specified'      => $scopeSpecified,
1593
-            'return_type'          => $returnType,
1594
-            'return_type_token'    => $returnTypeToken,
1595
-            'nullable_return_type' => $nullableReturnType,
1596
-            'is_abstract'          => $isAbstract,
1597
-            'is_final'             => $isFinal,
1598
-            'is_static'            => $isStatic,
1599
-            'has_body'             => $hasBody,
1600
-        ];
1601
-
1602
-    }//end getMethodProperties()
1603
-
1604
-
1605
-    /**
1606
-     * Returns the visibility and implementation properties of the class member
1607
-     * variable found at the specified position in the stack.
1608
-     *
1609
-     * The format of the array is:
1610
-     *
1611
-     * <code>
1612
-     *   array(
1613
-     *    'scope'           => 'public', // public protected or protected.
1614
-     *    'scope_specified' => false,    // true if the scope was explicitly specified.
1615
-     *    'is_static'       => false,    // true if the static keyword was found.
1616
-     *   );
1617
-     * </code>
1618
-     *
1619
-     * @param int $stackPtr The position in the stack of the T_VARIABLE token to
1620
-     *                      acquire the properties for.
1621
-     *
1622
-     * @return array
1623
-     * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the specified position is not a
1624
-     *                                                        T_VARIABLE token, or if the position is not
1625
-     *                                                        a class member variable.
1626
-     */
1627
-    public function getMemberProperties($stackPtr)
1628
-    {
1629
-        if ($this->tokens[$stackPtr]['code'] !== T_VARIABLE) {
1630
-            throw new TokenizerException('$stackPtr must be of type T_VARIABLE');
1631
-        }
1632
-
1633
-        $conditions = array_keys($this->tokens[$stackPtr]['conditions']);
1634
-        $ptr        = array_pop($conditions);
1635
-        if (isset($this->tokens[$ptr]) === false
1636
-            || ($this->tokens[$ptr]['code'] !== T_CLASS
1637
-            && $this->tokens[$ptr]['code'] !== T_ANON_CLASS
1638
-            && $this->tokens[$ptr]['code'] !== T_TRAIT)
1639
-        ) {
1640
-            if (isset($this->tokens[$ptr]) === true
1641
-                && $this->tokens[$ptr]['code'] === T_INTERFACE
1642
-            ) {
1643
-                // T_VARIABLEs in interfaces can actually be method arguments
1644
-                // but they wont be seen as being inside the method because there
1645
-                // are no scope openers and closers for abstract methods. If it is in
1646
-                // parentheses, we can be pretty sure it is a method argument.
1647
-                if (isset($this->tokens[$stackPtr]['nested_parenthesis']) === false
1648
-                    || empty($this->tokens[$stackPtr]['nested_parenthesis']) === true
1649
-                ) {
1650
-                    $error = 'Possible parse error: interfaces may not include member vars';
1651
-                    $this->addWarning($error, $stackPtr, 'Internal.ParseError.InterfaceHasMemberVar');
1652
-                    return [];
1653
-                }
1654
-            } else {
1655
-                throw new TokenizerException('$stackPtr is not a class member var');
1656
-            }
1657
-        }
1658
-
1659
-        // Make sure it's not a method parameter.
1660
-        if (empty($this->tokens[$stackPtr]['nested_parenthesis']) === false) {
1661
-            $parenthesis = array_keys($this->tokens[$stackPtr]['nested_parenthesis']);
1662
-            $deepestOpen = array_pop($parenthesis);
1663
-            if ($deepestOpen > $ptr
1664
-                && isset($this->tokens[$deepestOpen]['parenthesis_owner']) === true
1665
-                && $this->tokens[$this->tokens[$deepestOpen]['parenthesis_owner']]['code'] === T_FUNCTION
1666
-            ) {
1667
-                throw new TokenizerException('$stackPtr is not a class member var');
1668
-            }
1669
-        }
1670
-
1671
-        $valid = [
1672
-            T_PUBLIC    => T_PUBLIC,
1673
-            T_PRIVATE   => T_PRIVATE,
1674
-            T_PROTECTED => T_PROTECTED,
1675
-            T_STATIC    => T_STATIC,
1676
-            T_VAR       => T_VAR,
1677
-        ];
1678
-
1679
-        $valid += Util\Tokens::$emptyTokens;
1680
-
1681
-        $scope          = 'public';
1682
-        $scopeSpecified = false;
1683
-        $isStatic       = false;
1684
-
1685
-        $startOfStatement = $this->findPrevious(
1686
-            [
1687
-                T_SEMICOLON,
1688
-                T_OPEN_CURLY_BRACKET,
1689
-                T_CLOSE_CURLY_BRACKET,
1690
-            ],
1691
-            ($stackPtr - 1)
1692
-        );
1693
-
1694
-        for ($i = ($startOfStatement + 1); $i < $stackPtr; $i++) {
1695
-            if (isset($valid[$this->tokens[$i]['code']]) === false) {
1696
-                break;
1697
-            }
1698
-
1699
-            switch ($this->tokens[$i]['code']) {
1700
-            case T_PUBLIC:
1701
-                $scope          = 'public';
1702
-                $scopeSpecified = true;
1703
-                break;
1704
-            case T_PRIVATE:
1705
-                $scope          = 'private';
1706
-                $scopeSpecified = true;
1707
-                break;
1708
-            case T_PROTECTED:
1709
-                $scope          = 'protected';
1710
-                $scopeSpecified = true;
1711
-                break;
1712
-            case T_STATIC:
1713
-                $isStatic = true;
1714
-                break;
1715
-            }
1716
-        }//end for
1717
-
1718
-        return [
1719
-            'scope'           => $scope,
1720
-            'scope_specified' => $scopeSpecified,
1721
-            'is_static'       => $isStatic,
1722
-        ];
1723
-
1724
-    }//end getMemberProperties()
1725
-
1726
-
1727
-    /**
1728
-     * Returns the visibility and implementation properties of a class.
1729
-     *
1730
-     * The format of the array is:
1731
-     * <code>
1732
-     *   array(
1733
-     *    'is_abstract' => false, // true if the abstract keyword was found.
1734
-     *    'is_final'    => false, // true if the final keyword was found.
1735
-     *   );
1736
-     * </code>
1737
-     *
1738
-     * @param int $stackPtr The position in the stack of the T_CLASS token to
1739
-     *                      acquire the properties for.
1740
-     *
1741
-     * @return array
1742
-     * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the specified position is not a
1743
-     *                                                        T_CLASS token.
1744
-     */
1745
-    public function getClassProperties($stackPtr)
1746
-    {
1747
-        if ($this->tokens[$stackPtr]['code'] !== T_CLASS) {
1748
-            throw new TokenizerException('$stackPtr must be of type T_CLASS');
1749
-        }
1750
-
1751
-        $valid = [
1752
-            T_FINAL       => T_FINAL,
1753
-            T_ABSTRACT    => T_ABSTRACT,
1754
-            T_WHITESPACE  => T_WHITESPACE,
1755
-            T_COMMENT     => T_COMMENT,
1756
-            T_DOC_COMMENT => T_DOC_COMMENT,
1757
-        ];
1758
-
1759
-        $isAbstract = false;
1760
-        $isFinal    = false;
1761
-
1762
-        for ($i = ($stackPtr - 1); $i > 0; $i--) {
1763
-            if (isset($valid[$this->tokens[$i]['code']]) === false) {
1764
-                break;
1765
-            }
1766
-
1767
-            switch ($this->tokens[$i]['code']) {
1768
-            case T_ABSTRACT:
1769
-                $isAbstract = true;
1770
-                break;
1771
-
1772
-            case T_FINAL:
1773
-                $isFinal = true;
1774
-                break;
1775
-            }
1776
-        }//end for
1777
-
1778
-        return [
1779
-            'is_abstract' => $isAbstract,
1780
-            'is_final'    => $isFinal,
1781
-        ];
1782
-
1783
-    }//end getClassProperties()
1784
-
1785
-
1786
-    /**
1787
-     * Determine if the passed token is a reference operator.
1788
-     *
1789
-     * Returns true if the specified token position represents a reference.
1790
-     * Returns false if the token represents a bitwise operator.
1791
-     *
1792
-     * @param int $stackPtr The position of the T_BITWISE_AND token.
1793
-     *
1794
-     * @return boolean
1795
-     */
1796
-    public function isReference($stackPtr)
1797
-    {
1798
-        if ($this->tokens[$stackPtr]['code'] !== T_BITWISE_AND) {
1799
-            return false;
1800
-        }
1801
-
1802
-        $tokenBefore = $this->findPrevious(
1803
-            Util\Tokens::$emptyTokens,
1804
-            ($stackPtr - 1),
1805
-            null,
1806
-            true
1807
-        );
1808
-
1809
-        if ($this->tokens[$tokenBefore]['code'] === T_FUNCTION) {
1810
-            // Function returns a reference.
1811
-            return true;
1812
-        }
1813
-
1814
-        if ($this->tokens[$tokenBefore]['code'] === T_DOUBLE_ARROW) {
1815
-            // Inside a foreach loop or array assignment, this is a reference.
1816
-            return true;
1817
-        }
1818
-
1819
-        if ($this->tokens[$tokenBefore]['code'] === T_AS) {
1820
-            // Inside a foreach loop, this is a reference.
1821
-            return true;
1822
-        }
1823
-
1824
-        if (isset(Util\Tokens::$assignmentTokens[$this->tokens[$tokenBefore]['code']]) === true) {
1825
-            // This is directly after an assignment. It's a reference. Even if
1826
-            // it is part of an operation, the other tests will handle it.
1827
-            return true;
1828
-        }
1829
-
1830
-        $tokenAfter = $this->findNext(
1831
-            Util\Tokens::$emptyTokens,
1832
-            ($stackPtr + 1),
1833
-            null,
1834
-            true
1835
-        );
1836
-
1837
-        if ($this->tokens[$tokenAfter]['code'] === T_NEW) {
1838
-            return true;
1839
-        }
1840
-
1841
-        if (isset($this->tokens[$stackPtr]['nested_parenthesis']) === true) {
1842
-            $brackets    = $this->tokens[$stackPtr]['nested_parenthesis'];
1843
-            $lastBracket = array_pop($brackets);
1844
-            if (isset($this->tokens[$lastBracket]['parenthesis_owner']) === true) {
1845
-                $owner = $this->tokens[$this->tokens[$lastBracket]['parenthesis_owner']];
1846
-                if ($owner['code'] === T_FUNCTION
1847
-                    || $owner['code'] === T_CLOSURE
1848
-                ) {
1849
-                    $params = $this->getMethodParameters($this->tokens[$lastBracket]['parenthesis_owner']);
1850
-                    foreach ($params as $param) {
1851
-                        $varToken = $tokenAfter;
1852
-                        if ($param['variable_length'] === true) {
1853
-                            $varToken = $this->findNext(
1854
-                                (Util\Tokens::$emptyTokens + [T_ELLIPSIS]),
1855
-                                ($stackPtr + 1),
1856
-                                null,
1857
-                                true
1858
-                            );
1859
-                        }
1860
-
1861
-                        if ($param['token'] === $varToken
1862
-                            && $param['pass_by_reference'] === true
1863
-                        ) {
1864
-                            // Function parameter declared to be passed by reference.
1865
-                            return true;
1866
-                        }
1867
-                    }
1868
-                }//end if
1869
-            } else {
1870
-                $prev = false;
1871
-                for ($t = ($this->tokens[$lastBracket]['parenthesis_opener'] - 1); $t >= 0; $t--) {
1872
-                    if ($this->tokens[$t]['code'] !== T_WHITESPACE) {
1873
-                        $prev = $t;
1874
-                        break;
1875
-                    }
1876
-                }
1877
-
1878
-                if ($prev !== false && $this->tokens[$prev]['code'] === T_USE) {
1879
-                    // Closure use by reference.
1880
-                    return true;
1881
-                }
1882
-            }//end if
1883
-        }//end if
1884
-
1885
-        // Pass by reference in function calls and assign by reference in arrays.
1886
-        if ($this->tokens[$tokenBefore]['code'] === T_OPEN_PARENTHESIS
1887
-            || $this->tokens[$tokenBefore]['code'] === T_COMMA
1888
-            || $this->tokens[$tokenBefore]['code'] === T_OPEN_SHORT_ARRAY
1889
-        ) {
1890
-            if ($this->tokens[$tokenAfter]['code'] === T_VARIABLE) {
1891
-                return true;
1892
-            } else {
1893
-                $skip   = Util\Tokens::$emptyTokens;
1894
-                $skip[] = T_NS_SEPARATOR;
1895
-                $skip[] = T_SELF;
1896
-                $skip[] = T_PARENT;
1897
-                $skip[] = T_STATIC;
1898
-                $skip[] = T_STRING;
1899
-                $skip[] = T_NAMESPACE;
1900
-                $skip[] = T_DOUBLE_COLON;
1901
-
1902
-                $nextSignificantAfter = $this->findNext(
1903
-                    $skip,
1904
-                    ($stackPtr + 1),
1905
-                    null,
1906
-                    true
1907
-                );
1908
-                if ($this->tokens[$nextSignificantAfter]['code'] === T_VARIABLE) {
1909
-                    return true;
1910
-                }
1911
-            }//end if
1912
-        }//end if
1913
-
1914
-        return false;
1915
-
1916
-    }//end isReference()
1917
-
1918
-
1919
-    /**
1920
-     * Returns the content of the tokens from the specified start position in
1921
-     * the token stack for the specified length.
1922
-     *
1923
-     * @param int  $start       The position to start from in the token stack.
1924
-     * @param int  $length      The length of tokens to traverse from the start pos.
1925
-     * @param bool $origContent Whether the original content or the tab replaced
1926
-     *                          content should be used.
1927
-     *
1928
-     * @return string The token contents.
1929
-     */
1930
-    public function getTokensAsString($start, $length, $origContent=false)
1931
-    {
1932
-        if (is_int($start) === false || isset($this->tokens[$start]) === false) {
1933
-            throw new RuntimeException('The $start position for getTokensAsString() must exist in the token stack');
1934
-        }
1935
-
1936
-        if (is_int($length) === false || $length <= 0) {
1937
-            return '';
1938
-        }
1939
-
1940
-        $str = '';
1941
-        $end = ($start + $length);
1942
-        if ($end > $this->numTokens) {
1943
-            $end = $this->numTokens;
1944
-        }
1945
-
1946
-        for ($i = $start; $i < $end; $i++) {
1947
-            // If tabs are being converted to spaces by the tokeniser, the
1948
-            // original content should be used instead of the converted content.
1949
-            if ($origContent === true && isset($this->tokens[$i]['orig_content']) === true) {
1950
-                $str .= $this->tokens[$i]['orig_content'];
1951
-            } else {
1952
-                $str .= $this->tokens[$i]['content'];
1953
-            }
1954
-        }
1955
-
1956
-        return $str;
1957
-
1958
-    }//end getTokensAsString()
1959
-
1960
-
1961
-    /**
1962
-     * Returns the position of the previous specified token(s).
1963
-     *
1964
-     * If a value is specified, the previous token of the specified type(s)
1965
-     * containing the specified value will be returned.
1966
-     *
1967
-     * Returns false if no token can be found.
1968
-     *
1969
-     * @param int|string|array $types   The type(s) of tokens to search for.
1970
-     * @param int              $start   The position to start searching from in the
1971
-     *                                  token stack.
1972
-     * @param int              $end     The end position to fail if no token is found.
1973
-     *                                  if not specified or null, end will default to
1974
-     *                                  the start of the token stack.
1975
-     * @param bool             $exclude If true, find the previous token that is NOT of
1976
-     *                                  the types specified in $types.
1977
-     * @param string           $value   The value that the token(s) must be equal to.
1978
-     *                                  If value is omitted, tokens with any value will
1979
-     *                                  be returned.
1980
-     * @param bool             $local   If true, tokens outside the current statement
1981
-     *                                  will not be checked. IE. checking will stop
1982
-     *                                  at the previous semi-colon found.
1983
-     *
1984
-     * @return int|bool
1985
-     * @see    findNext()
1986
-     */
1987
-    public function findPrevious(
1988
-        $types,
1989
-        $start,
1990
-        $end=null,
1991
-        $exclude=false,
1992
-        $value=null,
1993
-        $local=false
1994
-    ) {
1995
-        $types = (array) $types;
1996
-
1997
-        if ($end === null) {
1998
-            $end = 0;
1999
-        }
2000
-
2001
-        for ($i = $start; $i >= $end; $i--) {
2002
-            $found = (bool) $exclude;
2003
-            foreach ($types as $type) {
2004
-                if ($this->tokens[$i]['code'] === $type) {
2005
-                    $found = !$exclude;
2006
-                    break;
2007
-                }
2008
-            }
2009
-
2010
-            if ($found === true) {
2011
-                if ($value === null) {
2012
-                    return $i;
2013
-                } else if ($this->tokens[$i]['content'] === $value) {
2014
-                    return $i;
2015
-                }
2016
-            }
2017
-
2018
-            if ($local === true) {
2019
-                if (isset($this->tokens[$i]['scope_opener']) === true
2020
-                    && $i === $this->tokens[$i]['scope_closer']
2021
-                ) {
2022
-                    $i = $this->tokens[$i]['scope_opener'];
2023
-                } else if (isset($this->tokens[$i]['bracket_opener']) === true
2024
-                    && $i === $this->tokens[$i]['bracket_closer']
2025
-                ) {
2026
-                    $i = $this->tokens[$i]['bracket_opener'];
2027
-                } else if (isset($this->tokens[$i]['parenthesis_opener']) === true
2028
-                    && $i === $this->tokens[$i]['parenthesis_closer']
2029
-                ) {
2030
-                    $i = $this->tokens[$i]['parenthesis_opener'];
2031
-                } else if ($this->tokens[$i]['code'] === T_SEMICOLON) {
2032
-                    break;
2033
-                }
2034
-            }
2035
-        }//end for
2036
-
2037
-        return false;
2038
-
2039
-    }//end findPrevious()
2040
-
2041
-
2042
-    /**
2043
-     * Returns the position of the next specified token(s).
2044
-     *
2045
-     * If a value is specified, the next token of the specified type(s)
2046
-     * containing the specified value will be returned.
2047
-     *
2048
-     * Returns false if no token can be found.
2049
-     *
2050
-     * @param int|string|array $types   The type(s) of tokens to search for.
2051
-     * @param int              $start   The position to start searching from in the
2052
-     *                                  token stack.
2053
-     * @param int              $end     The end position to fail if no token is found.
2054
-     *                                  if not specified or null, end will default to
2055
-     *                                  the end of the token stack.
2056
-     * @param bool             $exclude If true, find the next token that is NOT of
2057
-     *                                  a type specified in $types.
2058
-     * @param string           $value   The value that the token(s) must be equal to.
2059
-     *                                  If value is omitted, tokens with any value will
2060
-     *                                  be returned.
2061
-     * @param bool             $local   If true, tokens outside the current statement
2062
-     *                                  will not be checked. i.e., checking will stop
2063
-     *                                  at the next semi-colon found.
2064
-     *
2065
-     * @return int|bool
2066
-     * @see    findPrevious()
2067
-     */
2068
-    public function findNext(
2069
-        $types,
2070
-        $start,
2071
-        $end=null,
2072
-        $exclude=false,
2073
-        $value=null,
2074
-        $local=false
2075
-    ) {
2076
-        $types = (array) $types;
2077
-
2078
-        if ($end === null || $end > $this->numTokens) {
2079
-            $end = $this->numTokens;
2080
-        }
2081
-
2082
-        for ($i = $start; $i < $end; $i++) {
2083
-            $found = (bool) $exclude;
2084
-            foreach ($types as $type) {
2085
-                if ($this->tokens[$i]['code'] === $type) {
2086
-                    $found = !$exclude;
2087
-                    break;
2088
-                }
2089
-            }
2090
-
2091
-            if ($found === true) {
2092
-                if ($value === null) {
2093
-                    return $i;
2094
-                } else if ($this->tokens[$i]['content'] === $value) {
2095
-                    return $i;
2096
-                }
2097
-            }
2098
-
2099
-            if ($local === true && $this->tokens[$i]['code'] === T_SEMICOLON) {
2100
-                break;
2101
-            }
2102
-        }//end for
2103
-
2104
-        return false;
2105
-
2106
-    }//end findNext()
2107
-
2108
-
2109
-    /**
2110
-     * Returns the position of the first non-whitespace token in a statement.
2111
-     *
2112
-     * @param int       $start  The position to start searching from in the token stack.
2113
-     * @param int|array $ignore Token types that should not be considered stop points.
2114
-     *
2115
-     * @return int
2116
-     */
2117
-    public function findStartOfStatement($start, $ignore=null)
2118
-    {
2119
-        $endTokens = Util\Tokens::$blockOpeners;
2120
-
2121
-        $endTokens[T_COLON]            = true;
2122
-        $endTokens[T_COMMA]            = true;
2123
-        $endTokens[T_DOUBLE_ARROW]     = true;
2124
-        $endTokens[T_SEMICOLON]        = true;
2125
-        $endTokens[T_OPEN_TAG]         = true;
2126
-        $endTokens[T_CLOSE_TAG]        = true;
2127
-        $endTokens[T_OPEN_SHORT_ARRAY] = true;
2128
-
2129
-        if ($ignore !== null) {
2130
-            $ignore = (array) $ignore;
2131
-            foreach ($ignore as $code) {
2132
-                unset($endTokens[$code]);
2133
-            }
2134
-        }
2135
-
2136
-        $lastNotEmpty = $start;
2137
-
2138
-        for ($i = $start; $i >= 0; $i--) {
2139
-            if (isset($endTokens[$this->tokens[$i]['code']]) === true) {
2140
-                // Found the end of the previous statement.
2141
-                return $lastNotEmpty;
2142
-            }
2143
-
2144
-            if (isset($this->tokens[$i]['scope_opener']) === true
2145
-                && $i === $this->tokens[$i]['scope_closer']
2146
-            ) {
2147
-                // Found the end of the previous scope block.
2148
-                return $lastNotEmpty;
2149
-            }
2150
-
2151
-            // Skip nested statements.
2152
-            if (isset($this->tokens[$i]['bracket_opener']) === true
2153
-                && $i === $this->tokens[$i]['bracket_closer']
2154
-            ) {
2155
-                $i = $this->tokens[$i]['bracket_opener'];
2156
-            } else if (isset($this->tokens[$i]['parenthesis_opener']) === true
2157
-                && $i === $this->tokens[$i]['parenthesis_closer']
2158
-            ) {
2159
-                $i = $this->tokens[$i]['parenthesis_opener'];
2160
-            }
2161
-
2162
-            if (isset(Util\Tokens::$emptyTokens[$this->tokens[$i]['code']]) === false) {
2163
-                $lastNotEmpty = $i;
2164
-            }
2165
-        }//end for
2166
-
2167
-        return 0;
2168
-
2169
-    }//end findStartOfStatement()
2170
-
2171
-
2172
-    /**
2173
-     * Returns the position of the last non-whitespace token in a statement.
2174
-     *
2175
-     * @param int       $start  The position to start searching from in the token stack.
2176
-     * @param int|array $ignore Token types that should not be considered stop points.
2177
-     *
2178
-     * @return int
2179
-     */
2180
-    public function findEndOfStatement($start, $ignore=null)
2181
-    {
2182
-        $endTokens = [
2183
-            T_COLON                => true,
2184
-            T_COMMA                => true,
2185
-            T_DOUBLE_ARROW         => true,
2186
-            T_SEMICOLON            => true,
2187
-            T_CLOSE_PARENTHESIS    => true,
2188
-            T_CLOSE_SQUARE_BRACKET => true,
2189
-            T_CLOSE_CURLY_BRACKET  => true,
2190
-            T_CLOSE_SHORT_ARRAY    => true,
2191
-            T_OPEN_TAG             => true,
2192
-            T_CLOSE_TAG            => true,
2193
-        ];
2194
-
2195
-        if ($ignore !== null) {
2196
-            $ignore = (array) $ignore;
2197
-            foreach ($ignore as $code) {
2198
-                unset($endTokens[$code]);
2199
-            }
2200
-        }
2201
-
2202
-        $lastNotEmpty = $start;
2203
-
2204
-        for ($i = $start; $i < $this->numTokens; $i++) {
2205
-            if ($i !== $start && isset($endTokens[$this->tokens[$i]['code']]) === true) {
2206
-                // Found the end of the statement.
2207
-                if ($this->tokens[$i]['code'] === T_CLOSE_PARENTHESIS
2208
-                    || $this->tokens[$i]['code'] === T_CLOSE_SQUARE_BRACKET
2209
-                    || $this->tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET
2210
-                    || $this->tokens[$i]['code'] === T_CLOSE_SHORT_ARRAY
2211
-                    || $this->tokens[$i]['code'] === T_OPEN_TAG
2212
-                    || $this->tokens[$i]['code'] === T_CLOSE_TAG
2213
-                ) {
2214
-                    return $lastNotEmpty;
2215
-                }
2216
-
2217
-                return $i;
2218
-            }
2219
-
2220
-            // Skip nested statements.
2221
-            if (isset($this->tokens[$i]['scope_closer']) === true
2222
-                && ($i === $this->tokens[$i]['scope_opener']
2223
-                || $i === $this->tokens[$i]['scope_condition'])
2224
-            ) {
2225
-                if ($i === $start && isset(Util\Tokens::$scopeOpeners[$this->tokens[$i]['code']]) === true) {
2226
-                    return $this->tokens[$i]['scope_closer'];
2227
-                }
2228
-
2229
-                $i = $this->tokens[$i]['scope_closer'];
2230
-            } else if (isset($this->tokens[$i]['bracket_closer']) === true
2231
-                && $i === $this->tokens[$i]['bracket_opener']
2232
-            ) {
2233
-                $i = $this->tokens[$i]['bracket_closer'];
2234
-            } else if (isset($this->tokens[$i]['parenthesis_closer']) === true
2235
-                && $i === $this->tokens[$i]['parenthesis_opener']
2236
-            ) {
2237
-                $i = $this->tokens[$i]['parenthesis_closer'];
2238
-            }
2239
-
2240
-            if (isset(Util\Tokens::$emptyTokens[$this->tokens[$i]['code']]) === false) {
2241
-                $lastNotEmpty = $i;
2242
-            }
2243
-        }//end for
2244
-
2245
-        return ($this->numTokens - 1);
2246
-
2247
-    }//end findEndOfStatement()
2248
-
2249
-
2250
-    /**
2251
-     * Returns the position of the first token on a line, matching given type.
2252
-     *
2253
-     * Returns false if no token can be found.
2254
-     *
2255
-     * @param int|string|array $types   The type(s) of tokens to search for.
2256
-     * @param int              $start   The position to start searching from in the
2257
-     *                                  token stack. The first token matching on
2258
-     *                                  this line before this token will be returned.
2259
-     * @param bool             $exclude If true, find the token that is NOT of
2260
-     *                                  the types specified in $types.
2261
-     * @param string           $value   The value that the token must be equal to.
2262
-     *                                  If value is omitted, tokens with any value will
2263
-     *                                  be returned.
2264
-     *
2265
-     * @return int | bool
2266
-     */
2267
-    public function findFirstOnLine($types, $start, $exclude=false, $value=null)
2268
-    {
2269
-        if (is_array($types) === false) {
2270
-            $types = [$types];
2271
-        }
2272
-
2273
-        $foundToken = false;
2274
-
2275
-        for ($i = $start; $i >= 0; $i--) {
2276
-            if ($this->tokens[$i]['line'] < $this->tokens[$start]['line']) {
2277
-                break;
2278
-            }
2279
-
2280
-            $found = $exclude;
2281
-            foreach ($types as $type) {
2282
-                if ($exclude === false) {
2283
-                    if ($this->tokens[$i]['code'] === $type) {
2284
-                        $found = true;
2285
-                        break;
2286
-                    }
2287
-                } else {
2288
-                    if ($this->tokens[$i]['code'] === $type) {
2289
-                        $found = false;
2290
-                        break;
2291
-                    }
2292
-                }
2293
-            }
2294
-
2295
-            if ($found === true) {
2296
-                if ($value === null) {
2297
-                    $foundToken = $i;
2298
-                } else if ($this->tokens[$i]['content'] === $value) {
2299
-                    $foundToken = $i;
2300
-                }
2301
-            }
2302
-        }//end for
2303
-
2304
-        return $foundToken;
2305
-
2306
-    }//end findFirstOnLine()
2307
-
2308
-
2309
-    /**
2310
-     * Determine if the passed token has a condition of one of the passed types.
2311
-     *
2312
-     * @param int              $stackPtr The position of the token we are checking.
2313
-     * @param int|string|array $types    The type(s) of tokens to search for.
2314
-     *
2315
-     * @return boolean
2316
-     */
2317
-    public function hasCondition($stackPtr, $types)
2318
-    {
2319
-        // Check for the existence of the token.
2320
-        if (isset($this->tokens[$stackPtr]) === false) {
2321
-            return false;
2322
-        }
2323
-
2324
-        // Make sure the token has conditions.
2325
-        if (isset($this->tokens[$stackPtr]['conditions']) === false) {
2326
-            return false;
2327
-        }
2328
-
2329
-        $types      = (array) $types;
2330
-        $conditions = $this->tokens[$stackPtr]['conditions'];
2331
-
2332
-        foreach ($types as $type) {
2333
-            if (in_array($type, $conditions, true) === true) {
2334
-                // We found a token with the required type.
2335
-                return true;
2336
-            }
2337
-        }
2338
-
2339
-        return false;
2340
-
2341
-    }//end hasCondition()
2342
-
2343
-
2344
-    /**
2345
-     * Return the position of the condition for the passed token.
2346
-     *
2347
-     * Returns FALSE if the token does not have the condition.
2348
-     *
2349
-     * @param int        $stackPtr The position of the token we are checking.
2350
-     * @param int|string $type     The type of token to search for.
2351
-     *
2352
-     * @return int
2353
-     */
2354
-    public function getCondition($stackPtr, $type)
2355
-    {
2356
-        // Check for the existence of the token.
2357
-        if (isset($this->tokens[$stackPtr]) === false) {
2358
-            return false;
2359
-        }
2360
-
2361
-        // Make sure the token has conditions.
2362
-        if (isset($this->tokens[$stackPtr]['conditions']) === false) {
2363
-            return false;
2364
-        }
2365
-
2366
-        $conditions = $this->tokens[$stackPtr]['conditions'];
2367
-        foreach ($conditions as $token => $condition) {
2368
-            if ($condition === $type) {
2369
-                return $token;
2370
-            }
2371
-        }
2372
-
2373
-        return false;
2374
-
2375
-    }//end getCondition()
2376
-
2377
-
2378
-    /**
2379
-     * Returns the name of the class that the specified class extends.
2380
-     * (works for classes, anonymous classes and interfaces)
2381
-     *
2382
-     * Returns FALSE on error or if there is no extended class name.
2383
-     *
2384
-     * @param int $stackPtr The stack position of the class.
2385
-     *
2386
-     * @return string|false
2387
-     */
2388
-    public function findExtendedClassName($stackPtr)
2389
-    {
2390
-        // Check for the existence of the token.
2391
-        if (isset($this->tokens[$stackPtr]) === false) {
2392
-            return false;
2393
-        }
2394
-
2395
-        if ($this->tokens[$stackPtr]['code'] !== T_CLASS
2396
-            && $this->tokens[$stackPtr]['code'] !== T_ANON_CLASS
2397
-            && $this->tokens[$stackPtr]['code'] !== T_INTERFACE
2398
-        ) {
2399
-            return false;
2400
-        }
2401
-
2402
-        if (isset($this->tokens[$stackPtr]['scope_opener']) === false) {
2403
-            return false;
2404
-        }
2405
-
2406
-        $classOpenerIndex = $this->tokens[$stackPtr]['scope_opener'];
2407
-        $extendsIndex     = $this->findNext(T_EXTENDS, $stackPtr, $classOpenerIndex);
2408
-        if (false === $extendsIndex) {
2409
-            return false;
2410
-        }
2411
-
2412
-        $find = [
2413
-            T_NS_SEPARATOR,
2414
-            T_STRING,
2415
-            T_WHITESPACE,
2416
-        ];
2417
-
2418
-        $end  = $this->findNext($find, ($extendsIndex + 1), ($classOpenerIndex + 1), true);
2419
-        $name = $this->getTokensAsString(($extendsIndex + 1), ($end - $extendsIndex - 1));
2420
-        $name = trim($name);
2421
-
2422
-        if ($name === '') {
2423
-            return false;
2424
-        }
2425
-
2426
-        return $name;
2427
-
2428
-    }//end findExtendedClassName()
2429
-
2430
-
2431
-    /**
2432
-     * Returns the names of the interfaces that the specified class implements.
2433
-     *
2434
-     * Returns FALSE on error or if there are no implemented interface names.
2435
-     *
2436
-     * @param int $stackPtr The stack position of the class.
2437
-     *
2438
-     * @return array|false
2439
-     */
2440
-    public function findImplementedInterfaceNames($stackPtr)
2441
-    {
2442
-        // Check for the existence of the token.
2443
-        if (isset($this->tokens[$stackPtr]) === false) {
2444
-            return false;
2445
-        }
2446
-
2447
-        if ($this->tokens[$stackPtr]['code'] !== T_CLASS
2448
-            && $this->tokens[$stackPtr]['code'] !== T_ANON_CLASS
2449
-        ) {
2450
-            return false;
2451
-        }
2452
-
2453
-        if (isset($this->tokens[$stackPtr]['scope_closer']) === false) {
2454
-            return false;
2455
-        }
2456
-
2457
-        $classOpenerIndex = $this->tokens[$stackPtr]['scope_opener'];
2458
-        $implementsIndex  = $this->findNext(T_IMPLEMENTS, $stackPtr, $classOpenerIndex);
2459
-        if ($implementsIndex === false) {
2460
-            return false;
2461
-        }
2462
-
2463
-        $find = [
2464
-            T_NS_SEPARATOR,
2465
-            T_STRING,
2466
-            T_WHITESPACE,
2467
-            T_COMMA,
2468
-        ];
2469
-
2470
-        $end  = $this->findNext($find, ($implementsIndex + 1), ($classOpenerIndex + 1), true);
2471
-        $name = $this->getTokensAsString(($implementsIndex + 1), ($end - $implementsIndex - 1));
2472
-        $name = trim($name);
2473
-
2474
-        if ($name === '') {
2475
-            return false;
2476
-        } else {
2477
-            $names = explode(',', $name);
2478
-            $names = array_map('trim', $names);
2479
-            return $names;
2480
-        }
2481
-
2482
-    }//end findImplementedInterfaceNames()
22
+	/**
23
+	 * The absolute path to the file associated with this object.
24
+	 *
25
+	 * @var string
26
+	 */
27
+	public $path = '';
28
+
29
+	/**
30
+	 * The content of the file.
31
+	 *
32
+	 * @var string
33
+	 */
34
+	protected $content = '';
35
+
36
+	/**
37
+	 * The config data for the run.
38
+	 *
39
+	 * @var \PHP_CodeSniffer\Config
40
+	 */
41
+	public $config = null;
42
+
43
+	/**
44
+	 * The ruleset used for the run.
45
+	 *
46
+	 * @var \PHP_CodeSniffer\Ruleset
47
+	 */
48
+	public $ruleset = null;
49
+
50
+	/**
51
+	 * If TRUE, the entire file is being ignored.
52
+	 *
53
+	 * @var boolean
54
+	 */
55
+	public $ignored = false;
56
+
57
+	/**
58
+	 * The EOL character this file uses.
59
+	 *
60
+	 * @var string
61
+	 */
62
+	public $eolChar = '';
63
+
64
+	/**
65
+	 * The Fixer object to control fixing errors.
66
+	 *
67
+	 * @var \PHP_CodeSniffer\Fixer
68
+	 */
69
+	public $fixer = null;
70
+
71
+	/**
72
+	 * The tokenizer being used for this file.
73
+	 *
74
+	 * @var \PHP_CodeSniffer\Tokenizers\Tokenizer
75
+	 */
76
+	public $tokenizer = null;
77
+
78
+	/**
79
+	 * The name of the tokenizer being used for this file.
80
+	 *
81
+	 * @var string
82
+	 */
83
+	public $tokenizerType = 'PHP';
84
+
85
+	/**
86
+	 * Was the file loaded from cache?
87
+	 *
88
+	 * If TRUE, the file was loaded from a local cache.
89
+	 * If FALSE, the file was tokenized and processed fully.
90
+	 *
91
+	 * @var boolean
92
+	 */
93
+	public $fromCache = false;
94
+
95
+	/**
96
+	 * The number of tokens in this file.
97
+	 *
98
+	 * Stored here to save calling count() everywhere.
99
+	 *
100
+	 * @var integer
101
+	 */
102
+	public $numTokens = 0;
103
+
104
+	/**
105
+	 * The tokens stack map.
106
+	 *
107
+	 * @var array
108
+	 */
109
+	protected $tokens = [];
110
+
111
+	/**
112
+	 * The errors raised from sniffs.
113
+	 *
114
+	 * @var array
115
+	 * @see getErrors()
116
+	 */
117
+	protected $errors = [];
118
+
119
+	/**
120
+	 * The warnings raised from sniffs.
121
+	 *
122
+	 * @var array
123
+	 * @see getWarnings()
124
+	 */
125
+	protected $warnings = [];
126
+
127
+	/**
128
+	 * The metrics recorded by sniffs.
129
+	 *
130
+	 * @var array
131
+	 * @see getMetrics()
132
+	 */
133
+	protected $metrics = [];
134
+
135
+	/**
136
+	 * The metrics recorded for each token.
137
+	 *
138
+	 * Stops the same metric being recorded for the same token twice.
139
+	 *
140
+	 * @var array
141
+	 * @see getMetrics()
142
+	 */
143
+	private $metricTokens = [];
144
+
145
+	/**
146
+	 * The total number of errors raised.
147
+	 *
148
+	 * @var integer
149
+	 */
150
+	protected $errorCount = 0;
151
+
152
+	/**
153
+	 * The total number of warnings raised.
154
+	 *
155
+	 * @var integer
156
+	 */
157
+	protected $warningCount = 0;
158
+
159
+	/**
160
+	 * The total number of errors and warnings that can be fixed.
161
+	 *
162
+	 * @var integer
163
+	 */
164
+	protected $fixableCount = 0;
165
+
166
+	/**
167
+	 * The total number of errors and warnings that were fixed.
168
+	 *
169
+	 * @var integer
170
+	 */
171
+	protected $fixedCount = 0;
172
+
173
+	/**
174
+	 * An array of sniffs that are being ignored.
175
+	 *
176
+	 * @var array
177
+	 */
178
+	protected $ignoredListeners = [];
179
+
180
+	/**
181
+	 * An array of message codes that are being ignored.
182
+	 *
183
+	 * @var array
184
+	 */
185
+	protected $ignoredCodes = [];
186
+
187
+	/**
188
+	 * An array of sniffs listening to this file's processing.
189
+	 *
190
+	 * @var \PHP_CodeSniffer\Sniffs\Sniff[]
191
+	 */
192
+	protected $listeners = [];
193
+
194
+	/**
195
+	 * The class name of the sniff currently processing the file.
196
+	 *
197
+	 * @var string
198
+	 */
199
+	protected $activeListener = '';
200
+
201
+	/**
202
+	 * An array of sniffs being processed and how long they took.
203
+	 *
204
+	 * @var array
205
+	 */
206
+	protected $listenerTimes = [];
207
+
208
+	/**
209
+	 * A cache of often used config settings to improve performance.
210
+	 *
211
+	 * Storing them here saves 10k+ calls to __get() in the Config class.
212
+	 *
213
+	 * @var array
214
+	 */
215
+	protected $configCache = [];
216
+
217
+
218
+	/**
219
+	 * Constructs a file.
220
+	 *
221
+	 * @param string                   $path    The absolute path to the file to process.
222
+	 * @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
223
+	 * @param \PHP_CodeSniffer\Config  $config  The config data for the run.
224
+	 *
225
+	 * @return void
226
+	 */
227
+	public function __construct($path, Ruleset $ruleset, Config $config)
228
+	{
229
+		$this->path    = $path;
230
+		$this->ruleset = $ruleset;
231
+		$this->config  = $config;
232
+		$this->fixer   = new Fixer();
233
+
234
+		$parts     = explode('.', $path);
235
+		$extension = array_pop($parts);
236
+		if (isset($config->extensions[$extension]) === true) {
237
+			$this->tokenizerType = $config->extensions[$extension];
238
+		} else {
239
+			// Revert to default.
240
+			$this->tokenizerType = 'PHP';
241
+		}
242
+
243
+		$this->configCache['cache']           = $this->config->cache;
244
+		$this->configCache['sniffs']          = array_map('strtolower', $this->config->sniffs);
245
+		$this->configCache['exclude']         = array_map('strtolower', $this->config->exclude);
246
+		$this->configCache['errorSeverity']   = $this->config->errorSeverity;
247
+		$this->configCache['warningSeverity'] = $this->config->warningSeverity;
248
+		$this->configCache['recordErrors']    = $this->config->recordErrors;
249
+		$this->configCache['ignorePatterns']  = $this->ruleset->ignorePatterns;
250
+		$this->configCache['includePatterns'] = $this->ruleset->includePatterns;
251
+
252
+	}//end __construct()
253
+
254
+
255
+	/**
256
+	 * Set the content of the file.
257
+	 *
258
+	 * Setting the content also calculates the EOL char being used.
259
+	 *
260
+	 * @param string $content The file content.
261
+	 *
262
+	 * @return void
263
+	 */
264
+	public function setContent($content)
265
+	{
266
+		$this->content = $content;
267
+		$this->tokens  = [];
268
+
269
+		try {
270
+			$this->eolChar = Util\Common::detectLineEndings($content);
271
+		} catch (RuntimeException $e) {
272
+			$this->addWarningOnLine($e->getMessage(), 1, 'Internal.DetectLineEndings');
273
+			return;
274
+		}
275
+
276
+	}//end setContent()
277
+
278
+
279
+	/**
280
+	 * Reloads the content of the file.
281
+	 *
282
+	 * By default, we have no idea where our content comes from,
283
+	 * so we can't do anything.
284
+	 *
285
+	 * @return void
286
+	 */
287
+	public function reloadContent()
288
+	{
289
+
290
+	}//end reloadContent()
291
+
292
+
293
+	/**
294
+	 * Disables caching of this file.
295
+	 *
296
+	 * @return void
297
+	 */
298
+	public function disableCaching()
299
+	{
300
+		$this->configCache['cache'] = false;
301
+
302
+	}//end disableCaching()
303
+
304
+
305
+	/**
306
+	 * Starts the stack traversal and tells listeners when tokens are found.
307
+	 *
308
+	 * @return void
309
+	 */
310
+	public function process()
311
+	{
312
+		if ($this->ignored === true) {
313
+			return;
314
+		}
315
+
316
+		$this->errors       = [];
317
+		$this->warnings     = [];
318
+		$this->errorCount   = 0;
319
+		$this->warningCount = 0;
320
+		$this->fixableCount = 0;
321
+
322
+		$this->parse();
323
+
324
+		// Check if tokenizer errors cause this file to be ignored.
325
+		if ($this->ignored === true) {
326
+			return;
327
+		}
328
+
329
+		$this->fixer->startFile($this);
330
+
331
+		if (PHP_CODESNIFFER_VERBOSITY > 2) {
332
+			echo "\t*** START TOKEN PROCESSING ***".PHP_EOL;
333
+		}
334
+
335
+		$foundCode        = false;
336
+		$listenerIgnoreTo = [];
337
+		$inTests          = defined('PHP_CODESNIFFER_IN_TESTS');
338
+		$checkAnnotations = $this->config->annotations;
339
+
340
+		// Foreach of the listeners that have registered to listen for this
341
+		// token, get them to process it.
342
+		foreach ($this->tokens as $stackPtr => $token) {
343
+			// Check for ignored lines.
344
+			if ($checkAnnotations === true
345
+				&& ($token['code'] === T_COMMENT
346
+				|| $token['code'] === T_PHPCS_IGNORE_FILE
347
+				|| $token['code'] === T_PHPCS_SET
348
+				|| $token['code'] === T_DOC_COMMENT_STRING
349
+				|| $token['code'] === T_DOC_COMMENT_TAG
350
+				|| ($inTests === true && $token['code'] === T_INLINE_HTML))
351
+			) {
352
+				$commentText      = ltrim($this->tokens[$stackPtr]['content'], ' /*');
353
+				$commentTextLower = strtolower($commentText);
354
+				if (strpos($commentText, '@codingStandards') !== false) {
355
+					if (strpos($commentText, '@codingStandardsIgnoreFile') !== false) {
356
+						// Ignoring the whole file, just a little late.
357
+						$this->errors       = [];
358
+						$this->warnings     = [];
359
+						$this->errorCount   = 0;
360
+						$this->warningCount = 0;
361
+						$this->fixableCount = 0;
362
+						return;
363
+					} else if (strpos($commentText, '@codingStandardsChangeSetting') !== false) {
364
+						$start   = strpos($commentText, '@codingStandardsChangeSetting');
365
+						$comment = substr($commentText, ($start + 30));
366
+						$parts   = explode(' ', $comment);
367
+						if (count($parts) >= 2) {
368
+							$sniffParts = explode('.', $parts[0]);
369
+							if (count($sniffParts) >= 3) {
370
+								// If the sniff code is not known to us, it has not been registered in this run.
371
+								// But don't throw an error as it could be there for a different standard to use.
372
+								if (isset($this->ruleset->sniffCodes[$parts[0]]) === true) {
373
+									$listenerCode  = array_shift($parts);
374
+									$propertyCode  = array_shift($parts);
375
+									$propertyValue = rtrim(implode(' ', $parts), " */\r\n");
376
+									$listenerClass = $this->ruleset->sniffCodes[$listenerCode];
377
+									$this->ruleset->setSniffProperty($listenerClass, $propertyCode, $propertyValue);
378
+								}
379
+							}
380
+						}
381
+					}//end if
382
+				} else if (substr($commentTextLower, 0, 16) === 'phpcs:ignorefile'
383
+					|| substr($commentTextLower, 0, 17) === '@phpcs:ignorefile'
384
+				) {
385
+					// Ignoring the whole file, just a little late.
386
+					$this->errors       = [];
387
+					$this->warnings     = [];
388
+					$this->errorCount   = 0;
389
+					$this->warningCount = 0;
390
+					$this->fixableCount = 0;
391
+					return;
392
+				} else if (substr($commentTextLower, 0, 9) === 'phpcs:set'
393
+					|| substr($commentTextLower, 0, 10) === '@phpcs:set'
394
+				) {
395
+					if (isset($token['sniffCode']) === true) {
396
+						$listenerCode = $token['sniffCode'];
397
+						if (isset($this->ruleset->sniffCodes[$listenerCode]) === true) {
398
+							$propertyCode  = $token['sniffProperty'];
399
+							$propertyValue = $token['sniffPropertyValue'];
400
+							$listenerClass = $this->ruleset->sniffCodes[$listenerCode];
401
+							$this->ruleset->setSniffProperty($listenerClass, $propertyCode, $propertyValue);
402
+						}
403
+					}
404
+				}//end if
405
+			}//end if
406
+
407
+			if (PHP_CODESNIFFER_VERBOSITY > 2) {
408
+				$type    = $token['type'];
409
+				$content = Util\Common::prepareForOutput($token['content']);
410
+				echo "\t\tProcess token $stackPtr: $type => $content".PHP_EOL;
411
+			}
412
+
413
+			if ($token['code'] !== T_INLINE_HTML) {
414
+				$foundCode = true;
415
+			}
416
+
417
+			if (isset($this->ruleset->tokenListeners[$token['code']]) === false) {
418
+				continue;
419
+			}
420
+
421
+			foreach ($this->ruleset->tokenListeners[$token['code']] as $listenerData) {
422
+				if (isset($this->ignoredListeners[$listenerData['class']]) === true
423
+					|| (isset($listenerIgnoreTo[$listenerData['class']]) === true
424
+					&& $listenerIgnoreTo[$listenerData['class']] > $stackPtr)
425
+				) {
426
+					// This sniff is ignoring past this token, or the whole file.
427
+					continue;
428
+				}
429
+
430
+				// Make sure this sniff supports the tokenizer
431
+				// we are currently using.
432
+				$class = $listenerData['class'];
433
+
434
+				if (isset($listenerData['tokenizers'][$this->tokenizerType]) === false) {
435
+					continue;
436
+				}
437
+
438
+				// If the file path matches one of our ignore patterns, skip it.
439
+				// While there is support for a type of each pattern
440
+				// (absolute or relative) we don't actually support it here.
441
+				foreach ($listenerData['ignore'] as $pattern) {
442
+					// We assume a / directory separator, as do the exclude rules
443
+					// most developers write, so we need a special case for any system
444
+					// that is different.
445
+					if (DIRECTORY_SEPARATOR === '\\') {
446
+						$pattern = str_replace('/', '\\\\', $pattern);
447
+					}
448
+
449
+					$pattern = '`'.$pattern.'`i';
450
+					if (preg_match($pattern, $this->path) === 1) {
451
+						$this->ignoredListeners[$class] = true;
452
+						continue(2);
453
+					}
454
+				}
455
+
456
+				// If the file path does not match one of our include patterns, skip it.
457
+				// While there is support for a type of each pattern
458
+				// (absolute or relative) we don't actually support it here.
459
+				if (empty($listenerData['include']) === false) {
460
+					$included = false;
461
+					foreach ($listenerData['include'] as $pattern) {
462
+						// We assume a / directory separator, as do the exclude rules
463
+						// most developers write, so we need a special case for any system
464
+						// that is different.
465
+						if (DIRECTORY_SEPARATOR === '\\') {
466
+							$pattern = str_replace('/', '\\\\', $pattern);
467
+						}
468
+
469
+						$pattern = '`'.$pattern.'`i';
470
+						if (preg_match($pattern, $this->path) === 1) {
471
+							$included = true;
472
+							break;
473
+						}
474
+					}
475
+
476
+					if ($included === false) {
477
+						$this->ignoredListeners[$class] = true;
478
+						continue;
479
+					}
480
+				}//end if
481
+
482
+				$this->activeListener = $class;
483
+
484
+				if (PHP_CODESNIFFER_VERBOSITY > 2) {
485
+					$startTime = microtime(true);
486
+					echo "\t\t\tProcessing ".$this->activeListener.'... ';
487
+				}
488
+
489
+				$ignoreTo = $this->ruleset->sniffs[$class]->process($this, $stackPtr);
490
+				if ($ignoreTo !== null) {
491
+					$listenerIgnoreTo[$this->activeListener] = $ignoreTo;
492
+				}
493
+
494
+				if (PHP_CODESNIFFER_VERBOSITY > 2) {
495
+					$timeTaken = (microtime(true) - $startTime);
496
+					if (isset($this->listenerTimes[$this->activeListener]) === false) {
497
+						$this->listenerTimes[$this->activeListener] = 0;
498
+					}
499
+
500
+					$this->listenerTimes[$this->activeListener] += $timeTaken;
501
+
502
+					$timeTaken = round(($timeTaken), 4);
503
+					echo "DONE in $timeTaken seconds".PHP_EOL;
504
+				}
505
+
506
+				$this->activeListener = '';
507
+			}//end foreach
508
+		}//end foreach
509
+
510
+		// If short open tags are off but the file being checked uses
511
+		// short open tags, the whole content will be inline HTML
512
+		// and nothing will be checked. So try and handle this case.
513
+		// We don't show this error for STDIN because we can't be sure the content
514
+		// actually came directly from the user. It could be something like
515
+		// refs from a Git pre-push hook.
516
+		if ($foundCode === false && $this->tokenizerType === 'PHP' && $this->path !== 'STDIN') {
517
+			$shortTags = (bool) ini_get('short_open_tag');
518
+			if ($shortTags === false) {
519
+				$error = 'No PHP code was found in this file and short open tags are not allowed by this install of PHP. This file may be using short open tags but PHP does not allow them.';
520
+				$this->addWarning($error, null, 'Internal.NoCodeFound');
521
+			}
522
+		}
523
+
524
+		if (PHP_CODESNIFFER_VERBOSITY > 2) {
525
+			echo "\t*** END TOKEN PROCESSING ***".PHP_EOL;
526
+			echo "\t*** START SNIFF PROCESSING REPORT ***".PHP_EOL;
527
+
528
+			asort($this->listenerTimes, SORT_NUMERIC);
529
+			$this->listenerTimes = array_reverse($this->listenerTimes, true);
530
+			foreach ($this->listenerTimes as $listener => $timeTaken) {
531
+				echo "\t$listener: ".round(($timeTaken), 4).' secs'.PHP_EOL;
532
+			}
533
+
534
+			echo "\t*** END SNIFF PROCESSING REPORT ***".PHP_EOL;
535
+		}
536
+
537
+		$this->fixedCount += $this->fixer->getFixCount();
538
+
539
+	}//end process()
540
+
541
+
542
+	/**
543
+	 * Tokenizes the file and prepares it for the test run.
544
+	 *
545
+	 * @return void
546
+	 */
547
+	public function parse()
548
+	{
549
+		if (empty($this->tokens) === false) {
550
+			// File has already been parsed.
551
+			return;
552
+		}
553
+
554
+		try {
555
+			$tokenizerClass  = 'PHP_CodeSniffer\Tokenizers\\'.$this->tokenizerType;
556
+			$this->tokenizer = new $tokenizerClass($this->content, $this->config, $this->eolChar);
557
+			$this->tokens    = $this->tokenizer->getTokens();
558
+		} catch (TokenizerException $e) {
559
+			$this->ignored = true;
560
+			$this->addWarning($e->getMessage(), null, 'Internal.Tokenizer.Exception');
561
+			if (PHP_CODESNIFFER_VERBOSITY > 0) {
562
+				echo "[$this->tokenizerType => tokenizer error]... ";
563
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
564
+					echo PHP_EOL;
565
+				}
566
+			}
567
+
568
+			return;
569
+		}
570
+
571
+		$this->numTokens = count($this->tokens);
572
+
573
+		// Check for mixed line endings as these can cause tokenizer errors and we
574
+		// should let the user know that the results they get may be incorrect.
575
+		// This is done by removing all backslashes, removing the newline char we
576
+		// detected, then converting newlines chars into text. If any backslashes
577
+		// are left at the end, we have additional newline chars in use.
578
+		$contents = str_replace('\\', '', $this->content);
579
+		$contents = str_replace($this->eolChar, '', $contents);
580
+		$contents = str_replace("\n", '\n', $contents);
581
+		$contents = str_replace("\r", '\r', $contents);
582
+		if (strpos($contents, '\\') !== false) {
583
+			$error = 'File has mixed line endings; this may cause incorrect results';
584
+			$this->addWarningOnLine($error, 1, 'Internal.LineEndings.Mixed');
585
+		}
586
+
587
+		if (PHP_CODESNIFFER_VERBOSITY > 0) {
588
+			if ($this->numTokens === 0) {
589
+				$numLines = 0;
590
+			} else {
591
+				$numLines = $this->tokens[($this->numTokens - 1)]['line'];
592
+			}
593
+
594
+			echo "[$this->tokenizerType => $this->numTokens tokens in $numLines lines]... ";
595
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
596
+				echo PHP_EOL;
597
+			}
598
+		}
599
+
600
+	}//end parse()
601
+
602
+
603
+	/**
604
+	 * Returns the token stack for this file.
605
+	 *
606
+	 * @return array
607
+	 */
608
+	public function getTokens()
609
+	{
610
+		return $this->tokens;
611
+
612
+	}//end getTokens()
613
+
614
+
615
+	/**
616
+	 * Remove vars stored in this file that are no longer required.
617
+	 *
618
+	 * @return void
619
+	 */
620
+	public function cleanUp()
621
+	{
622
+		$this->listenerTimes = null;
623
+		$this->content       = null;
624
+		$this->tokens        = null;
625
+		$this->metricTokens  = null;
626
+		$this->tokenizer     = null;
627
+		$this->fixer         = null;
628
+		$this->config        = null;
629
+		$this->ruleset       = null;
630
+
631
+	}//end cleanUp()
632
+
633
+
634
+	/**
635
+	 * Records an error against a specific token in the file.
636
+	 *
637
+	 * @param string  $error    The error message.
638
+	 * @param int     $stackPtr The stack position where the error occurred.
639
+	 * @param string  $code     A violation code unique to the sniff message.
640
+	 * @param array   $data     Replacements for the error message.
641
+	 * @param int     $severity The severity level for this error. A value of 0
642
+	 *                          will be converted into the default severity level.
643
+	 * @param boolean $fixable  Can the error be fixed by the sniff?
644
+	 *
645
+	 * @return boolean
646
+	 */
647
+	public function addError(
648
+		$error,
649
+		$stackPtr,
650
+		$code,
651
+		$data=[],
652
+		$severity=0,
653
+		$fixable=false
654
+	) {
655
+		if ($stackPtr === null) {
656
+			$line   = 1;
657
+			$column = 1;
658
+		} else {
659
+			$line   = $this->tokens[$stackPtr]['line'];
660
+			$column = $this->tokens[$stackPtr]['column'];
661
+		}
662
+
663
+		return $this->addMessage(true, $error, $line, $column, $code, $data, $severity, $fixable);
664
+
665
+	}//end addError()
666
+
667
+
668
+	/**
669
+	 * Records a warning against a specific token in the file.
670
+	 *
671
+	 * @param string  $warning  The error message.
672
+	 * @param int     $stackPtr The stack position where the error occurred.
673
+	 * @param string  $code     A violation code unique to the sniff message.
674
+	 * @param array   $data     Replacements for the warning message.
675
+	 * @param int     $severity The severity level for this warning. A value of 0
676
+	 *                          will be converted into the default severity level.
677
+	 * @param boolean $fixable  Can the warning be fixed by the sniff?
678
+	 *
679
+	 * @return boolean
680
+	 */
681
+	public function addWarning(
682
+		$warning,
683
+		$stackPtr,
684
+		$code,
685
+		$data=[],
686
+		$severity=0,
687
+		$fixable=false
688
+	) {
689
+		if ($stackPtr === null) {
690
+			$line   = 1;
691
+			$column = 1;
692
+		} else {
693
+			$line   = $this->tokens[$stackPtr]['line'];
694
+			$column = $this->tokens[$stackPtr]['column'];
695
+		}
696
+
697
+		return $this->addMessage(false, $warning, $line, $column, $code, $data, $severity, $fixable);
698
+
699
+	}//end addWarning()
700
+
701
+
702
+	/**
703
+	 * Records an error against a specific line in the file.
704
+	 *
705
+	 * @param string $error    The error message.
706
+	 * @param int    $line     The line on which the error occurred.
707
+	 * @param string $code     A violation code unique to the sniff message.
708
+	 * @param array  $data     Replacements for the error message.
709
+	 * @param int    $severity The severity level for this error. A value of 0
710
+	 *                         will be converted into the default severity level.
711
+	 *
712
+	 * @return boolean
713
+	 */
714
+	public function addErrorOnLine(
715
+		$error,
716
+		$line,
717
+		$code,
718
+		$data=[],
719
+		$severity=0
720
+	) {
721
+		return $this->addMessage(true, $error, $line, 1, $code, $data, $severity, false);
722
+
723
+	}//end addErrorOnLine()
724
+
725
+
726
+	/**
727
+	 * Records a warning against a specific token in the file.
728
+	 *
729
+	 * @param string $warning  The error message.
730
+	 * @param int    $line     The line on which the warning occurred.
731
+	 * @param string $code     A violation code unique to the sniff message.
732
+	 * @param array  $data     Replacements for the warning message.
733
+	 * @param int    $severity The severity level for this warning. A value of 0 will
734
+	 *                         will be converted into the default severity level.
735
+	 *
736
+	 * @return boolean
737
+	 */
738
+	public function addWarningOnLine(
739
+		$warning,
740
+		$line,
741
+		$code,
742
+		$data=[],
743
+		$severity=0
744
+	) {
745
+		return $this->addMessage(false, $warning, $line, 1, $code, $data, $severity, false);
746
+
747
+	}//end addWarningOnLine()
748
+
749
+
750
+	/**
751
+	 * Records a fixable error against a specific token in the file.
752
+	 *
753
+	 * Returns true if the error was recorded and should be fixed.
754
+	 *
755
+	 * @param string $error    The error message.
756
+	 * @param int    $stackPtr The stack position where the error occurred.
757
+	 * @param string $code     A violation code unique to the sniff message.
758
+	 * @param array  $data     Replacements for the error message.
759
+	 * @param int    $severity The severity level for this error. A value of 0
760
+	 *                         will be converted into the default severity level.
761
+	 *
762
+	 * @return boolean
763
+	 */
764
+	public function addFixableError(
765
+		$error,
766
+		$stackPtr,
767
+		$code,
768
+		$data=[],
769
+		$severity=0
770
+	) {
771
+		$recorded = $this->addError($error, $stackPtr, $code, $data, $severity, true);
772
+		if ($recorded === true && $this->fixer->enabled === true) {
773
+			return true;
774
+		}
775
+
776
+		return false;
777
+
778
+	}//end addFixableError()
779
+
780
+
781
+	/**
782
+	 * Records a fixable warning against a specific token in the file.
783
+	 *
784
+	 * Returns true if the warning was recorded and should be fixed.
785
+	 *
786
+	 * @param string $warning  The error message.
787
+	 * @param int    $stackPtr The stack position where the error occurred.
788
+	 * @param string $code     A violation code unique to the sniff message.
789
+	 * @param array  $data     Replacements for the warning message.
790
+	 * @param int    $severity The severity level for this warning. A value of 0
791
+	 *                         will be converted into the default severity level.
792
+	 *
793
+	 * @return boolean
794
+	 */
795
+	public function addFixableWarning(
796
+		$warning,
797
+		$stackPtr,
798
+		$code,
799
+		$data=[],
800
+		$severity=0
801
+	) {
802
+		$recorded = $this->addWarning($warning, $stackPtr, $code, $data, $severity, true);
803
+		if ($recorded === true && $this->fixer->enabled === true) {
804
+			return true;
805
+		}
806
+
807
+		return false;
808
+
809
+	}//end addFixableWarning()
810
+
811
+
812
+	/**
813
+	 * Adds an error to the error stack.
814
+	 *
815
+	 * @param boolean $error    Is this an error message?
816
+	 * @param string  $message  The text of the message.
817
+	 * @param int     $line     The line on which the message occurred.
818
+	 * @param int     $column   The column at which the message occurred.
819
+	 * @param string  $code     A violation code unique to the sniff message.
820
+	 * @param array   $data     Replacements for the message.
821
+	 * @param int     $severity The severity level for this message. A value of 0
822
+	 *                          will be converted into the default severity level.
823
+	 * @param boolean $fixable  Can the problem be fixed by the sniff?
824
+	 *
825
+	 * @return boolean
826
+	 */
827
+	protected function addMessage($error, $message, $line, $column, $code, $data, $severity, $fixable)
828
+	{
829
+		// Check if this line is ignoring all message codes.
830
+		if (isset($this->tokenizer->ignoredLines[$line]['.all']) === true) {
831
+			return false;
832
+		}
833
+
834
+		// Work out which sniff generated the message.
835
+		$parts = explode('.', $code);
836
+		if ($parts[0] === 'Internal') {
837
+			// An internal message.
838
+			$listenerCode = Util\Common::getSniffCode($this->activeListener);
839
+			$sniffCode    = $code;
840
+			$checkCodes   = [$sniffCode];
841
+		} else {
842
+			if ($parts[0] !== $code) {
843
+				// The full message code has been passed in.
844
+				$sniffCode    = $code;
845
+				$listenerCode = substr($sniffCode, 0, strrpos($sniffCode, '.'));
846
+			} else {
847
+				$listenerCode = Util\Common::getSniffCode($this->activeListener);
848
+				$sniffCode    = $listenerCode.'.'.$code;
849
+				$parts        = explode('.', $sniffCode);
850
+			}
851
+
852
+			$checkCodes = [
853
+				$sniffCode,
854
+				$parts[0].'.'.$parts[1].'.'.$parts[2],
855
+				$parts[0].'.'.$parts[1],
856
+				$parts[0],
857
+			];
858
+		}//end if
859
+
860
+		if (isset($this->tokenizer->ignoredLines[$line]) === true) {
861
+			// Check if this line is ignoring this specific message.
862
+			$ignored = false;
863
+			foreach ($checkCodes as $checkCode) {
864
+				if (isset($this->tokenizer->ignoredLines[$line][$checkCode]) === true) {
865
+					$ignored = true;
866
+					break;
867
+				}
868
+			}
869
+
870
+			// If it is ignored, make sure it's not whitelisted.
871
+			if ($ignored === true
872
+				&& isset($this->tokenizer->ignoredLines[$line]['.except']) === true
873
+			) {
874
+				foreach ($checkCodes as $checkCode) {
875
+					if (isset($this->tokenizer->ignoredLines[$line]['.except'][$checkCode]) === true) {
876
+						$ignored = false;
877
+						break;
878
+					}
879
+				}
880
+			}
881
+
882
+			if ($ignored === true) {
883
+				return false;
884
+			}
885
+		}//end if
886
+
887
+		$includeAll = true;
888
+		if ($this->configCache['cache'] === false
889
+			|| $this->configCache['recordErrors'] === false
890
+		) {
891
+			$includeAll = false;
892
+		}
893
+
894
+		// Filter out any messages for sniffs that shouldn't have run
895
+		// due to the use of the --sniffs command line argument.
896
+		if ($includeAll === false
897
+			&& ((empty($this->configCache['sniffs']) === false
898
+			&& in_array(strtolower($listenerCode), $this->configCache['sniffs'], true) === false)
899
+			|| (empty($this->configCache['exclude']) === false
900
+			&& in_array(strtolower($listenerCode), $this->configCache['exclude'], true) === true))
901
+		) {
902
+			return false;
903
+		}
904
+
905
+		// If we know this sniff code is being ignored for this file, return early.
906
+		foreach ($checkCodes as $checkCode) {
907
+			if (isset($this->ignoredCodes[$checkCode]) === true) {
908
+				return false;
909
+			}
910
+		}
911
+
912
+		$oppositeType = 'warning';
913
+		if ($error === false) {
914
+			$oppositeType = 'error';
915
+		}
916
+
917
+		foreach ($checkCodes as $checkCode) {
918
+			// Make sure this message type has not been set to the opposite message type.
919
+			if (isset($this->ruleset->ruleset[$checkCode]['type']) === true
920
+				&& $this->ruleset->ruleset[$checkCode]['type'] === $oppositeType
921
+			) {
922
+				$error = !$error;
923
+				break;
924
+			}
925
+		}
926
+
927
+		if ($error === true) {
928
+			$configSeverity = $this->configCache['errorSeverity'];
929
+			$messageCount   = &$this->errorCount;
930
+			$messages       = &$this->errors;
931
+		} else {
932
+			$configSeverity = $this->configCache['warningSeverity'];
933
+			$messageCount   = &$this->warningCount;
934
+			$messages       = &$this->warnings;
935
+		}
936
+
937
+		if ($includeAll === false && $configSeverity === 0) {
938
+			// Don't bother doing any processing as these messages are just going to
939
+			// be hidden in the reports anyway.
940
+			return false;
941
+		}
942
+
943
+		if ($severity === 0) {
944
+			$severity = 5;
945
+		}
946
+
947
+		foreach ($checkCodes as $checkCode) {
948
+			// Make sure we are interested in this severity level.
949
+			if (isset($this->ruleset->ruleset[$checkCode]['severity']) === true) {
950
+				$severity = $this->ruleset->ruleset[$checkCode]['severity'];
951
+				break;
952
+			}
953
+		}
954
+
955
+		if ($includeAll === false && $configSeverity > $severity) {
956
+			return false;
957
+		}
958
+
959
+		// Make sure we are not ignoring this file.
960
+		$included = null;
961
+		foreach ($checkCodes as $checkCode) {
962
+			$patterns = null;
963
+
964
+			if (isset($this->configCache['includePatterns'][$checkCode]) === true) {
965
+				$patterns  = $this->configCache['includePatterns'][$checkCode];
966
+				$excluding = false;
967
+			} else if (isset($this->configCache['ignorePatterns'][$checkCode]) === true) {
968
+				$patterns  = $this->configCache['ignorePatterns'][$checkCode];
969
+				$excluding = true;
970
+			}
971
+
972
+			if ($patterns === null) {
973
+				continue;
974
+			}
975
+
976
+			foreach ($patterns as $pattern => $type) {
977
+				// While there is support for a type of each pattern
978
+				// (absolute or relative) we don't actually support it here.
979
+				$replacements = [
980
+					'\\,' => ',',
981
+					'*'   => '.*',
982
+				];
983
+
984
+				// We assume a / directory separator, as do the exclude rules
985
+				// most developers write, so we need a special case for any system
986
+				// that is different.
987
+				if (DIRECTORY_SEPARATOR === '\\') {
988
+					$replacements['/'] = '\\\\';
989
+				}
990
+
991
+				$pattern = '`'.strtr($pattern, $replacements).'`i';
992
+				$matched = preg_match($pattern, $this->path);
993
+
994
+				if ($matched === 0) {
995
+					if ($excluding === false && $included === null) {
996
+						// This file path is not being included.
997
+						$included = false;
998
+					}
999
+
1000
+					continue;
1001
+				}
1002
+
1003
+				if ($excluding === true) {
1004
+					// This file path is being excluded.
1005
+					$this->ignoredCodes[$checkCode] = true;
1006
+					return false;
1007
+				}
1008
+
1009
+				// This file path is being included.
1010
+				$included = true;
1011
+				break;
1012
+			}//end foreach
1013
+		}//end foreach
1014
+
1015
+		if ($included === false) {
1016
+			// There were include rules set, but this file
1017
+			// path didn't match any of them.
1018
+			return false;
1019
+		}
1020
+
1021
+		$messageCount++;
1022
+		if ($fixable === true) {
1023
+			$this->fixableCount++;
1024
+		}
1025
+
1026
+		if ($this->configCache['recordErrors'] === false
1027
+			&& $includeAll === false
1028
+		) {
1029
+			return true;
1030
+		}
1031
+
1032
+		// Work out the error message.
1033
+		if (isset($this->ruleset->ruleset[$sniffCode]['message']) === true) {
1034
+			$message = $this->ruleset->ruleset[$sniffCode]['message'];
1035
+		}
1036
+
1037
+		if (empty($data) === false) {
1038
+			$message = vsprintf($message, $data);
1039
+		}
1040
+
1041
+		if (isset($messages[$line]) === false) {
1042
+			$messages[$line] = [];
1043
+		}
1044
+
1045
+		if (isset($messages[$line][$column]) === false) {
1046
+			$messages[$line][$column] = [];
1047
+		}
1048
+
1049
+		$messages[$line][$column][] = [
1050
+			'message'  => $message,
1051
+			'source'   => $sniffCode,
1052
+			'listener' => $this->activeListener,
1053
+			'severity' => $severity,
1054
+			'fixable'  => $fixable,
1055
+		];
1056
+
1057
+		if (PHP_CODESNIFFER_VERBOSITY > 1
1058
+			&& $this->fixer->enabled === true
1059
+			&& $fixable === true
1060
+		) {
1061
+			@ob_end_clean();
1062
+			echo "\tE: [Line $line] $message ($sniffCode)".PHP_EOL;
1063
+			ob_start();
1064
+		}
1065
+
1066
+		return true;
1067
+
1068
+	}//end addMessage()
1069
+
1070
+
1071
+	/**
1072
+	 * Record a metric about the file being examined.
1073
+	 *
1074
+	 * @param int    $stackPtr The stack position where the metric was recorded.
1075
+	 * @param string $metric   The name of the metric being recorded.
1076
+	 * @param string $value    The value of the metric being recorded.
1077
+	 *
1078
+	 * @return boolean
1079
+	 */
1080
+	public function recordMetric($stackPtr, $metric, $value)
1081
+	{
1082
+		if (isset($this->metrics[$metric]) === false) {
1083
+			$this->metrics[$metric] = ['values' => [$value => 1]];
1084
+			$this->metricTokens[$metric][$stackPtr] = true;
1085
+		} else if (isset($this->metricTokens[$metric][$stackPtr]) === false) {
1086
+			$this->metricTokens[$metric][$stackPtr] = true;
1087
+			if (isset($this->metrics[$metric]['values'][$value]) === false) {
1088
+				$this->metrics[$metric]['values'][$value] = 1;
1089
+			} else {
1090
+				$this->metrics[$metric]['values'][$value]++;
1091
+			}
1092
+		}
1093
+
1094
+		return true;
1095
+
1096
+	}//end recordMetric()
1097
+
1098
+
1099
+	/**
1100
+	 * Returns the number of errors raised.
1101
+	 *
1102
+	 * @return int
1103
+	 */
1104
+	public function getErrorCount()
1105
+	{
1106
+		return $this->errorCount;
1107
+
1108
+	}//end getErrorCount()
1109
+
1110
+
1111
+	/**
1112
+	 * Returns the number of warnings raised.
1113
+	 *
1114
+	 * @return int
1115
+	 */
1116
+	public function getWarningCount()
1117
+	{
1118
+		return $this->warningCount;
1119
+
1120
+	}//end getWarningCount()
1121
+
1122
+
1123
+	/**
1124
+	 * Returns the number of fixable errors/warnings raised.
1125
+	 *
1126
+	 * @return int
1127
+	 */
1128
+	public function getFixableCount()
1129
+	{
1130
+		return $this->fixableCount;
1131
+
1132
+	}//end getFixableCount()
1133
+
1134
+
1135
+	/**
1136
+	 * Returns the number of fixed errors/warnings.
1137
+	 *
1138
+	 * @return int
1139
+	 */
1140
+	public function getFixedCount()
1141
+	{
1142
+		return $this->fixedCount;
1143
+
1144
+	}//end getFixedCount()
1145
+
1146
+
1147
+	/**
1148
+	 * Returns the list of ignored lines.
1149
+	 *
1150
+	 * @return array
1151
+	 */
1152
+	public function getIgnoredLines()
1153
+	{
1154
+		return $this->tokenizer->ignoredLines;
1155
+
1156
+	}//end getIgnoredLines()
1157
+
1158
+
1159
+	/**
1160
+	 * Returns the errors raised from processing this file.
1161
+	 *
1162
+	 * @return array
1163
+	 */
1164
+	public function getErrors()
1165
+	{
1166
+		return $this->errors;
1167
+
1168
+	}//end getErrors()
1169
+
1170
+
1171
+	/**
1172
+	 * Returns the warnings raised from processing this file.
1173
+	 *
1174
+	 * @return array
1175
+	 */
1176
+	public function getWarnings()
1177
+	{
1178
+		return $this->warnings;
1179
+
1180
+	}//end getWarnings()
1181
+
1182
+
1183
+	/**
1184
+	 * Returns the metrics found while processing this file.
1185
+	 *
1186
+	 * @return array
1187
+	 */
1188
+	public function getMetrics()
1189
+	{
1190
+		return $this->metrics;
1191
+
1192
+	}//end getMetrics()
1193
+
1194
+
1195
+	/**
1196
+	 * Returns the absolute filename of this file.
1197
+	 *
1198
+	 * @return string
1199
+	 */
1200
+	public function getFilename()
1201
+	{
1202
+		return $this->path;
1203
+
1204
+	}//end getFilename()
1205
+
1206
+
1207
+	/**
1208
+	 * Returns the declaration names for classes, interfaces, traits, and functions.
1209
+	 *
1210
+	 * @param int $stackPtr The position of the declaration token which
1211
+	 *                      declared the class, interface, trait, or function.
1212
+	 *
1213
+	 * @return string|null The name of the class, interface, trait, or function;
1214
+	 *                     or NULL if the function or class is anonymous.
1215
+	 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified token is not of type
1216
+	 *                                                      T_FUNCTION, T_CLASS, T_ANON_CLASS,
1217
+	 *                                                      T_CLOSURE, T_TRAIT, or T_INTERFACE.
1218
+	 */
1219
+	public function getDeclarationName($stackPtr)
1220
+	{
1221
+		$tokenCode = $this->tokens[$stackPtr]['code'];
1222
+
1223
+		if ($tokenCode === T_ANON_CLASS || $tokenCode === T_CLOSURE) {
1224
+			return null;
1225
+		}
1226
+
1227
+		if ($tokenCode !== T_FUNCTION
1228
+			&& $tokenCode !== T_CLASS
1229
+			&& $tokenCode !== T_INTERFACE
1230
+			&& $tokenCode !== T_TRAIT
1231
+		) {
1232
+			throw new RuntimeException('Token type "'.$this->tokens[$stackPtr]['type'].'" is not T_FUNCTION, T_CLASS, T_INTERFACE or T_TRAIT');
1233
+		}
1234
+
1235
+		if ($tokenCode === T_FUNCTION
1236
+			&& strtolower($this->tokens[$stackPtr]['content']) !== 'function'
1237
+		) {
1238
+			// This is a function declared without the "function" keyword.
1239
+			// So this token is the function name.
1240
+			return $this->tokens[$stackPtr]['content'];
1241
+		}
1242
+
1243
+		$content = null;
1244
+		for ($i = $stackPtr; $i < $this->numTokens; $i++) {
1245
+			if ($this->tokens[$i]['code'] === T_STRING) {
1246
+				$content = $this->tokens[$i]['content'];
1247
+				break;
1248
+			}
1249
+		}
1250
+
1251
+		return $content;
1252
+
1253
+	}//end getDeclarationName()
1254
+
1255
+
1256
+	/**
1257
+	 * Returns the method parameters for the specified function token.
1258
+	 *
1259
+	 * Each parameter is in the following format:
1260
+	 *
1261
+	 * <code>
1262
+	 *   0 => array(
1263
+	 *         'name'              => '$var',  // The variable name.
1264
+	 *         'token'             => integer, // The stack pointer to the variable name.
1265
+	 *         'content'           => string,  // The full content of the variable definition.
1266
+	 *         'pass_by_reference' => boolean, // Is the variable passed by reference?
1267
+	 *         'variable_length'   => boolean, // Is the param of variable length through use of `...` ?
1268
+	 *         'type_hint'         => string,  // The type hint for the variable.
1269
+	 *         'type_hint_token'   => integer, // The stack pointer to the type hint
1270
+	 *                                         // or false if there is no type hint.
1271
+	 *         'nullable_type'     => boolean, // Is the variable using a nullable type?
1272
+	 *        )
1273
+	 * </code>
1274
+	 *
1275
+	 * Parameters with default values have an additional array index of
1276
+	 * 'default' with the value of the default as a string.
1277
+	 *
1278
+	 * @param int $stackPtr The position in the stack of the function token
1279
+	 *                      to acquire the parameters for.
1280
+	 *
1281
+	 * @return array
1282
+	 * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the specified $stackPtr is not of
1283
+	 *                                                        type T_FUNCTION or T_CLOSURE.
1284
+	 */
1285
+	public function getMethodParameters($stackPtr)
1286
+	{
1287
+		if ($this->tokens[$stackPtr]['code'] !== T_FUNCTION
1288
+			&& $this->tokens[$stackPtr]['code'] !== T_CLOSURE
1289
+		) {
1290
+			throw new TokenizerException('$stackPtr must be of type T_FUNCTION or T_CLOSURE');
1291
+		}
1292
+
1293
+		$opener = $this->tokens[$stackPtr]['parenthesis_opener'];
1294
+		$closer = $this->tokens[$stackPtr]['parenthesis_closer'];
1295
+
1296
+		$vars            = [];
1297
+		$currVar         = null;
1298
+		$paramStart      = ($opener + 1);
1299
+		$defaultStart    = null;
1300
+		$paramCount      = 0;
1301
+		$passByReference = false;
1302
+		$variableLength  = false;
1303
+		$typeHint        = '';
1304
+		$typeHintToken   = false;
1305
+		$nullableType    = false;
1306
+
1307
+		for ($i = $paramStart; $i <= $closer; $i++) {
1308
+			// Check to see if this token has a parenthesis or bracket opener. If it does
1309
+			// it's likely to be an array which might have arguments in it. This
1310
+			// could cause problems in our parsing below, so lets just skip to the
1311
+			// end of it.
1312
+			if (isset($this->tokens[$i]['parenthesis_opener']) === true) {
1313
+				// Don't do this if it's the close parenthesis for the method.
1314
+				if ($i !== $this->tokens[$i]['parenthesis_closer']) {
1315
+					$i = ($this->tokens[$i]['parenthesis_closer'] + 1);
1316
+				}
1317
+			}
1318
+
1319
+			if (isset($this->tokens[$i]['bracket_opener']) === true) {
1320
+				// Don't do this if it's the close parenthesis for the method.
1321
+				if ($i !== $this->tokens[$i]['bracket_closer']) {
1322
+					$i = ($this->tokens[$i]['bracket_closer'] + 1);
1323
+				}
1324
+			}
1325
+
1326
+			switch ($this->tokens[$i]['code']) {
1327
+			case T_BITWISE_AND:
1328
+				if ($defaultStart === null) {
1329
+					$passByReference = true;
1330
+				}
1331
+				break;
1332
+			case T_VARIABLE:
1333
+				$currVar = $i;
1334
+				break;
1335
+			case T_ELLIPSIS:
1336
+				$variableLength = true;
1337
+				break;
1338
+			case T_CALLABLE:
1339
+				if ($typeHintToken === false) {
1340
+					$typeHintToken = $i;
1341
+				}
1342
+
1343
+				$typeHint .= $this->tokens[$i]['content'];
1344
+				break;
1345
+			case T_SELF:
1346
+			case T_PARENT:
1347
+			case T_STATIC:
1348
+				// Self and parent are valid, static invalid, but was probably intended as type hint.
1349
+				if (isset($defaultStart) === false) {
1350
+					if ($typeHintToken === false) {
1351
+						$typeHintToken = $i;
1352
+					}
1353
+
1354
+					$typeHint .= $this->tokens[$i]['content'];
1355
+				}
1356
+				break;
1357
+			case T_STRING:
1358
+				// This is a string, so it may be a type hint, but it could
1359
+				// also be a constant used as a default value.
1360
+				$prevComma = false;
1361
+				for ($t = $i; $t >= $opener; $t--) {
1362
+					if ($this->tokens[$t]['code'] === T_COMMA) {
1363
+						$prevComma = $t;
1364
+						break;
1365
+					}
1366
+				}
1367
+
1368
+				if ($prevComma !== false) {
1369
+					$nextEquals = false;
1370
+					for ($t = $prevComma; $t < $i; $t++) {
1371
+						if ($this->tokens[$t]['code'] === T_EQUAL) {
1372
+							$nextEquals = $t;
1373
+							break;
1374
+						}
1375
+					}
1376
+
1377
+					if ($nextEquals !== false) {
1378
+						break;
1379
+					}
1380
+				}
1381
+
1382
+				if ($defaultStart === null) {
1383
+					if ($typeHintToken === false) {
1384
+						$typeHintToken = $i;
1385
+					}
1386
+
1387
+					$typeHint .= $this->tokens[$i]['content'];
1388
+				}
1389
+				break;
1390
+			case T_NS_SEPARATOR:
1391
+				// Part of a type hint or default value.
1392
+				if ($defaultStart === null) {
1393
+					if ($typeHintToken === false) {
1394
+						$typeHintToken = $i;
1395
+					}
1396
+
1397
+					$typeHint .= $this->tokens[$i]['content'];
1398
+				}
1399
+				break;
1400
+			case T_NULLABLE:
1401
+				if ($defaultStart === null) {
1402
+					$nullableType = true;
1403
+					$typeHint    .= $this->tokens[$i]['content'];
1404
+				}
1405
+				break;
1406
+			case T_CLOSE_PARENTHESIS:
1407
+			case T_COMMA:
1408
+				// If it's null, then there must be no parameters for this
1409
+				// method.
1410
+				if ($currVar === null) {
1411
+					continue 2;
1412
+				}
1413
+
1414
+				$vars[$paramCount]            = [];
1415
+				$vars[$paramCount]['token']   = $currVar;
1416
+				$vars[$paramCount]['name']    = $this->tokens[$currVar]['content'];
1417
+				$vars[$paramCount]['content'] = trim($this->getTokensAsString($paramStart, ($i - $paramStart)));
1418
+
1419
+				if ($defaultStart !== null) {
1420
+					$vars[$paramCount]['default'] = trim($this->getTokensAsString($defaultStart, ($i - $defaultStart)));
1421
+				}
1422
+
1423
+				$vars[$paramCount]['pass_by_reference'] = $passByReference;
1424
+				$vars[$paramCount]['variable_length']   = $variableLength;
1425
+				$vars[$paramCount]['type_hint']         = $typeHint;
1426
+				$vars[$paramCount]['type_hint_token']   = $typeHintToken;
1427
+				$vars[$paramCount]['nullable_type']     = $nullableType;
1428
+
1429
+				// Reset the vars, as we are about to process the next parameter.
1430
+				$defaultStart    = null;
1431
+				$paramStart      = ($i + 1);
1432
+				$passByReference = false;
1433
+				$variableLength  = false;
1434
+				$typeHint        = '';
1435
+				$typeHintToken   = false;
1436
+				$nullableType    = false;
1437
+
1438
+				$paramCount++;
1439
+				break;
1440
+			case T_EQUAL:
1441
+				$defaultStart = ($i + 1);
1442
+				break;
1443
+			}//end switch
1444
+		}//end for
1445
+
1446
+		return $vars;
1447
+
1448
+	}//end getMethodParameters()
1449
+
1450
+
1451
+	/**
1452
+	 * Returns the visibility and implementation properties of a method.
1453
+	 *
1454
+	 * The format of the array is:
1455
+	 * <code>
1456
+	 *   array(
1457
+	 *    'scope'                => 'public', // public protected or protected
1458
+	 *    'scope_specified'      => true,     // true is scope keyword was found.
1459
+	 *    'return_type'          => '',       // the return type of the method.
1460
+	 *    'return_type_token'    => integer,  // The stack pointer to the start of the return type
1461
+	 *                                        // or false if there is no return type.
1462
+	 *    'nullable_return_type' => false,    // true if the return type is nullable.
1463
+	 *    'is_abstract'          => false,    // true if the abstract keyword was found.
1464
+	 *    'is_final'             => false,    // true if the final keyword was found.
1465
+	 *    'is_static'            => false,    // true if the static keyword was found.
1466
+	 *    'has_body'             => false,    // true if the method has a body
1467
+	 *   );
1468
+	 * </code>
1469
+	 *
1470
+	 * @param int $stackPtr The position in the stack of the function token to
1471
+	 *                      acquire the properties for.
1472
+	 *
1473
+	 * @return array
1474
+	 * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the specified position is not a
1475
+	 *                                                        T_FUNCTION token.
1476
+	 */
1477
+	public function getMethodProperties($stackPtr)
1478
+	{
1479
+		if ($this->tokens[$stackPtr]['code'] !== T_FUNCTION
1480
+			&& $this->tokens[$stackPtr]['code'] !== T_CLOSURE
1481
+		) {
1482
+			throw new TokenizerException('$stackPtr must be of type T_FUNCTION or T_CLOSURE');
1483
+		}
1484
+
1485
+		if ($this->tokens[$stackPtr]['code'] === T_FUNCTION) {
1486
+			$valid = [
1487
+				T_PUBLIC      => T_PUBLIC,
1488
+				T_PRIVATE     => T_PRIVATE,
1489
+				T_PROTECTED   => T_PROTECTED,
1490
+				T_STATIC      => T_STATIC,
1491
+				T_FINAL       => T_FINAL,
1492
+				T_ABSTRACT    => T_ABSTRACT,
1493
+				T_WHITESPACE  => T_WHITESPACE,
1494
+				T_COMMENT     => T_COMMENT,
1495
+				T_DOC_COMMENT => T_DOC_COMMENT,
1496
+			];
1497
+		} else {
1498
+			$valid = [
1499
+				T_STATIC      => T_STATIC,
1500
+				T_WHITESPACE  => T_WHITESPACE,
1501
+				T_COMMENT     => T_COMMENT,
1502
+				T_DOC_COMMENT => T_DOC_COMMENT,
1503
+			];
1504
+		}
1505
+
1506
+		$scope          = 'public';
1507
+		$scopeSpecified = false;
1508
+		$isAbstract     = false;
1509
+		$isFinal        = false;
1510
+		$isStatic       = false;
1511
+
1512
+		for ($i = ($stackPtr - 1); $i > 0; $i--) {
1513
+			if (isset($valid[$this->tokens[$i]['code']]) === false) {
1514
+				break;
1515
+			}
1516
+
1517
+			switch ($this->tokens[$i]['code']) {
1518
+			case T_PUBLIC:
1519
+				$scope          = 'public';
1520
+				$scopeSpecified = true;
1521
+				break;
1522
+			case T_PRIVATE:
1523
+				$scope          = 'private';
1524
+				$scopeSpecified = true;
1525
+				break;
1526
+			case T_PROTECTED:
1527
+				$scope          = 'protected';
1528
+				$scopeSpecified = true;
1529
+				break;
1530
+			case T_ABSTRACT:
1531
+				$isAbstract = true;
1532
+				break;
1533
+			case T_FINAL:
1534
+				$isFinal = true;
1535
+				break;
1536
+			case T_STATIC:
1537
+				$isStatic = true;
1538
+				break;
1539
+			}//end switch
1540
+		}//end for
1541
+
1542
+		$returnType         = '';
1543
+		$returnTypeToken    = false;
1544
+		$nullableReturnType = false;
1545
+		$hasBody            = true;
1546
+
1547
+		if (isset($this->tokens[$stackPtr]['parenthesis_closer']) === true) {
1548
+			$scopeOpener = null;
1549
+			if (isset($this->tokens[$stackPtr]['scope_opener']) === true) {
1550
+				$scopeOpener = $this->tokens[$stackPtr]['scope_opener'];
1551
+			}
1552
+
1553
+			$valid = [
1554
+				T_STRING       => T_STRING,
1555
+				T_CALLABLE     => T_CALLABLE,
1556
+				T_SELF         => T_SELF,
1557
+				T_PARENT       => T_PARENT,
1558
+				T_NS_SEPARATOR => T_NS_SEPARATOR,
1559
+			];
1560
+
1561
+			for ($i = $this->tokens[$stackPtr]['parenthesis_closer']; $i < $this->numTokens; $i++) {
1562
+				if (($scopeOpener === null && $this->tokens[$i]['code'] === T_SEMICOLON)
1563
+					|| ($scopeOpener !== null && $i === $scopeOpener)
1564
+				) {
1565
+					// End of function definition.
1566
+					break;
1567
+				}
1568
+
1569
+				if ($this->tokens[$i]['code'] === T_NULLABLE) {
1570
+					$nullableReturnType = true;
1571
+				}
1572
+
1573
+				if (isset($valid[$this->tokens[$i]['code']]) === true) {
1574
+					if ($returnTypeToken === false) {
1575
+						$returnTypeToken = $i;
1576
+					}
1577
+
1578
+					$returnType .= $this->tokens[$i]['content'];
1579
+				}
1580
+			}
1581
+
1582
+			$end     = $this->findNext([T_OPEN_CURLY_BRACKET, T_SEMICOLON], $this->tokens[$stackPtr]['parenthesis_closer']);
1583
+			$hasBody = $this->tokens[$end]['code'] === T_OPEN_CURLY_BRACKET;
1584
+		}//end if
1585
+
1586
+		if ($returnType !== '' && $nullableReturnType === true) {
1587
+			$returnType = '?'.$returnType;
1588
+		}
1589
+
1590
+		return [
1591
+			'scope'                => $scope,
1592
+			'scope_specified'      => $scopeSpecified,
1593
+			'return_type'          => $returnType,
1594
+			'return_type_token'    => $returnTypeToken,
1595
+			'nullable_return_type' => $nullableReturnType,
1596
+			'is_abstract'          => $isAbstract,
1597
+			'is_final'             => $isFinal,
1598
+			'is_static'            => $isStatic,
1599
+			'has_body'             => $hasBody,
1600
+		];
1601
+
1602
+	}//end getMethodProperties()
1603
+
1604
+
1605
+	/**
1606
+	 * Returns the visibility and implementation properties of the class member
1607
+	 * variable found at the specified position in the stack.
1608
+	 *
1609
+	 * The format of the array is:
1610
+	 *
1611
+	 * <code>
1612
+	 *   array(
1613
+	 *    'scope'           => 'public', // public protected or protected.
1614
+	 *    'scope_specified' => false,    // true if the scope was explicitly specified.
1615
+	 *    'is_static'       => false,    // true if the static keyword was found.
1616
+	 *   );
1617
+	 * </code>
1618
+	 *
1619
+	 * @param int $stackPtr The position in the stack of the T_VARIABLE token to
1620
+	 *                      acquire the properties for.
1621
+	 *
1622
+	 * @return array
1623
+	 * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the specified position is not a
1624
+	 *                                                        T_VARIABLE token, or if the position is not
1625
+	 *                                                        a class member variable.
1626
+	 */
1627
+	public function getMemberProperties($stackPtr)
1628
+	{
1629
+		if ($this->tokens[$stackPtr]['code'] !== T_VARIABLE) {
1630
+			throw new TokenizerException('$stackPtr must be of type T_VARIABLE');
1631
+		}
1632
+
1633
+		$conditions = array_keys($this->tokens[$stackPtr]['conditions']);
1634
+		$ptr        = array_pop($conditions);
1635
+		if (isset($this->tokens[$ptr]) === false
1636
+			|| ($this->tokens[$ptr]['code'] !== T_CLASS
1637
+			&& $this->tokens[$ptr]['code'] !== T_ANON_CLASS
1638
+			&& $this->tokens[$ptr]['code'] !== T_TRAIT)
1639
+		) {
1640
+			if (isset($this->tokens[$ptr]) === true
1641
+				&& $this->tokens[$ptr]['code'] === T_INTERFACE
1642
+			) {
1643
+				// T_VARIABLEs in interfaces can actually be method arguments
1644
+				// but they wont be seen as being inside the method because there
1645
+				// are no scope openers and closers for abstract methods. If it is in
1646
+				// parentheses, we can be pretty sure it is a method argument.
1647
+				if (isset($this->tokens[$stackPtr]['nested_parenthesis']) === false
1648
+					|| empty($this->tokens[$stackPtr]['nested_parenthesis']) === true
1649
+				) {
1650
+					$error = 'Possible parse error: interfaces may not include member vars';
1651
+					$this->addWarning($error, $stackPtr, 'Internal.ParseError.InterfaceHasMemberVar');
1652
+					return [];
1653
+				}
1654
+			} else {
1655
+				throw new TokenizerException('$stackPtr is not a class member var');
1656
+			}
1657
+		}
1658
+
1659
+		// Make sure it's not a method parameter.
1660
+		if (empty($this->tokens[$stackPtr]['nested_parenthesis']) === false) {
1661
+			$parenthesis = array_keys($this->tokens[$stackPtr]['nested_parenthesis']);
1662
+			$deepestOpen = array_pop($parenthesis);
1663
+			if ($deepestOpen > $ptr
1664
+				&& isset($this->tokens[$deepestOpen]['parenthesis_owner']) === true
1665
+				&& $this->tokens[$this->tokens[$deepestOpen]['parenthesis_owner']]['code'] === T_FUNCTION
1666
+			) {
1667
+				throw new TokenizerException('$stackPtr is not a class member var');
1668
+			}
1669
+		}
1670
+
1671
+		$valid = [
1672
+			T_PUBLIC    => T_PUBLIC,
1673
+			T_PRIVATE   => T_PRIVATE,
1674
+			T_PROTECTED => T_PROTECTED,
1675
+			T_STATIC    => T_STATIC,
1676
+			T_VAR       => T_VAR,
1677
+		];
1678
+
1679
+		$valid += Util\Tokens::$emptyTokens;
1680
+
1681
+		$scope          = 'public';
1682
+		$scopeSpecified = false;
1683
+		$isStatic       = false;
1684
+
1685
+		$startOfStatement = $this->findPrevious(
1686
+			[
1687
+				T_SEMICOLON,
1688
+				T_OPEN_CURLY_BRACKET,
1689
+				T_CLOSE_CURLY_BRACKET,
1690
+			],
1691
+			($stackPtr - 1)
1692
+		);
1693
+
1694
+		for ($i = ($startOfStatement + 1); $i < $stackPtr; $i++) {
1695
+			if (isset($valid[$this->tokens[$i]['code']]) === false) {
1696
+				break;
1697
+			}
1698
+
1699
+			switch ($this->tokens[$i]['code']) {
1700
+			case T_PUBLIC:
1701
+				$scope          = 'public';
1702
+				$scopeSpecified = true;
1703
+				break;
1704
+			case T_PRIVATE:
1705
+				$scope          = 'private';
1706
+				$scopeSpecified = true;
1707
+				break;
1708
+			case T_PROTECTED:
1709
+				$scope          = 'protected';
1710
+				$scopeSpecified = true;
1711
+				break;
1712
+			case T_STATIC:
1713
+				$isStatic = true;
1714
+				break;
1715
+			}
1716
+		}//end for
1717
+
1718
+		return [
1719
+			'scope'           => $scope,
1720
+			'scope_specified' => $scopeSpecified,
1721
+			'is_static'       => $isStatic,
1722
+		];
1723
+
1724
+	}//end getMemberProperties()
1725
+
1726
+
1727
+	/**
1728
+	 * Returns the visibility and implementation properties of a class.
1729
+	 *
1730
+	 * The format of the array is:
1731
+	 * <code>
1732
+	 *   array(
1733
+	 *    'is_abstract' => false, // true if the abstract keyword was found.
1734
+	 *    'is_final'    => false, // true if the final keyword was found.
1735
+	 *   );
1736
+	 * </code>
1737
+	 *
1738
+	 * @param int $stackPtr The position in the stack of the T_CLASS token to
1739
+	 *                      acquire the properties for.
1740
+	 *
1741
+	 * @return array
1742
+	 * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the specified position is not a
1743
+	 *                                                        T_CLASS token.
1744
+	 */
1745
+	public function getClassProperties($stackPtr)
1746
+	{
1747
+		if ($this->tokens[$stackPtr]['code'] !== T_CLASS) {
1748
+			throw new TokenizerException('$stackPtr must be of type T_CLASS');
1749
+		}
1750
+
1751
+		$valid = [
1752
+			T_FINAL       => T_FINAL,
1753
+			T_ABSTRACT    => T_ABSTRACT,
1754
+			T_WHITESPACE  => T_WHITESPACE,
1755
+			T_COMMENT     => T_COMMENT,
1756
+			T_DOC_COMMENT => T_DOC_COMMENT,
1757
+		];
1758
+
1759
+		$isAbstract = false;
1760
+		$isFinal    = false;
1761
+
1762
+		for ($i = ($stackPtr - 1); $i > 0; $i--) {
1763
+			if (isset($valid[$this->tokens[$i]['code']]) === false) {
1764
+				break;
1765
+			}
1766
+
1767
+			switch ($this->tokens[$i]['code']) {
1768
+			case T_ABSTRACT:
1769
+				$isAbstract = true;
1770
+				break;
1771
+
1772
+			case T_FINAL:
1773
+				$isFinal = true;
1774
+				break;
1775
+			}
1776
+		}//end for
1777
+
1778
+		return [
1779
+			'is_abstract' => $isAbstract,
1780
+			'is_final'    => $isFinal,
1781
+		];
1782
+
1783
+	}//end getClassProperties()
1784
+
1785
+
1786
+	/**
1787
+	 * Determine if the passed token is a reference operator.
1788
+	 *
1789
+	 * Returns true if the specified token position represents a reference.
1790
+	 * Returns false if the token represents a bitwise operator.
1791
+	 *
1792
+	 * @param int $stackPtr The position of the T_BITWISE_AND token.
1793
+	 *
1794
+	 * @return boolean
1795
+	 */
1796
+	public function isReference($stackPtr)
1797
+	{
1798
+		if ($this->tokens[$stackPtr]['code'] !== T_BITWISE_AND) {
1799
+			return false;
1800
+		}
1801
+
1802
+		$tokenBefore = $this->findPrevious(
1803
+			Util\Tokens::$emptyTokens,
1804
+			($stackPtr - 1),
1805
+			null,
1806
+			true
1807
+		);
1808
+
1809
+		if ($this->tokens[$tokenBefore]['code'] === T_FUNCTION) {
1810
+			// Function returns a reference.
1811
+			return true;
1812
+		}
1813
+
1814
+		if ($this->tokens[$tokenBefore]['code'] === T_DOUBLE_ARROW) {
1815
+			// Inside a foreach loop or array assignment, this is a reference.
1816
+			return true;
1817
+		}
1818
+
1819
+		if ($this->tokens[$tokenBefore]['code'] === T_AS) {
1820
+			// Inside a foreach loop, this is a reference.
1821
+			return true;
1822
+		}
1823
+
1824
+		if (isset(Util\Tokens::$assignmentTokens[$this->tokens[$tokenBefore]['code']]) === true) {
1825
+			// This is directly after an assignment. It's a reference. Even if
1826
+			// it is part of an operation, the other tests will handle it.
1827
+			return true;
1828
+		}
1829
+
1830
+		$tokenAfter = $this->findNext(
1831
+			Util\Tokens::$emptyTokens,
1832
+			($stackPtr + 1),
1833
+			null,
1834
+			true
1835
+		);
1836
+
1837
+		if ($this->tokens[$tokenAfter]['code'] === T_NEW) {
1838
+			return true;
1839
+		}
1840
+
1841
+		if (isset($this->tokens[$stackPtr]['nested_parenthesis']) === true) {
1842
+			$brackets    = $this->tokens[$stackPtr]['nested_parenthesis'];
1843
+			$lastBracket = array_pop($brackets);
1844
+			if (isset($this->tokens[$lastBracket]['parenthesis_owner']) === true) {
1845
+				$owner = $this->tokens[$this->tokens[$lastBracket]['parenthesis_owner']];
1846
+				if ($owner['code'] === T_FUNCTION
1847
+					|| $owner['code'] === T_CLOSURE
1848
+				) {
1849
+					$params = $this->getMethodParameters($this->tokens[$lastBracket]['parenthesis_owner']);
1850
+					foreach ($params as $param) {
1851
+						$varToken = $tokenAfter;
1852
+						if ($param['variable_length'] === true) {
1853
+							$varToken = $this->findNext(
1854
+								(Util\Tokens::$emptyTokens + [T_ELLIPSIS]),
1855
+								($stackPtr + 1),
1856
+								null,
1857
+								true
1858
+							);
1859
+						}
1860
+
1861
+						if ($param['token'] === $varToken
1862
+							&& $param['pass_by_reference'] === true
1863
+						) {
1864
+							// Function parameter declared to be passed by reference.
1865
+							return true;
1866
+						}
1867
+					}
1868
+				}//end if
1869
+			} else {
1870
+				$prev = false;
1871
+				for ($t = ($this->tokens[$lastBracket]['parenthesis_opener'] - 1); $t >= 0; $t--) {
1872
+					if ($this->tokens[$t]['code'] !== T_WHITESPACE) {
1873
+						$prev = $t;
1874
+						break;
1875
+					}
1876
+				}
1877
+
1878
+				if ($prev !== false && $this->tokens[$prev]['code'] === T_USE) {
1879
+					// Closure use by reference.
1880
+					return true;
1881
+				}
1882
+			}//end if
1883
+		}//end if
1884
+
1885
+		// Pass by reference in function calls and assign by reference in arrays.
1886
+		if ($this->tokens[$tokenBefore]['code'] === T_OPEN_PARENTHESIS
1887
+			|| $this->tokens[$tokenBefore]['code'] === T_COMMA
1888
+			|| $this->tokens[$tokenBefore]['code'] === T_OPEN_SHORT_ARRAY
1889
+		) {
1890
+			if ($this->tokens[$tokenAfter]['code'] === T_VARIABLE) {
1891
+				return true;
1892
+			} else {
1893
+				$skip   = Util\Tokens::$emptyTokens;
1894
+				$skip[] = T_NS_SEPARATOR;
1895
+				$skip[] = T_SELF;
1896
+				$skip[] = T_PARENT;
1897
+				$skip[] = T_STATIC;
1898
+				$skip[] = T_STRING;
1899
+				$skip[] = T_NAMESPACE;
1900
+				$skip[] = T_DOUBLE_COLON;
1901
+
1902
+				$nextSignificantAfter = $this->findNext(
1903
+					$skip,
1904
+					($stackPtr + 1),
1905
+					null,
1906
+					true
1907
+				);
1908
+				if ($this->tokens[$nextSignificantAfter]['code'] === T_VARIABLE) {
1909
+					return true;
1910
+				}
1911
+			}//end if
1912
+		}//end if
1913
+
1914
+		return false;
1915
+
1916
+	}//end isReference()
1917
+
1918
+
1919
+	/**
1920
+	 * Returns the content of the tokens from the specified start position in
1921
+	 * the token stack for the specified length.
1922
+	 *
1923
+	 * @param int  $start       The position to start from in the token stack.
1924
+	 * @param int  $length      The length of tokens to traverse from the start pos.
1925
+	 * @param bool $origContent Whether the original content or the tab replaced
1926
+	 *                          content should be used.
1927
+	 *
1928
+	 * @return string The token contents.
1929
+	 */
1930
+	public function getTokensAsString($start, $length, $origContent=false)
1931
+	{
1932
+		if (is_int($start) === false || isset($this->tokens[$start]) === false) {
1933
+			throw new RuntimeException('The $start position for getTokensAsString() must exist in the token stack');
1934
+		}
1935
+
1936
+		if (is_int($length) === false || $length <= 0) {
1937
+			return '';
1938
+		}
1939
+
1940
+		$str = '';
1941
+		$end = ($start + $length);
1942
+		if ($end > $this->numTokens) {
1943
+			$end = $this->numTokens;
1944
+		}
1945
+
1946
+		for ($i = $start; $i < $end; $i++) {
1947
+			// If tabs are being converted to spaces by the tokeniser, the
1948
+			// original content should be used instead of the converted content.
1949
+			if ($origContent === true && isset($this->tokens[$i]['orig_content']) === true) {
1950
+				$str .= $this->tokens[$i]['orig_content'];
1951
+			} else {
1952
+				$str .= $this->tokens[$i]['content'];
1953
+			}
1954
+		}
1955
+
1956
+		return $str;
1957
+
1958
+	}//end getTokensAsString()
1959
+
1960
+
1961
+	/**
1962
+	 * Returns the position of the previous specified token(s).
1963
+	 *
1964
+	 * If a value is specified, the previous token of the specified type(s)
1965
+	 * containing the specified value will be returned.
1966
+	 *
1967
+	 * Returns false if no token can be found.
1968
+	 *
1969
+	 * @param int|string|array $types   The type(s) of tokens to search for.
1970
+	 * @param int              $start   The position to start searching from in the
1971
+	 *                                  token stack.
1972
+	 * @param int              $end     The end position to fail if no token is found.
1973
+	 *                                  if not specified or null, end will default to
1974
+	 *                                  the start of the token stack.
1975
+	 * @param bool             $exclude If true, find the previous token that is NOT of
1976
+	 *                                  the types specified in $types.
1977
+	 * @param string           $value   The value that the token(s) must be equal to.
1978
+	 *                                  If value is omitted, tokens with any value will
1979
+	 *                                  be returned.
1980
+	 * @param bool             $local   If true, tokens outside the current statement
1981
+	 *                                  will not be checked. IE. checking will stop
1982
+	 *                                  at the previous semi-colon found.
1983
+	 *
1984
+	 * @return int|bool
1985
+	 * @see    findNext()
1986
+	 */
1987
+	public function findPrevious(
1988
+		$types,
1989
+		$start,
1990
+		$end=null,
1991
+		$exclude=false,
1992
+		$value=null,
1993
+		$local=false
1994
+	) {
1995
+		$types = (array) $types;
1996
+
1997
+		if ($end === null) {
1998
+			$end = 0;
1999
+		}
2000
+
2001
+		for ($i = $start; $i >= $end; $i--) {
2002
+			$found = (bool) $exclude;
2003
+			foreach ($types as $type) {
2004
+				if ($this->tokens[$i]['code'] === $type) {
2005
+					$found = !$exclude;
2006
+					break;
2007
+				}
2008
+			}
2009
+
2010
+			if ($found === true) {
2011
+				if ($value === null) {
2012
+					return $i;
2013
+				} else if ($this->tokens[$i]['content'] === $value) {
2014
+					return $i;
2015
+				}
2016
+			}
2017
+
2018
+			if ($local === true) {
2019
+				if (isset($this->tokens[$i]['scope_opener']) === true
2020
+					&& $i === $this->tokens[$i]['scope_closer']
2021
+				) {
2022
+					$i = $this->tokens[$i]['scope_opener'];
2023
+				} else if (isset($this->tokens[$i]['bracket_opener']) === true
2024
+					&& $i === $this->tokens[$i]['bracket_closer']
2025
+				) {
2026
+					$i = $this->tokens[$i]['bracket_opener'];
2027
+				} else if (isset($this->tokens[$i]['parenthesis_opener']) === true
2028
+					&& $i === $this->tokens[$i]['parenthesis_closer']
2029
+				) {
2030
+					$i = $this->tokens[$i]['parenthesis_opener'];
2031
+				} else if ($this->tokens[$i]['code'] === T_SEMICOLON) {
2032
+					break;
2033
+				}
2034
+			}
2035
+		}//end for
2036
+
2037
+		return false;
2038
+
2039
+	}//end findPrevious()
2040
+
2041
+
2042
+	/**
2043
+	 * Returns the position of the next specified token(s).
2044
+	 *
2045
+	 * If a value is specified, the next token of the specified type(s)
2046
+	 * containing the specified value will be returned.
2047
+	 *
2048
+	 * Returns false if no token can be found.
2049
+	 *
2050
+	 * @param int|string|array $types   The type(s) of tokens to search for.
2051
+	 * @param int              $start   The position to start searching from in the
2052
+	 *                                  token stack.
2053
+	 * @param int              $end     The end position to fail if no token is found.
2054
+	 *                                  if not specified or null, end will default to
2055
+	 *                                  the end of the token stack.
2056
+	 * @param bool             $exclude If true, find the next token that is NOT of
2057
+	 *                                  a type specified in $types.
2058
+	 * @param string           $value   The value that the token(s) must be equal to.
2059
+	 *                                  If value is omitted, tokens with any value will
2060
+	 *                                  be returned.
2061
+	 * @param bool             $local   If true, tokens outside the current statement
2062
+	 *                                  will not be checked. i.e., checking will stop
2063
+	 *                                  at the next semi-colon found.
2064
+	 *
2065
+	 * @return int|bool
2066
+	 * @see    findPrevious()
2067
+	 */
2068
+	public function findNext(
2069
+		$types,
2070
+		$start,
2071
+		$end=null,
2072
+		$exclude=false,
2073
+		$value=null,
2074
+		$local=false
2075
+	) {
2076
+		$types = (array) $types;
2077
+
2078
+		if ($end === null || $end > $this->numTokens) {
2079
+			$end = $this->numTokens;
2080
+		}
2081
+
2082
+		for ($i = $start; $i < $end; $i++) {
2083
+			$found = (bool) $exclude;
2084
+			foreach ($types as $type) {
2085
+				if ($this->tokens[$i]['code'] === $type) {
2086
+					$found = !$exclude;
2087
+					break;
2088
+				}
2089
+			}
2090
+
2091
+			if ($found === true) {
2092
+				if ($value === null) {
2093
+					return $i;
2094
+				} else if ($this->tokens[$i]['content'] === $value) {
2095
+					return $i;
2096
+				}
2097
+			}
2098
+
2099
+			if ($local === true && $this->tokens[$i]['code'] === T_SEMICOLON) {
2100
+				break;
2101
+			}
2102
+		}//end for
2103
+
2104
+		return false;
2105
+
2106
+	}//end findNext()
2107
+
2108
+
2109
+	/**
2110
+	 * Returns the position of the first non-whitespace token in a statement.
2111
+	 *
2112
+	 * @param int       $start  The position to start searching from in the token stack.
2113
+	 * @param int|array $ignore Token types that should not be considered stop points.
2114
+	 *
2115
+	 * @return int
2116
+	 */
2117
+	public function findStartOfStatement($start, $ignore=null)
2118
+	{
2119
+		$endTokens = Util\Tokens::$blockOpeners;
2120
+
2121
+		$endTokens[T_COLON]            = true;
2122
+		$endTokens[T_COMMA]            = true;
2123
+		$endTokens[T_DOUBLE_ARROW]     = true;
2124
+		$endTokens[T_SEMICOLON]        = true;
2125
+		$endTokens[T_OPEN_TAG]         = true;
2126
+		$endTokens[T_CLOSE_TAG]        = true;
2127
+		$endTokens[T_OPEN_SHORT_ARRAY] = true;
2128
+
2129
+		if ($ignore !== null) {
2130
+			$ignore = (array) $ignore;
2131
+			foreach ($ignore as $code) {
2132
+				unset($endTokens[$code]);
2133
+			}
2134
+		}
2135
+
2136
+		$lastNotEmpty = $start;
2137
+
2138
+		for ($i = $start; $i >= 0; $i--) {
2139
+			if (isset($endTokens[$this->tokens[$i]['code']]) === true) {
2140
+				// Found the end of the previous statement.
2141
+				return $lastNotEmpty;
2142
+			}
2143
+
2144
+			if (isset($this->tokens[$i]['scope_opener']) === true
2145
+				&& $i === $this->tokens[$i]['scope_closer']
2146
+			) {
2147
+				// Found the end of the previous scope block.
2148
+				return $lastNotEmpty;
2149
+			}
2150
+
2151
+			// Skip nested statements.
2152
+			if (isset($this->tokens[$i]['bracket_opener']) === true
2153
+				&& $i === $this->tokens[$i]['bracket_closer']
2154
+			) {
2155
+				$i = $this->tokens[$i]['bracket_opener'];
2156
+			} else if (isset($this->tokens[$i]['parenthesis_opener']) === true
2157
+				&& $i === $this->tokens[$i]['parenthesis_closer']
2158
+			) {
2159
+				$i = $this->tokens[$i]['parenthesis_opener'];
2160
+			}
2161
+
2162
+			if (isset(Util\Tokens::$emptyTokens[$this->tokens[$i]['code']]) === false) {
2163
+				$lastNotEmpty = $i;
2164
+			}
2165
+		}//end for
2166
+
2167
+		return 0;
2168
+
2169
+	}//end findStartOfStatement()
2170
+
2171
+
2172
+	/**
2173
+	 * Returns the position of the last non-whitespace token in a statement.
2174
+	 *
2175
+	 * @param int       $start  The position to start searching from in the token stack.
2176
+	 * @param int|array $ignore Token types that should not be considered stop points.
2177
+	 *
2178
+	 * @return int
2179
+	 */
2180
+	public function findEndOfStatement($start, $ignore=null)
2181
+	{
2182
+		$endTokens = [
2183
+			T_COLON                => true,
2184
+			T_COMMA                => true,
2185
+			T_DOUBLE_ARROW         => true,
2186
+			T_SEMICOLON            => true,
2187
+			T_CLOSE_PARENTHESIS    => true,
2188
+			T_CLOSE_SQUARE_BRACKET => true,
2189
+			T_CLOSE_CURLY_BRACKET  => true,
2190
+			T_CLOSE_SHORT_ARRAY    => true,
2191
+			T_OPEN_TAG             => true,
2192
+			T_CLOSE_TAG            => true,
2193
+		];
2194
+
2195
+		if ($ignore !== null) {
2196
+			$ignore = (array) $ignore;
2197
+			foreach ($ignore as $code) {
2198
+				unset($endTokens[$code]);
2199
+			}
2200
+		}
2201
+
2202
+		$lastNotEmpty = $start;
2203
+
2204
+		for ($i = $start; $i < $this->numTokens; $i++) {
2205
+			if ($i !== $start && isset($endTokens[$this->tokens[$i]['code']]) === true) {
2206
+				// Found the end of the statement.
2207
+				if ($this->tokens[$i]['code'] === T_CLOSE_PARENTHESIS
2208
+					|| $this->tokens[$i]['code'] === T_CLOSE_SQUARE_BRACKET
2209
+					|| $this->tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET
2210
+					|| $this->tokens[$i]['code'] === T_CLOSE_SHORT_ARRAY
2211
+					|| $this->tokens[$i]['code'] === T_OPEN_TAG
2212
+					|| $this->tokens[$i]['code'] === T_CLOSE_TAG
2213
+				) {
2214
+					return $lastNotEmpty;
2215
+				}
2216
+
2217
+				return $i;
2218
+			}
2219
+
2220
+			// Skip nested statements.
2221
+			if (isset($this->tokens[$i]['scope_closer']) === true
2222
+				&& ($i === $this->tokens[$i]['scope_opener']
2223
+				|| $i === $this->tokens[$i]['scope_condition'])
2224
+			) {
2225
+				if ($i === $start && isset(Util\Tokens::$scopeOpeners[$this->tokens[$i]['code']]) === true) {
2226
+					return $this->tokens[$i]['scope_closer'];
2227
+				}
2228
+
2229
+				$i = $this->tokens[$i]['scope_closer'];
2230
+			} else if (isset($this->tokens[$i]['bracket_closer']) === true
2231
+				&& $i === $this->tokens[$i]['bracket_opener']
2232
+			) {
2233
+				$i = $this->tokens[$i]['bracket_closer'];
2234
+			} else if (isset($this->tokens[$i]['parenthesis_closer']) === true
2235
+				&& $i === $this->tokens[$i]['parenthesis_opener']
2236
+			) {
2237
+				$i = $this->tokens[$i]['parenthesis_closer'];
2238
+			}
2239
+
2240
+			if (isset(Util\Tokens::$emptyTokens[$this->tokens[$i]['code']]) === false) {
2241
+				$lastNotEmpty = $i;
2242
+			}
2243
+		}//end for
2244
+
2245
+		return ($this->numTokens - 1);
2246
+
2247
+	}//end findEndOfStatement()
2248
+
2249
+
2250
+	/**
2251
+	 * Returns the position of the first token on a line, matching given type.
2252
+	 *
2253
+	 * Returns false if no token can be found.
2254
+	 *
2255
+	 * @param int|string|array $types   The type(s) of tokens to search for.
2256
+	 * @param int              $start   The position to start searching from in the
2257
+	 *                                  token stack. The first token matching on
2258
+	 *                                  this line before this token will be returned.
2259
+	 * @param bool             $exclude If true, find the token that is NOT of
2260
+	 *                                  the types specified in $types.
2261
+	 * @param string           $value   The value that the token must be equal to.
2262
+	 *                                  If value is omitted, tokens with any value will
2263
+	 *                                  be returned.
2264
+	 *
2265
+	 * @return int | bool
2266
+	 */
2267
+	public function findFirstOnLine($types, $start, $exclude=false, $value=null)
2268
+	{
2269
+		if (is_array($types) === false) {
2270
+			$types = [$types];
2271
+		}
2272
+
2273
+		$foundToken = false;
2274
+
2275
+		for ($i = $start; $i >= 0; $i--) {
2276
+			if ($this->tokens[$i]['line'] < $this->tokens[$start]['line']) {
2277
+				break;
2278
+			}
2279
+
2280
+			$found = $exclude;
2281
+			foreach ($types as $type) {
2282
+				if ($exclude === false) {
2283
+					if ($this->tokens[$i]['code'] === $type) {
2284
+						$found = true;
2285
+						break;
2286
+					}
2287
+				} else {
2288
+					if ($this->tokens[$i]['code'] === $type) {
2289
+						$found = false;
2290
+						break;
2291
+					}
2292
+				}
2293
+			}
2294
+
2295
+			if ($found === true) {
2296
+				if ($value === null) {
2297
+					$foundToken = $i;
2298
+				} else if ($this->tokens[$i]['content'] === $value) {
2299
+					$foundToken = $i;
2300
+				}
2301
+			}
2302
+		}//end for
2303
+
2304
+		return $foundToken;
2305
+
2306
+	}//end findFirstOnLine()
2307
+
2308
+
2309
+	/**
2310
+	 * Determine if the passed token has a condition of one of the passed types.
2311
+	 *
2312
+	 * @param int              $stackPtr The position of the token we are checking.
2313
+	 * @param int|string|array $types    The type(s) of tokens to search for.
2314
+	 *
2315
+	 * @return boolean
2316
+	 */
2317
+	public function hasCondition($stackPtr, $types)
2318
+	{
2319
+		// Check for the existence of the token.
2320
+		if (isset($this->tokens[$stackPtr]) === false) {
2321
+			return false;
2322
+		}
2323
+
2324
+		// Make sure the token has conditions.
2325
+		if (isset($this->tokens[$stackPtr]['conditions']) === false) {
2326
+			return false;
2327
+		}
2328
+
2329
+		$types      = (array) $types;
2330
+		$conditions = $this->tokens[$stackPtr]['conditions'];
2331
+
2332
+		foreach ($types as $type) {
2333
+			if (in_array($type, $conditions, true) === true) {
2334
+				// We found a token with the required type.
2335
+				return true;
2336
+			}
2337
+		}
2338
+
2339
+		return false;
2340
+
2341
+	}//end hasCondition()
2342
+
2343
+
2344
+	/**
2345
+	 * Return the position of the condition for the passed token.
2346
+	 *
2347
+	 * Returns FALSE if the token does not have the condition.
2348
+	 *
2349
+	 * @param int        $stackPtr The position of the token we are checking.
2350
+	 * @param int|string $type     The type of token to search for.
2351
+	 *
2352
+	 * @return int
2353
+	 */
2354
+	public function getCondition($stackPtr, $type)
2355
+	{
2356
+		// Check for the existence of the token.
2357
+		if (isset($this->tokens[$stackPtr]) === false) {
2358
+			return false;
2359
+		}
2360
+
2361
+		// Make sure the token has conditions.
2362
+		if (isset($this->tokens[$stackPtr]['conditions']) === false) {
2363
+			return false;
2364
+		}
2365
+
2366
+		$conditions = $this->tokens[$stackPtr]['conditions'];
2367
+		foreach ($conditions as $token => $condition) {
2368
+			if ($condition === $type) {
2369
+				return $token;
2370
+			}
2371
+		}
2372
+
2373
+		return false;
2374
+
2375
+	}//end getCondition()
2376
+
2377
+
2378
+	/**
2379
+	 * Returns the name of the class that the specified class extends.
2380
+	 * (works for classes, anonymous classes and interfaces)
2381
+	 *
2382
+	 * Returns FALSE on error or if there is no extended class name.
2383
+	 *
2384
+	 * @param int $stackPtr The stack position of the class.
2385
+	 *
2386
+	 * @return string|false
2387
+	 */
2388
+	public function findExtendedClassName($stackPtr)
2389
+	{
2390
+		// Check for the existence of the token.
2391
+		if (isset($this->tokens[$stackPtr]) === false) {
2392
+			return false;
2393
+		}
2394
+
2395
+		if ($this->tokens[$stackPtr]['code'] !== T_CLASS
2396
+			&& $this->tokens[$stackPtr]['code'] !== T_ANON_CLASS
2397
+			&& $this->tokens[$stackPtr]['code'] !== T_INTERFACE
2398
+		) {
2399
+			return false;
2400
+		}
2401
+
2402
+		if (isset($this->tokens[$stackPtr]['scope_opener']) === false) {
2403
+			return false;
2404
+		}
2405
+
2406
+		$classOpenerIndex = $this->tokens[$stackPtr]['scope_opener'];
2407
+		$extendsIndex     = $this->findNext(T_EXTENDS, $stackPtr, $classOpenerIndex);
2408
+		if (false === $extendsIndex) {
2409
+			return false;
2410
+		}
2411
+
2412
+		$find = [
2413
+			T_NS_SEPARATOR,
2414
+			T_STRING,
2415
+			T_WHITESPACE,
2416
+		];
2417
+
2418
+		$end  = $this->findNext($find, ($extendsIndex + 1), ($classOpenerIndex + 1), true);
2419
+		$name = $this->getTokensAsString(($extendsIndex + 1), ($end - $extendsIndex - 1));
2420
+		$name = trim($name);
2421
+
2422
+		if ($name === '') {
2423
+			return false;
2424
+		}
2425
+
2426
+		return $name;
2427
+
2428
+	}//end findExtendedClassName()
2429
+
2430
+
2431
+	/**
2432
+	 * Returns the names of the interfaces that the specified class implements.
2433
+	 *
2434
+	 * Returns FALSE on error or if there are no implemented interface names.
2435
+	 *
2436
+	 * @param int $stackPtr The stack position of the class.
2437
+	 *
2438
+	 * @return array|false
2439
+	 */
2440
+	public function findImplementedInterfaceNames($stackPtr)
2441
+	{
2442
+		// Check for the existence of the token.
2443
+		if (isset($this->tokens[$stackPtr]) === false) {
2444
+			return false;
2445
+		}
2446
+
2447
+		if ($this->tokens[$stackPtr]['code'] !== T_CLASS
2448
+			&& $this->tokens[$stackPtr]['code'] !== T_ANON_CLASS
2449
+		) {
2450
+			return false;
2451
+		}
2452
+
2453
+		if (isset($this->tokens[$stackPtr]['scope_closer']) === false) {
2454
+			return false;
2455
+		}
2456
+
2457
+		$classOpenerIndex = $this->tokens[$stackPtr]['scope_opener'];
2458
+		$implementsIndex  = $this->findNext(T_IMPLEMENTS, $stackPtr, $classOpenerIndex);
2459
+		if ($implementsIndex === false) {
2460
+			return false;
2461
+		}
2462
+
2463
+		$find = [
2464
+			T_NS_SEPARATOR,
2465
+			T_STRING,
2466
+			T_WHITESPACE,
2467
+			T_COMMA,
2468
+		];
2469
+
2470
+		$end  = $this->findNext($find, ($implementsIndex + 1), ($classOpenerIndex + 1), true);
2471
+		$name = $this->getTokensAsString(($implementsIndex + 1), ($end - $implementsIndex - 1));
2472
+		$name = trim($name);
2473
+
2474
+		if ($name === '') {
2475
+			return false;
2476
+		} else {
2477
+			$names = explode(',', $name);
2478
+			$names = array_map('trim', $names);
2479
+			return $names;
2480
+		}
2481
+
2482
+	}//end findImplementedInterfaceNames()
2483 2483
 
2484 2484
 
2485 2485
 }//end class
Please login to merge, or discard this patch.