Completed
Pull Request — develop (#1492)
by Zack
33:18 queued 12:17
created
vendor/squizlabs/php_codesniffer/src/Files/LocalFile.php 1 patch
Indentation   +191 added lines, -191 removed lines patch added patch discarded remove patch
@@ -17,197 +17,197 @@
 block discarded – undo
17 17
 {
18 18
 
19 19
 
20
-    /**
21
-     * Creates a LocalFile object and sets the content.
22
-     *
23
-     * @param string                   $path    The absolute path to the file.
24
-     * @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
25
-     * @param \PHP_CodeSniffer\Config  $config  The config data for the run.
26
-     *
27
-     * @return void
28
-     */
29
-    public function __construct($path, Ruleset $ruleset, Config $config)
30
-    {
31
-        $this->path = trim($path);
32
-        if (is_readable($this->path) === false) {
33
-            parent::__construct($this->path, $ruleset, $config);
34
-            $error = 'Error opening file; file no longer exists or you do not have access to read the file';
35
-            $this->addMessage(true, $error, 1, 1, 'Internal.LocalFile', [], 5, false);
36
-            $this->ignored = true;
37
-            return;
38
-        }
39
-
40
-        // Before we go and spend time tokenizing this file, just check
41
-        // to see if there is a tag up top to indicate that the whole
42
-        // file should be ignored. It must be on one of the first two lines.
43
-        if ($config->annotations === true) {
44
-            $handle = fopen($this->path, 'r');
45
-            if ($handle !== false) {
46
-                $firstContent  = fgets($handle);
47
-                $firstContent .= fgets($handle);
48
-                fclose($handle);
49
-
50
-                if (strpos($firstContent, '@codingStandardsIgnoreFile') !== false
51
-                    || stripos($firstContent, 'phpcs:ignorefile') !== false
52
-                ) {
53
-                    // We are ignoring the whole file.
54
-                    $this->ignored = true;
55
-                    return;
56
-                }
57
-            }
58
-        }
59
-
60
-        $this->reloadContent();
61
-
62
-        parent::__construct($this->path, $ruleset, $config);
63
-
64
-    }//end __construct()
65
-
66
-
67
-    /**
68
-     * Loads the latest version of the file's content from the file system.
69
-     *
70
-     * @return void
71
-     */
72
-    public function reloadContent()
73
-    {
74
-        $this->setContent(file_get_contents($this->path));
75
-
76
-    }//end reloadContent()
77
-
78
-
79
-    /**
80
-     * Processes the file.
81
-     *
82
-     * @return void
83
-     */
84
-    public function process()
85
-    {
86
-        if ($this->ignored === true) {
87
-            return;
88
-        }
89
-
90
-        if ($this->configCache['cache'] === false) {
91
-            parent::process();
92
-            return;
93
-        }
94
-
95
-        $hash  = md5_file($this->path);
96
-        $cache = Cache::get($this->path);
97
-        if ($cache !== false && $cache['hash'] === $hash) {
98
-            // We can't filter metrics, so just load all of them.
99
-            $this->metrics = $cache['metrics'];
100
-
101
-            if ($this->configCache['recordErrors'] === true) {
102
-                // Replay the cached errors and warnings to filter out the ones
103
-                // we don't need for this specific run.
104
-                $this->configCache['cache'] = false;
105
-                $this->replayErrors($cache['errors'], $cache['warnings']);
106
-                $this->configCache['cache'] = true;
107
-            } else {
108
-                $this->errorCount   = $cache['errorCount'];
109
-                $this->warningCount = $cache['warningCount'];
110
-                $this->fixableCount = $cache['fixableCount'];
111
-            }
112
-
113
-            if (PHP_CODESNIFFER_VERBOSITY > 0
114
-                || (PHP_CODESNIFFER_CBF === true && empty($this->config->files) === false)
115
-            ) {
116
-                echo "[loaded from cache]... ";
117
-            }
118
-
119
-            $this->numTokens = $cache['numTokens'];
120
-            $this->fromCache = true;
121
-            return;
122
-        }//end if
123
-
124
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
125
-            echo PHP_EOL;
126
-        }
127
-
128
-        parent::process();
129
-
130
-        $cache = [
131
-            'hash'         => $hash,
132
-            'errors'       => $this->errors,
133
-            'warnings'     => $this->warnings,
134
-            'metrics'      => $this->metrics,
135
-            'errorCount'   => $this->errorCount,
136
-            'warningCount' => $this->warningCount,
137
-            'fixableCount' => $this->fixableCount,
138
-            'numTokens'    => $this->numTokens,
139
-        ];
140
-
141
-        Cache::set($this->path, $cache);
142
-
143
-        // During caching, we don't filter out errors in any way, so
144
-        // we need to do that manually now by replaying them.
145
-        if ($this->configCache['recordErrors'] === true) {
146
-            $this->configCache['cache'] = false;
147
-            $this->replayErrors($this->errors, $this->warnings);
148
-            $this->configCache['cache'] = true;
149
-        }
150
-
151
-    }//end process()
152
-
153
-
154
-    /**
155
-     * Clears and replays error and warnings for the file.
156
-     *
157
-     * Replaying errors and warnings allows for filtering rules to be changed
158
-     * and then errors and warnings to be reapplied with the new rules. This is
159
-     * particularly useful while caching.
160
-     *
161
-     * @param array $errors   The list of errors to replay.
162
-     * @param array $warnings The list of warnings to replay.
163
-     *
164
-     * @return void
165
-     */
166
-    private function replayErrors($errors, $warnings)
167
-    {
168
-        $this->errors       = [];
169
-        $this->warnings     = [];
170
-        $this->errorCount   = 0;
171
-        $this->warningCount = 0;
172
-        $this->fixableCount = 0;
173
-
174
-        foreach ($errors as $line => $lineErrors) {
175
-            foreach ($lineErrors as $column => $colErrors) {
176
-                foreach ($colErrors as $error) {
177
-                    $this->activeListener = $error['listener'];
178
-                    $this->addMessage(
179
-                        true,
180
-                        $error['message'],
181
-                        $line,
182
-                        $column,
183
-                        $error['source'],
184
-                        [],
185
-                        $error['severity'],
186
-                        $error['fixable']
187
-                    );
188
-                }
189
-            }
190
-        }
191
-
192
-        foreach ($warnings as $line => $lineErrors) {
193
-            foreach ($lineErrors as $column => $colErrors) {
194
-                foreach ($colErrors as $error) {
195
-                    $this->activeListener = $error['listener'];
196
-                    $this->addMessage(
197
-                        false,
198
-                        $error['message'],
199
-                        $line,
200
-                        $column,
201
-                        $error['source'],
202
-                        [],
203
-                        $error['severity'],
204
-                        $error['fixable']
205
-                    );
206
-                }
207
-            }
208
-        }
209
-
210
-    }//end replayErrors()
20
+	/**
21
+	 * Creates a LocalFile object and sets the content.
22
+	 *
23
+	 * @param string                   $path    The absolute path to the file.
24
+	 * @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
25
+	 * @param \PHP_CodeSniffer\Config  $config  The config data for the run.
26
+	 *
27
+	 * @return void
28
+	 */
29
+	public function __construct($path, Ruleset $ruleset, Config $config)
30
+	{
31
+		$this->path = trim($path);
32
+		if (is_readable($this->path) === false) {
33
+			parent::__construct($this->path, $ruleset, $config);
34
+			$error = 'Error opening file; file no longer exists or you do not have access to read the file';
35
+			$this->addMessage(true, $error, 1, 1, 'Internal.LocalFile', [], 5, false);
36
+			$this->ignored = true;
37
+			return;
38
+		}
39
+
40
+		// Before we go and spend time tokenizing this file, just check
41
+		// to see if there is a tag up top to indicate that the whole
42
+		// file should be ignored. It must be on one of the first two lines.
43
+		if ($config->annotations === true) {
44
+			$handle = fopen($this->path, 'r');
45
+			if ($handle !== false) {
46
+				$firstContent  = fgets($handle);
47
+				$firstContent .= fgets($handle);
48
+				fclose($handle);
49
+
50
+				if (strpos($firstContent, '@codingStandardsIgnoreFile') !== false
51
+					|| stripos($firstContent, 'phpcs:ignorefile') !== false
52
+				) {
53
+					// We are ignoring the whole file.
54
+					$this->ignored = true;
55
+					return;
56
+				}
57
+			}
58
+		}
59
+
60
+		$this->reloadContent();
61
+
62
+		parent::__construct($this->path, $ruleset, $config);
63
+
64
+	}//end __construct()
65
+
66
+
67
+	/**
68
+	 * Loads the latest version of the file's content from the file system.
69
+	 *
70
+	 * @return void
71
+	 */
72
+	public function reloadContent()
73
+	{
74
+		$this->setContent(file_get_contents($this->path));
75
+
76
+	}//end reloadContent()
77
+
78
+
79
+	/**
80
+	 * Processes the file.
81
+	 *
82
+	 * @return void
83
+	 */
84
+	public function process()
85
+	{
86
+		if ($this->ignored === true) {
87
+			return;
88
+		}
89
+
90
+		if ($this->configCache['cache'] === false) {
91
+			parent::process();
92
+			return;
93
+		}
94
+
95
+		$hash  = md5_file($this->path);
96
+		$cache = Cache::get($this->path);
97
+		if ($cache !== false && $cache['hash'] === $hash) {
98
+			// We can't filter metrics, so just load all of them.
99
+			$this->metrics = $cache['metrics'];
100
+
101
+			if ($this->configCache['recordErrors'] === true) {
102
+				// Replay the cached errors and warnings to filter out the ones
103
+				// we don't need for this specific run.
104
+				$this->configCache['cache'] = false;
105
+				$this->replayErrors($cache['errors'], $cache['warnings']);
106
+				$this->configCache['cache'] = true;
107
+			} else {
108
+				$this->errorCount   = $cache['errorCount'];
109
+				$this->warningCount = $cache['warningCount'];
110
+				$this->fixableCount = $cache['fixableCount'];
111
+			}
112
+
113
+			if (PHP_CODESNIFFER_VERBOSITY > 0
114
+				|| (PHP_CODESNIFFER_CBF === true && empty($this->config->files) === false)
115
+			) {
116
+				echo "[loaded from cache]... ";
117
+			}
118
+
119
+			$this->numTokens = $cache['numTokens'];
120
+			$this->fromCache = true;
121
+			return;
122
+		}//end if
123
+
124
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
125
+			echo PHP_EOL;
126
+		}
127
+
128
+		parent::process();
129
+
130
+		$cache = [
131
+			'hash'         => $hash,
132
+			'errors'       => $this->errors,
133
+			'warnings'     => $this->warnings,
134
+			'metrics'      => $this->metrics,
135
+			'errorCount'   => $this->errorCount,
136
+			'warningCount' => $this->warningCount,
137
+			'fixableCount' => $this->fixableCount,
138
+			'numTokens'    => $this->numTokens,
139
+		];
140
+
141
+		Cache::set($this->path, $cache);
142
+
143
+		// During caching, we don't filter out errors in any way, so
144
+		// we need to do that manually now by replaying them.
145
+		if ($this->configCache['recordErrors'] === true) {
146
+			$this->configCache['cache'] = false;
147
+			$this->replayErrors($this->errors, $this->warnings);
148
+			$this->configCache['cache'] = true;
149
+		}
150
+
151
+	}//end process()
152
+
153
+
154
+	/**
155
+	 * Clears and replays error and warnings for the file.
156
+	 *
157
+	 * Replaying errors and warnings allows for filtering rules to be changed
158
+	 * and then errors and warnings to be reapplied with the new rules. This is
159
+	 * particularly useful while caching.
160
+	 *
161
+	 * @param array $errors   The list of errors to replay.
162
+	 * @param array $warnings The list of warnings to replay.
163
+	 *
164
+	 * @return void
165
+	 */
166
+	private function replayErrors($errors, $warnings)
167
+	{
168
+		$this->errors       = [];
169
+		$this->warnings     = [];
170
+		$this->errorCount   = 0;
171
+		$this->warningCount = 0;
172
+		$this->fixableCount = 0;
173
+
174
+		foreach ($errors as $line => $lineErrors) {
175
+			foreach ($lineErrors as $column => $colErrors) {
176
+				foreach ($colErrors as $error) {
177
+					$this->activeListener = $error['listener'];
178
+					$this->addMessage(
179
+						true,
180
+						$error['message'],
181
+						$line,
182
+						$column,
183
+						$error['source'],
184
+						[],
185
+						$error['severity'],
186
+						$error['fixable']
187
+					);
188
+				}
189
+			}
190
+		}
191
+
192
+		foreach ($warnings as $line => $lineErrors) {
193
+			foreach ($lineErrors as $column => $colErrors) {
194
+				foreach ($colErrors as $error) {
195
+					$this->activeListener = $error['listener'];
196
+					$this->addMessage(
197
+						false,
198
+						$error['message'],
199
+						$line,
200
+						$column,
201
+						$error['source'],
202
+						[],
203
+						$error['severity'],
204
+						$error['fixable']
205
+					);
206
+				}
207
+			}
208
+		}
209
+
210
+	}//end replayErrors()
211 211
 
212 212
 
213 213
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Files/DummyFile.php 1 patch
Indentation   +48 added lines, -48 removed lines patch added patch discarded remove patch
@@ -21,62 +21,62 @@
 block discarded – undo
21 21
 {
22 22
 
23 23
 
24
-    /**
25
-     * Creates a DummyFile object and sets the content.
26
-     *
27
-     * @param string                   $content The content of the file.
28
-     * @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
29
-     * @param \PHP_CodeSniffer\Config  $config  The config data for the run.
30
-     *
31
-     * @return void
32
-     */
33
-    public function __construct($content, Ruleset $ruleset, Config $config)
34
-    {
35
-        $this->setContent($content);
24
+	/**
25
+	 * Creates a DummyFile object and sets the content.
26
+	 *
27
+	 * @param string                   $content The content of the file.
28
+	 * @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
29
+	 * @param \PHP_CodeSniffer\Config  $config  The config data for the run.
30
+	 *
31
+	 * @return void
32
+	 */
33
+	public function __construct($content, Ruleset $ruleset, Config $config)
34
+	{
35
+		$this->setContent($content);
36 36
 
37
-        // See if a filename was defined in the content.
38
-        // This is done by including: phpcs_input_file: [file path]
39
-        // as the first line of content.
40
-        $path = 'STDIN';
41
-        if ($content !== null) {
42
-            if (substr($content, 0, 17) === 'phpcs_input_file:') {
43
-                $eolPos   = strpos($content, $this->eolChar);
44
-                $filename = trim(substr($content, 17, ($eolPos - 17)));
45
-                $content  = substr($content, ($eolPos + strlen($this->eolChar)));
46
-                $path     = $filename;
37
+		// See if a filename was defined in the content.
38
+		// This is done by including: phpcs_input_file: [file path]
39
+		// as the first line of content.
40
+		$path = 'STDIN';
41
+		if ($content !== null) {
42
+			if (substr($content, 0, 17) === 'phpcs_input_file:') {
43
+				$eolPos   = strpos($content, $this->eolChar);
44
+				$filename = trim(substr($content, 17, ($eolPos - 17)));
45
+				$content  = substr($content, ($eolPos + strlen($this->eolChar)));
46
+				$path     = $filename;
47 47
 
48
-                $this->setContent($content);
49
-            }
50
-        }
48
+				$this->setContent($content);
49
+			}
50
+		}
51 51
 
52
-        // The CLI arg overrides anything passed in the content.
53
-        if ($config->stdinPath !== null) {
54
-            $path = $config->stdinPath;
55
-        }
52
+		// The CLI arg overrides anything passed in the content.
53
+		if ($config->stdinPath !== null) {
54
+			$path = $config->stdinPath;
55
+		}
56 56
 
57
-        parent::__construct($path, $ruleset, $config);
57
+		parent::__construct($path, $ruleset, $config);
58 58
 
59
-    }//end __construct()
59
+	}//end __construct()
60 60
 
61 61
 
62
-    /**
63
-     * Set the error, warning, and fixable counts for the file.
64
-     *
65
-     * @param int $errorCount   The number of errors found.
66
-     * @param int $warningCount The number of warnings found.
67
-     * @param int $fixableCount The number of fixable errors found.
68
-     * @param int $fixedCount   The number of errors that were fixed.
69
-     *
70
-     * @return void
71
-     */
72
-    public function setErrorCounts($errorCount, $warningCount, $fixableCount, $fixedCount)
73
-    {
74
-        $this->errorCount   = $errorCount;
75
-        $this->warningCount = $warningCount;
76
-        $this->fixableCount = $fixableCount;
77
-        $this->fixedCount   = $fixedCount;
62
+	/**
63
+	 * Set the error, warning, and fixable counts for the file.
64
+	 *
65
+	 * @param int $errorCount   The number of errors found.
66
+	 * @param int $warningCount The number of warnings found.
67
+	 * @param int $fixableCount The number of fixable errors found.
68
+	 * @param int $fixedCount   The number of errors that were fixed.
69
+	 *
70
+	 * @return void
71
+	 */
72
+	public function setErrorCounts($errorCount, $warningCount, $fixableCount, $fixedCount)
73
+	{
74
+		$this->errorCount   = $errorCount;
75
+		$this->warningCount = $warningCount;
76
+		$this->fixableCount = $fixableCount;
77
+		$this->fixedCount   = $fixedCount;
78 78
 
79
-    }//end setErrorCounts()
79
+	}//end setErrorCounts()
80 80
 
81 81
 
82 82
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Ruleset.php 1 patch
Indentation   +1348 added lines, -1348 removed lines patch added patch discarded remove patch
@@ -17,1354 +17,1354 @@
 block discarded – undo
17 17
 class Ruleset
18 18
 {
19 19
 
20
-    /**
21
-     * The name of the coding standard being used.
22
-     *
23
-     * If a top-level standard includes other standards, or sniffs
24
-     * from other standards, only the name of the top-level standard
25
-     * will be stored in here.
26
-     *
27
-     * If multiple top-level standards are being loaded into
28
-     * a single ruleset object, this will store a comma separated list
29
-     * of the top-level standard names.
30
-     *
31
-     * @var string
32
-     */
33
-    public $name = '';
34
-
35
-    /**
36
-     * A list of file paths for the ruleset files being used.
37
-     *
38
-     * @var string[]
39
-     */
40
-    public $paths = [];
41
-
42
-    /**
43
-     * A list of regular expressions used to ignore specific sniffs for files and folders.
44
-     *
45
-     * Is also used to set global exclude patterns.
46
-     * The key is the regular expression and the value is the type
47
-     * of ignore pattern (absolute or relative).
48
-     *
49
-     * @var array<string, string>
50
-     */
51
-    public $ignorePatterns = [];
52
-
53
-    /**
54
-     * A list of regular expressions used to include specific sniffs for files and folders.
55
-     *
56
-     * The key is the sniff code and the value is an array with
57
-     * the key being a regular expression and the value is the type
58
-     * of ignore pattern (absolute or relative).
59
-     *
60
-     * @var array<string, array<string, string>>
61
-     */
62
-    public $includePatterns = [];
63
-
64
-    /**
65
-     * An array of sniff objects that are being used to check files.
66
-     *
67
-     * The key is the fully qualified name of the sniff class
68
-     * and the value is the sniff object.
69
-     *
70
-     * @var array<string, \PHP_CodeSniffer\Sniffs\Sniff>
71
-     */
72
-    public $sniffs = [];
73
-
74
-    /**
75
-     * A mapping of sniff codes to fully qualified class names.
76
-     *
77
-     * The key is the sniff code and the value
78
-     * is the fully qualified name of the sniff class.
79
-     *
80
-     * @var array<string, string>
81
-     */
82
-    public $sniffCodes = [];
83
-
84
-    /**
85
-     * An array of token types and the sniffs that are listening for them.
86
-     *
87
-     * The key is the token name being listened for and the value
88
-     * is the sniff object.
89
-     *
90
-     * @var array<int, \PHP_CodeSniffer\Sniffs\Sniff>
91
-     */
92
-    public $tokenListeners = [];
93
-
94
-    /**
95
-     * An array of rules from the ruleset.xml file.
96
-     *
97
-     * It may be empty, indicating that the ruleset does not override
98
-     * any of the default sniff settings.
99
-     *
100
-     * @var array<string, mixed>
101
-     */
102
-    public $ruleset = [];
103
-
104
-    /**
105
-     * The directories that the processed rulesets are in.
106
-     *
107
-     * @var string[]
108
-     */
109
-    protected $rulesetDirs = [];
110
-
111
-    /**
112
-     * The config data for the run.
113
-     *
114
-     * @var \PHP_CodeSniffer\Config
115
-     */
116
-    private $config = null;
117
-
118
-
119
-    /**
120
-     * Initialise the ruleset that the run will use.
121
-     *
122
-     * @param \PHP_CodeSniffer\Config $config The config data for the run.
123
-     *
124
-     * @return void
125
-     */
126
-    public function __construct(Config $config)
127
-    {
128
-        // Ignore sniff restrictions if caching is on.
129
-        $restrictions = [];
130
-        $exclusions   = [];
131
-        if ($config->cache === false) {
132
-            $restrictions = $config->sniffs;
133
-            $exclusions   = $config->exclude;
134
-        }
135
-
136
-        $this->config = $config;
137
-        $sniffs       = [];
138
-
139
-        $standardPaths = [];
140
-        foreach ($config->standards as $standard) {
141
-            $installed = Util\Standards::getInstalledStandardPath($standard);
142
-            if ($installed === null) {
143
-                $standard = Util\Common::realpath($standard);
144
-                if (is_dir($standard) === true
145
-                    && is_file(Util\Common::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml')) === true
146
-                ) {
147
-                    $standard = Util\Common::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml');
148
-                }
149
-            } else {
150
-                $standard = $installed;
151
-            }
152
-
153
-            $standardPaths[] = $standard;
154
-        }
155
-
156
-        foreach ($standardPaths as $standard) {
157
-            $ruleset = @simplexml_load_string(file_get_contents($standard));
158
-            if ($ruleset !== false) {
159
-                $standardName = (string) $ruleset['name'];
160
-                if ($this->name !== '') {
161
-                    $this->name .= ', ';
162
-                }
163
-
164
-                $this->name .= $standardName;
165
-
166
-                // Allow autoloading of custom files inside this standard.
167
-                if (isset($ruleset['namespace']) === true) {
168
-                    $namespace = (string) $ruleset['namespace'];
169
-                } else {
170
-                    $namespace = basename(dirname($standard));
171
-                }
172
-
173
-                Autoload::addSearchPath(dirname($standard), $namespace);
174
-            }
175
-
176
-            if (defined('PHP_CODESNIFFER_IN_TESTS') === true && empty($restrictions) === false) {
177
-                // In unit tests, only register the sniffs that the test wants and not the entire standard.
178
-                try {
179
-                    foreach ($restrictions as $restriction) {
180
-                        $sniffs = array_merge($sniffs, $this->expandRulesetReference($restriction, dirname($standard)));
181
-                    }
182
-                } catch (RuntimeException $e) {
183
-                    // Sniff reference could not be expanded, which probably means this
184
-                    // is an installed standard. Let the unit test system take care of
185
-                    // setting the correct sniff for testing.
186
-                    return;
187
-                }
188
-
189
-                break;
190
-            }
191
-
192
-            if (PHP_CODESNIFFER_VERBOSITY === 1) {
193
-                echo "Registering sniffs in the $standardName standard... ";
194
-                if (count($config->standards) > 1 || PHP_CODESNIFFER_VERBOSITY > 2) {
195
-                    echo PHP_EOL;
196
-                }
197
-            }
198
-
199
-            $sniffs = array_merge($sniffs, $this->processRuleset($standard));
200
-        }//end foreach
201
-
202
-        $sniffRestrictions = [];
203
-        foreach ($restrictions as $sniffCode) {
204
-            $parts     = explode('.', strtolower($sniffCode));
205
-            $sniffName = $parts[0].'\sniffs\\'.$parts[1].'\\'.$parts[2].'sniff';
206
-            $sniffRestrictions[$sniffName] = true;
207
-        }
208
-
209
-        $sniffExclusions = [];
210
-        foreach ($exclusions as $sniffCode) {
211
-            $parts     = explode('.', strtolower($sniffCode));
212
-            $sniffName = $parts[0].'\sniffs\\'.$parts[1].'\\'.$parts[2].'sniff';
213
-            $sniffExclusions[$sniffName] = true;
214
-        }
215
-
216
-        $this->registerSniffs($sniffs, $sniffRestrictions, $sniffExclusions);
217
-        $this->populateTokenListeners();
218
-
219
-        $numSniffs = count($this->sniffs);
220
-        if (PHP_CODESNIFFER_VERBOSITY === 1) {
221
-            echo "DONE ($numSniffs sniffs registered)".PHP_EOL;
222
-        }
223
-
224
-        if ($numSniffs === 0) {
225
-            throw new RuntimeException('No sniffs were registered');
226
-        }
227
-
228
-    }//end __construct()
229
-
230
-
231
-    /**
232
-     * Prints a report showing the sniffs contained in a standard.
233
-     *
234
-     * @return void
235
-     */
236
-    public function explain()
237
-    {
238
-        $sniffs = array_keys($this->sniffCodes);
239
-        sort($sniffs);
240
-
241
-        ob_start();
242
-
243
-        $lastStandard = null;
244
-        $lastCount    = '';
245
-        $sniffCount   = count($sniffs);
246
-
247
-        // Add a dummy entry to the end so we loop
248
-        // one last time and clear the output buffer.
249
-        $sniffs[] = '';
250
-
251
-        echo PHP_EOL."The $this->name standard contains $sniffCount sniffs".PHP_EOL;
252
-
253
-        ob_start();
254
-
255
-        foreach ($sniffs as $i => $sniff) {
256
-            if ($i === $sniffCount) {
257
-                $currentStandard = null;
258
-            } else {
259
-                $currentStandard = substr($sniff, 0, strpos($sniff, '.'));
260
-                if ($lastStandard === null) {
261
-                    $lastStandard = $currentStandard;
262
-                }
263
-            }
264
-
265
-            if ($currentStandard !== $lastStandard) {
266
-                $sniffList = ob_get_contents();
267
-                ob_end_clean();
268
-
269
-                echo PHP_EOL.$lastStandard.' ('.$lastCount.' sniff';
270
-                if ($lastCount > 1) {
271
-                    echo 's';
272
-                }
273
-
274
-                echo ')'.PHP_EOL;
275
-                echo str_repeat('-', (strlen($lastStandard.$lastCount) + 10));
276
-                echo PHP_EOL;
277
-                echo $sniffList;
278
-
279
-                $lastStandard = $currentStandard;
280
-                $lastCount    = 0;
281
-
282
-                if ($currentStandard === null) {
283
-                    break;
284
-                }
285
-
286
-                ob_start();
287
-            }//end if
288
-
289
-            echo '  '.$sniff.PHP_EOL;
290
-            $lastCount++;
291
-        }//end foreach
292
-
293
-    }//end explain()
294
-
295
-
296
-    /**
297
-     * Processes a single ruleset and returns a list of the sniffs it represents.
298
-     *
299
-     * Rules founds within the ruleset are processed immediately, but sniff classes
300
-     * are not registered by this method.
301
-     *
302
-     * @param string $rulesetPath The path to a ruleset XML file.
303
-     * @param int    $depth       How many nested processing steps we are in. This
304
-     *                            is only used for debug output.
305
-     *
306
-     * @return string[]
307
-     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the ruleset path is invalid.
308
-     */
309
-    public function processRuleset($rulesetPath, $depth=0)
310
-    {
311
-        $rulesetPath = Util\Common::realpath($rulesetPath);
312
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
313
-            echo str_repeat("\t", $depth);
314
-            echo 'Processing ruleset '.Util\Common::stripBasepath($rulesetPath, $this->config->basepath).PHP_EOL;
315
-        }
316
-
317
-        libxml_use_internal_errors(true);
318
-        $ruleset = simplexml_load_string(file_get_contents($rulesetPath));
319
-        if ($ruleset === false) {
320
-            $errorMsg = "Ruleset $rulesetPath is not valid".PHP_EOL;
321
-            $errors   = libxml_get_errors();
322
-            foreach ($errors as $error) {
323
-                $errorMsg .= '- On line '.$error->line.', column '.$error->column.': '.$error->message;
324
-            }
325
-
326
-            libxml_clear_errors();
327
-            throw new RuntimeException($errorMsg);
328
-        }
329
-
330
-        libxml_use_internal_errors(false);
331
-
332
-        $ownSniffs      = [];
333
-        $includedSniffs = [];
334
-        $excludedSniffs = [];
335
-
336
-        $this->paths[]       = $rulesetPath;
337
-        $rulesetDir          = dirname($rulesetPath);
338
-        $this->rulesetDirs[] = $rulesetDir;
339
-
340
-        $sniffDir = $rulesetDir.DIRECTORY_SEPARATOR.'Sniffs';
341
-        if (is_dir($sniffDir) === true) {
342
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
343
-                echo str_repeat("\t", $depth);
344
-                echo "\tAdding sniff files from ".Util\Common::stripBasepath($sniffDir, $this->config->basepath).' directory'.PHP_EOL;
345
-            }
346
-
347
-            $ownSniffs = $this->expandSniffDirectory($sniffDir, $depth);
348
-        }
349
-
350
-        // Included custom autoloaders.
351
-        foreach ($ruleset->{'autoload'} as $autoload) {
352
-            if ($this->shouldProcessElement($autoload) === false) {
353
-                continue;
354
-            }
355
-
356
-            $autoloadPath = (string) $autoload;
357
-            if (is_file($autoloadPath) === false) {
358
-                $autoloadPath = Util\Common::realPath(dirname($rulesetPath).DIRECTORY_SEPARATOR.$autoloadPath);
359
-            }
360
-
361
-            if ($autoloadPath === false) {
362
-                throw new RuntimeException('The specified autoload file "'.$autoload.'" does not exist');
363
-            }
364
-
365
-            include_once $autoloadPath;
366
-
367
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
368
-                echo str_repeat("\t", $depth);
369
-                echo "\t=> included autoloader $autoloadPath".PHP_EOL;
370
-            }
371
-        }//end foreach
372
-
373
-        // Process custom sniff config settings.
374
-        foreach ($ruleset->{'config'} as $config) {
375
-            if ($this->shouldProcessElement($config) === false) {
376
-                continue;
377
-            }
378
-
379
-            Config::setConfigData((string) $config['name'], (string) $config['value'], true);
380
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
381
-                echo str_repeat("\t", $depth);
382
-                echo "\t=> set config value ".(string) $config['name'].': '.(string) $config['value'].PHP_EOL;
383
-            }
384
-        }
385
-
386
-        foreach ($ruleset->rule as $rule) {
387
-            if (isset($rule['ref']) === false
388
-                || $this->shouldProcessElement($rule) === false
389
-            ) {
390
-                continue;
391
-            }
392
-
393
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
394
-                echo str_repeat("\t", $depth);
395
-                echo "\tProcessing rule \"".$rule['ref'].'"'.PHP_EOL;
396
-            }
397
-
398
-            $expandedSniffs = $this->expandRulesetReference((string) $rule['ref'], $rulesetDir, $depth);
399
-            $newSniffs      = array_diff($expandedSniffs, $includedSniffs);
400
-            $includedSniffs = array_merge($includedSniffs, $expandedSniffs);
401
-
402
-            $parts = explode('.', $rule['ref']);
403
-            if (count($parts) === 4
404
-                && $parts[0] !== ''
405
-                && $parts[1] !== ''
406
-                && $parts[2] !== ''
407
-            ) {
408
-                $sniffCode = $parts[0].'.'.$parts[1].'.'.$parts[2];
409
-                if (isset($this->ruleset[$sniffCode]['severity']) === true
410
-                    && $this->ruleset[$sniffCode]['severity'] === 0
411
-                ) {
412
-                    // This sniff code has already been turned off, but now
413
-                    // it is being explicitly included again, so turn it back on.
414
-                    $this->ruleset[(string) $rule['ref']]['severity'] = 5;
415
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
416
-                        echo str_repeat("\t", $depth);
417
-                        echo "\t\t* disabling sniff exclusion for specific message code *".PHP_EOL;
418
-                        echo str_repeat("\t", $depth);
419
-                        echo "\t\t=> severity set to 5".PHP_EOL;
420
-                    }
421
-                } else if (empty($newSniffs) === false) {
422
-                    $newSniff = $newSniffs[0];
423
-                    if (in_array($newSniff, $ownSniffs, true) === false) {
424
-                        // Including a sniff that hasn't been included higher up, but
425
-                        // only including a single message from it. So turn off all messages in
426
-                        // the sniff, except this one.
427
-                        $this->ruleset[$sniffCode]['severity']            = 0;
428
-                        $this->ruleset[(string) $rule['ref']]['severity'] = 5;
429
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
430
-                            echo str_repeat("\t", $depth);
431
-                            echo "\t\tExcluding sniff \"".$sniffCode.'" except for "'.$parts[3].'"'.PHP_EOL;
432
-                        }
433
-                    }
434
-                }//end if
435
-            }//end if
436
-
437
-            if (isset($rule->exclude) === true) {
438
-                foreach ($rule->exclude as $exclude) {
439
-                    if (isset($exclude['name']) === false) {
440
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
441
-                            echo str_repeat("\t", $depth);
442
-                            echo "\t\t* ignoring empty exclude rule *".PHP_EOL;
443
-                            echo "\t\t\t=> ".$exclude->asXML().PHP_EOL;
444
-                        }
445
-
446
-                        continue;
447
-                    }
448
-
449
-                    if ($this->shouldProcessElement($exclude) === false) {
450
-                        continue;
451
-                    }
452
-
453
-                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
454
-                        echo str_repeat("\t", $depth);
455
-                        echo "\t\tExcluding rule \"".$exclude['name'].'"'.PHP_EOL;
456
-                    }
457
-
458
-                    // Check if a single code is being excluded, which is a shortcut
459
-                    // for setting the severity of the message to 0.
460
-                    $parts = explode('.', $exclude['name']);
461
-                    if (count($parts) === 4) {
462
-                        $this->ruleset[(string) $exclude['name']]['severity'] = 0;
463
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
464
-                            echo str_repeat("\t", $depth);
465
-                            echo "\t\t=> severity set to 0".PHP_EOL;
466
-                        }
467
-                    } else {
468
-                        $excludedSniffs = array_merge(
469
-                            $excludedSniffs,
470
-                            $this->expandRulesetReference((string) $exclude['name'], $rulesetDir, ($depth + 1))
471
-                        );
472
-                    }
473
-                }//end foreach
474
-            }//end if
475
-
476
-            $this->processRule($rule, $newSniffs, $depth);
477
-        }//end foreach
478
-
479
-        // Process custom command line arguments.
480
-        $cliArgs = [];
481
-        foreach ($ruleset->{'arg'} as $arg) {
482
-            if ($this->shouldProcessElement($arg) === false) {
483
-                continue;
484
-            }
485
-
486
-            if (isset($arg['name']) === true) {
487
-                $argString = '--'.(string) $arg['name'];
488
-                if (isset($arg['value']) === true) {
489
-                    $argString .= '='.(string) $arg['value'];
490
-                }
491
-            } else {
492
-                $argString = '-'.(string) $arg['value'];
493
-            }
494
-
495
-            $cliArgs[] = $argString;
496
-
497
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
498
-                echo str_repeat("\t", $depth);
499
-                echo "\t=> set command line value $argString".PHP_EOL;
500
-            }
501
-        }//end foreach
502
-
503
-        // Set custom php ini values as CLI args.
504
-        foreach ($ruleset->{'ini'} as $arg) {
505
-            if ($this->shouldProcessElement($arg) === false) {
506
-                continue;
507
-            }
508
-
509
-            if (isset($arg['name']) === false) {
510
-                continue;
511
-            }
512
-
513
-            $name      = (string) $arg['name'];
514
-            $argString = $name;
515
-            if (isset($arg['value']) === true) {
516
-                $value      = (string) $arg['value'];
517
-                $argString .= "=$value";
518
-            } else {
519
-                $value = 'true';
520
-            }
521
-
522
-            $cliArgs[] = '-d';
523
-            $cliArgs[] = $argString;
524
-
525
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
526
-                echo str_repeat("\t", $depth);
527
-                echo "\t=> set PHP ini value $name to $value".PHP_EOL;
528
-            }
529
-        }//end foreach
530
-
531
-        if (empty($this->config->files) === true) {
532
-            // Process hard-coded file paths.
533
-            foreach ($ruleset->{'file'} as $file) {
534
-                $file      = (string) $file;
535
-                $cliArgs[] = $file;
536
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
537
-                    echo str_repeat("\t", $depth);
538
-                    echo "\t=> added \"$file\" to the file list".PHP_EOL;
539
-                }
540
-            }
541
-        }
542
-
543
-        if (empty($cliArgs) === false) {
544
-            // Change the directory so all relative paths are worked
545
-            // out based on the location of the ruleset instead of
546
-            // the location of the user.
547
-            $inPhar = Util\Common::isPharFile($rulesetDir);
548
-            if ($inPhar === false) {
549
-                $currentDir = getcwd();
550
-                chdir($rulesetDir);
551
-            }
552
-
553
-            $this->config->setCommandLineValues($cliArgs);
554
-
555
-            if ($inPhar === false) {
556
-                chdir($currentDir);
557
-            }
558
-        }
559
-
560
-        // Process custom ignore pattern rules.
561
-        foreach ($ruleset->{'exclude-pattern'} as $pattern) {
562
-            if ($this->shouldProcessElement($pattern) === false) {
563
-                continue;
564
-            }
565
-
566
-            if (isset($pattern['type']) === false) {
567
-                $pattern['type'] = 'absolute';
568
-            }
569
-
570
-            $this->ignorePatterns[(string) $pattern] = (string) $pattern['type'];
571
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
572
-                echo str_repeat("\t", $depth);
573
-                echo "\t=> added global ".(string) $pattern['type'].' ignore pattern: '.(string) $pattern.PHP_EOL;
574
-            }
575
-        }
576
-
577
-        $includedSniffs = array_unique(array_merge($ownSniffs, $includedSniffs));
578
-        $excludedSniffs = array_unique($excludedSniffs);
579
-
580
-        if (PHP_CODESNIFFER_VERBOSITY > 1) {
581
-            $included = count($includedSniffs);
582
-            $excluded = count($excludedSniffs);
583
-            echo str_repeat("\t", $depth);
584
-            echo "=> Ruleset processing complete; included $included sniffs and excluded $excluded".PHP_EOL;
585
-        }
586
-
587
-        // Merge our own sniff list with our externally included
588
-        // sniff list, but filter out any excluded sniffs.
589
-        $files = [];
590
-        foreach ($includedSniffs as $sniff) {
591
-            if (in_array($sniff, $excludedSniffs, true) === true) {
592
-                continue;
593
-            } else {
594
-                $files[] = Util\Common::realpath($sniff);
595
-            }
596
-        }
597
-
598
-        return $files;
599
-
600
-    }//end processRuleset()
601
-
602
-
603
-    /**
604
-     * Expands a directory into a list of sniff files within.
605
-     *
606
-     * @param string $directory The path to a directory.
607
-     * @param int    $depth     How many nested processing steps we are in. This
608
-     *                          is only used for debug output.
609
-     *
610
-     * @return array
611
-     */
612
-    private function expandSniffDirectory($directory, $depth=0)
613
-    {
614
-        $sniffs = [];
615
-
616
-        $rdi = new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS);
617
-        $di  = new \RecursiveIteratorIterator($rdi, 0, \RecursiveIteratorIterator::CATCH_GET_CHILD);
618
-
619
-        $dirLen = strlen($directory);
620
-
621
-        foreach ($di as $file) {
622
-            $filename = $file->getFilename();
623
-
624
-            // Skip hidden files.
625
-            if (substr($filename, 0, 1) === '.') {
626
-                continue;
627
-            }
628
-
629
-            // We are only interested in PHP and sniff files.
630
-            $fileParts = explode('.', $filename);
631
-            if (array_pop($fileParts) !== 'php') {
632
-                continue;
633
-            }
634
-
635
-            $basename = basename($filename, '.php');
636
-            if (substr($basename, -5) !== 'Sniff') {
637
-                continue;
638
-            }
639
-
640
-            $path = $file->getPathname();
641
-
642
-            // Skip files in hidden directories within the Sniffs directory of this
643
-            // standard. We use the offset with strpos() to allow hidden directories
644
-            // before, valid example:
645
-            // /home/foo/.composer/vendor/squiz/custom_tool/MyStandard/Sniffs/...
646
-            if (strpos($path, DIRECTORY_SEPARATOR.'.', $dirLen) !== false) {
647
-                continue;
648
-            }
649
-
650
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
651
-                echo str_repeat("\t", $depth);
652
-                echo "\t\t=> ".Util\Common::stripBasepath($path, $this->config->basepath).PHP_EOL;
653
-            }
654
-
655
-            $sniffs[] = $path;
656
-        }//end foreach
657
-
658
-        return $sniffs;
659
-
660
-    }//end expandSniffDirectory()
661
-
662
-
663
-    /**
664
-     * Expands a ruleset reference into a list of sniff files.
665
-     *
666
-     * @param string $ref        The reference from the ruleset XML file.
667
-     * @param string $rulesetDir The directory of the ruleset XML file, used to
668
-     *                           evaluate relative paths.
669
-     * @param int    $depth      How many nested processing steps we are in. This
670
-     *                           is only used for debug output.
671
-     *
672
-     * @return array
673
-     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the reference is invalid.
674
-     */
675
-    private function expandRulesetReference($ref, $rulesetDir, $depth=0)
676
-    {
677
-        // Ignore internal sniffs codes as they are used to only
678
-        // hide and change internal messages.
679
-        if (substr($ref, 0, 9) === 'Internal.') {
680
-            if (PHP_CODESNIFFER_VERBOSITY > 1) {
681
-                echo str_repeat("\t", $depth);
682
-                echo "\t\t* ignoring internal sniff code *".PHP_EOL;
683
-            }
684
-
685
-            return [];
686
-        }
687
-
688
-        // As sniffs can't begin with a full stop, assume references in
689
-        // this format are relative paths and attempt to convert them
690
-        // to absolute paths. If this fails, let the reference run through
691
-        // the normal checks and have it fail as normal.
692
-        if (substr($ref, 0, 1) === '.') {
693
-            $realpath = Util\Common::realpath($rulesetDir.'/'.$ref);
694
-            if ($realpath !== false) {
695
-                $ref = $realpath;
696
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
697
-                    echo str_repeat("\t", $depth);
698
-                    echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
699
-                }
700
-            }
701
-        }
702
-
703
-        // As sniffs can't begin with a tilde, assume references in
704
-        // this format are relative to the user's home directory.
705
-        if (substr($ref, 0, 2) === '~/') {
706
-            $realpath = Util\Common::realpath($ref);
707
-            if ($realpath !== false) {
708
-                $ref = $realpath;
709
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
710
-                    echo str_repeat("\t", $depth);
711
-                    echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
712
-                }
713
-            }
714
-        }
715
-
716
-        if (is_file($ref) === true) {
717
-            if (substr($ref, -9) === 'Sniff.php') {
718
-                // A single external sniff.
719
-                $this->rulesetDirs[] = dirname(dirname(dirname($ref)));
720
-                return [$ref];
721
-            }
722
-        } else {
723
-            // See if this is a whole standard being referenced.
724
-            $path = Util\Standards::getInstalledStandardPath($ref);
725
-            if (Util\Common::isPharFile($path) === true && strpos($path, 'ruleset.xml') === false) {
726
-                // If the ruleset exists inside the phar file, use it.
727
-                if (file_exists($path.DIRECTORY_SEPARATOR.'ruleset.xml') === true) {
728
-                    $path .= DIRECTORY_SEPARATOR.'ruleset.xml';
729
-                } else {
730
-                    $path = null;
731
-                }
732
-            }
733
-
734
-            if ($path !== null) {
735
-                $ref = $path;
736
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
737
-                    echo str_repeat("\t", $depth);
738
-                    echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
739
-                }
740
-            } else if (is_dir($ref) === false) {
741
-                // Work out the sniff path.
742
-                $sepPos = strpos($ref, DIRECTORY_SEPARATOR);
743
-                if ($sepPos !== false) {
744
-                    $stdName = substr($ref, 0, $sepPos);
745
-                    $path    = substr($ref, $sepPos);
746
-                } else {
747
-                    $parts   = explode('.', $ref);
748
-                    $stdName = $parts[0];
749
-                    if (count($parts) === 1) {
750
-                        // A whole standard?
751
-                        $path = '';
752
-                    } else if (count($parts) === 2) {
753
-                        // A directory of sniffs?
754
-                        $path = DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$parts[1];
755
-                    } else {
756
-                        // A single sniff?
757
-                        $path = DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$parts[1].DIRECTORY_SEPARATOR.$parts[2].'Sniff.php';
758
-                    }
759
-                }
760
-
761
-                $newRef  = false;
762
-                $stdPath = Util\Standards::getInstalledStandardPath($stdName);
763
-                if ($stdPath !== null && $path !== '') {
764
-                    if (Util\Common::isPharFile($stdPath) === true
765
-                        && strpos($stdPath, 'ruleset.xml') === false
766
-                    ) {
767
-                        // Phar files can only return the directory,
768
-                        // since ruleset can be omitted if building one standard.
769
-                        $newRef = Util\Common::realpath($stdPath.$path);
770
-                    } else {
771
-                        $newRef = Util\Common::realpath(dirname($stdPath).$path);
772
-                    }
773
-                }
774
-
775
-                if ($newRef === false) {
776
-                    // The sniff is not locally installed, so check if it is being
777
-                    // referenced as a remote sniff outside the install. We do this
778
-                    // by looking through all directories where we have found ruleset
779
-                    // files before, looking for ones for this particular standard,
780
-                    // and seeing if it is in there.
781
-                    foreach ($this->rulesetDirs as $dir) {
782
-                        if (strtolower(basename($dir)) !== strtolower($stdName)) {
783
-                            continue;
784
-                        }
785
-
786
-                        $newRef = Util\Common::realpath($dir.$path);
787
-
788
-                        if ($newRef !== false) {
789
-                            $ref = $newRef;
790
-                        }
791
-                    }
792
-                } else {
793
-                    $ref = $newRef;
794
-                }
795
-
796
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
797
-                    echo str_repeat("\t", $depth);
798
-                    echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
799
-                }
800
-            }//end if
801
-        }//end if
802
-
803
-        if (is_dir($ref) === true) {
804
-            if (is_file($ref.DIRECTORY_SEPARATOR.'ruleset.xml') === true) {
805
-                // We are referencing an external coding standard.
806
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
807
-                    echo str_repeat("\t", $depth);
808
-                    echo "\t\t* rule is referencing a standard using directory name; processing *".PHP_EOL;
809
-                }
810
-
811
-                return $this->processRuleset($ref.DIRECTORY_SEPARATOR.'ruleset.xml', ($depth + 2));
812
-            } else {
813
-                // We are referencing a whole directory of sniffs.
814
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
815
-                    echo str_repeat("\t", $depth);
816
-                    echo "\t\t* rule is referencing a directory of sniffs *".PHP_EOL;
817
-                    echo str_repeat("\t", $depth);
818
-                    echo "\t\tAdding sniff files from directory".PHP_EOL;
819
-                }
820
-
821
-                return $this->expandSniffDirectory($ref, ($depth + 1));
822
-            }
823
-        } else {
824
-            if (is_file($ref) === false) {
825
-                $error = "Referenced sniff \"$ref\" does not exist";
826
-                throw new RuntimeException($error);
827
-            }
828
-
829
-            if (substr($ref, -9) === 'Sniff.php') {
830
-                // A single sniff.
831
-                return [$ref];
832
-            } else {
833
-                // Assume an external ruleset.xml file.
834
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
835
-                    echo str_repeat("\t", $depth);
836
-                    echo "\t\t* rule is referencing a standard using ruleset path; processing *".PHP_EOL;
837
-                }
838
-
839
-                return $this->processRuleset($ref, ($depth + 2));
840
-            }
841
-        }//end if
842
-
843
-    }//end expandRulesetReference()
844
-
845
-
846
-    /**
847
-     * Processes a rule from a ruleset XML file, overriding built-in defaults.
848
-     *
849
-     * @param \SimpleXMLElement $rule      The rule object from a ruleset XML file.
850
-     * @param string[]          $newSniffs An array of sniffs that got included by this rule.
851
-     * @param int               $depth     How many nested processing steps we are in.
852
-     *                                     This is only used for debug output.
853
-     *
854
-     * @return void
855
-     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If rule settings are invalid.
856
-     */
857
-    private function processRule($rule, $newSniffs, $depth=0)
858
-    {
859
-        $ref  = (string) $rule['ref'];
860
-        $todo = [$ref];
861
-
862
-        $parts = explode('.', $ref);
863
-        if (count($parts) <= 2) {
864
-            // We are processing a standard or a category of sniffs.
865
-            foreach ($newSniffs as $sniffFile) {
866
-                $parts         = explode(DIRECTORY_SEPARATOR, $sniffFile);
867
-                $sniffName     = array_pop($parts);
868
-                $sniffCategory = array_pop($parts);
869
-                array_pop($parts);
870
-                $sniffStandard = array_pop($parts);
871
-                $todo[]        = $sniffStandard.'.'.$sniffCategory.'.'.substr($sniffName, 0, -9);
872
-            }
873
-        }
874
-
875
-        foreach ($todo as $code) {
876
-            // Custom severity.
877
-            if (isset($rule->severity) === true
878
-                && $this->shouldProcessElement($rule->severity) === true
879
-            ) {
880
-                if (isset($this->ruleset[$code]) === false) {
881
-                    $this->ruleset[$code] = [];
882
-                }
883
-
884
-                $this->ruleset[$code]['severity'] = (int) $rule->severity;
885
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
886
-                    echo str_repeat("\t", $depth);
887
-                    echo "\t\t=> severity set to ".(int) $rule->severity;
888
-                    if ($code !== $ref) {
889
-                        echo " for $code";
890
-                    }
891
-
892
-                    echo PHP_EOL;
893
-                }
894
-            }
895
-
896
-            // Custom message type.
897
-            if (isset($rule->type) === true
898
-                && $this->shouldProcessElement($rule->type) === true
899
-            ) {
900
-                if (isset($this->ruleset[$code]) === false) {
901
-                    $this->ruleset[$code] = [];
902
-                }
903
-
904
-                $type = strtolower((string) $rule->type);
905
-                if ($type !== 'error' && $type !== 'warning') {
906
-                    throw new RuntimeException("Message type \"$type\" is invalid; must be \"error\" or \"warning\"");
907
-                }
908
-
909
-                $this->ruleset[$code]['type'] = $type;
910
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
911
-                    echo str_repeat("\t", $depth);
912
-                    echo "\t\t=> message type set to ".(string) $rule->type;
913
-                    if ($code !== $ref) {
914
-                        echo " for $code";
915
-                    }
916
-
917
-                    echo PHP_EOL;
918
-                }
919
-            }//end if
920
-
921
-            // Custom message.
922
-            if (isset($rule->message) === true
923
-                && $this->shouldProcessElement($rule->message) === true
924
-            ) {
925
-                if (isset($this->ruleset[$code]) === false) {
926
-                    $this->ruleset[$code] = [];
927
-                }
928
-
929
-                $this->ruleset[$code]['message'] = (string) $rule->message;
930
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
931
-                    echo str_repeat("\t", $depth);
932
-                    echo "\t\t=> message set to ".(string) $rule->message;
933
-                    if ($code !== $ref) {
934
-                        echo " for $code";
935
-                    }
936
-
937
-                    echo PHP_EOL;
938
-                }
939
-            }
940
-
941
-            // Custom properties.
942
-            if (isset($rule->properties) === true
943
-                && $this->shouldProcessElement($rule->properties) === true
944
-            ) {
945
-                foreach ($rule->properties->property as $prop) {
946
-                    if ($this->shouldProcessElement($prop) === false) {
947
-                        continue;
948
-                    }
949
-
950
-                    if (isset($this->ruleset[$code]) === false) {
951
-                        $this->ruleset[$code] = [
952
-                            'properties' => [],
953
-                        ];
954
-                    } else if (isset($this->ruleset[$code]['properties']) === false) {
955
-                        $this->ruleset[$code]['properties'] = [];
956
-                    }
957
-
958
-                    $name = (string) $prop['name'];
959
-                    if (isset($prop['type']) === true
960
-                        && (string) $prop['type'] === 'array'
961
-                    ) {
962
-                        $values = [];
963
-                        if (isset($prop['extend']) === true
964
-                            && (string) $prop['extend'] === 'true'
965
-                            && isset($this->ruleset[$code]['properties'][$name]) === true
966
-                        ) {
967
-                            $values = $this->ruleset[$code]['properties'][$name];
968
-                        }
969
-
970
-                        if (isset($prop->element) === true) {
971
-                            $printValue = '';
972
-                            foreach ($prop->element as $element) {
973
-                                if ($this->shouldProcessElement($element) === false) {
974
-                                    continue;
975
-                                }
976
-
977
-                                $value = (string) $element['value'];
978
-                                if (isset($element['key']) === true) {
979
-                                    $key          = (string) $element['key'];
980
-                                    $values[$key] = $value;
981
-                                    $printValue  .= $key.'=>'.$value.',';
982
-                                } else {
983
-                                    $values[]    = $value;
984
-                                    $printValue .= $value.',';
985
-                                }
986
-                            }
987
-
988
-                            $printValue = rtrim($printValue, ',');
989
-                        } else {
990
-                            $value      = (string) $prop['value'];
991
-                            $printValue = $value;
992
-                            foreach (explode(',', $value) as $val) {
993
-                                list($k, $v) = explode('=>', $val.'=>');
994
-                                if ($v !== '') {
995
-                                    $values[trim($k)] = trim($v);
996
-                                } else {
997
-                                    $values[] = trim($k);
998
-                                }
999
-                            }
1000
-                        }//end if
1001
-
1002
-                        $this->ruleset[$code]['properties'][$name] = $values;
1003
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1004
-                            echo str_repeat("\t", $depth);
1005
-                            echo "\t\t=> array property \"$name\" set to \"$printValue\"";
1006
-                            if ($code !== $ref) {
1007
-                                echo " for $code";
1008
-                            }
1009
-
1010
-                            echo PHP_EOL;
1011
-                        }
1012
-                    } else {
1013
-                        $this->ruleset[$code]['properties'][$name] = (string) $prop['value'];
1014
-                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
1015
-                            echo str_repeat("\t", $depth);
1016
-                            echo "\t\t=> property \"$name\" set to \"".(string) $prop['value'].'"';
1017
-                            if ($code !== $ref) {
1018
-                                echo " for $code";
1019
-                            }
1020
-
1021
-                            echo PHP_EOL;
1022
-                        }
1023
-                    }//end if
1024
-                }//end foreach
1025
-            }//end if
1026
-
1027
-            // Ignore patterns.
1028
-            foreach ($rule->{'exclude-pattern'} as $pattern) {
1029
-                if ($this->shouldProcessElement($pattern) === false) {
1030
-                    continue;
1031
-                }
1032
-
1033
-                if (isset($this->ignorePatterns[$code]) === false) {
1034
-                    $this->ignorePatterns[$code] = [];
1035
-                }
1036
-
1037
-                if (isset($pattern['type']) === false) {
1038
-                    $pattern['type'] = 'absolute';
1039
-                }
1040
-
1041
-                $this->ignorePatterns[$code][(string) $pattern] = (string) $pattern['type'];
1042
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1043
-                    echo str_repeat("\t", $depth);
1044
-                    echo "\t\t=> added rule-specific ".(string) $pattern['type'].' ignore pattern';
1045
-                    if ($code !== $ref) {
1046
-                        echo " for $code";
1047
-                    }
1048
-
1049
-                    echo ': '.(string) $pattern.PHP_EOL;
1050
-                }
1051
-            }//end foreach
1052
-
1053
-            // Include patterns.
1054
-            foreach ($rule->{'include-pattern'} as $pattern) {
1055
-                if ($this->shouldProcessElement($pattern) === false) {
1056
-                    continue;
1057
-                }
1058
-
1059
-                if (isset($this->includePatterns[$code]) === false) {
1060
-                    $this->includePatterns[$code] = [];
1061
-                }
1062
-
1063
-                if (isset($pattern['type']) === false) {
1064
-                    $pattern['type'] = 'absolute';
1065
-                }
1066
-
1067
-                $this->includePatterns[$code][(string) $pattern] = (string) $pattern['type'];
1068
-                if (PHP_CODESNIFFER_VERBOSITY > 1) {
1069
-                    echo str_repeat("\t", $depth);
1070
-                    echo "\t\t=> added rule-specific ".(string) $pattern['type'].' include pattern';
1071
-                    if ($code !== $ref) {
1072
-                        echo " for $code";
1073
-                    }
1074
-
1075
-                    echo ': '.(string) $pattern.PHP_EOL;
1076
-                }
1077
-            }//end foreach
1078
-        }//end foreach
1079
-
1080
-    }//end processRule()
1081
-
1082
-
1083
-    /**
1084
-     * Determine if an element should be processed or ignored.
1085
-     *
1086
-     * @param \SimpleXMLElement $element An object from a ruleset XML file.
1087
-     *
1088
-     * @return bool
1089
-     */
1090
-    private function shouldProcessElement($element)
1091
-    {
1092
-        if (isset($element['phpcbf-only']) === false
1093
-            && isset($element['phpcs-only']) === false
1094
-        ) {
1095
-            // No exceptions are being made.
1096
-            return true;
1097
-        }
1098
-
1099
-        if (PHP_CODESNIFFER_CBF === true
1100
-            && isset($element['phpcbf-only']) === true
1101
-            && (string) $element['phpcbf-only'] === 'true'
1102
-        ) {
1103
-            return true;
1104
-        }
1105
-
1106
-        if (PHP_CODESNIFFER_CBF === false
1107
-            && isset($element['phpcs-only']) === true
1108
-            && (string) $element['phpcs-only'] === 'true'
1109
-        ) {
1110
-            return true;
1111
-        }
1112
-
1113
-        return false;
1114
-
1115
-    }//end shouldProcessElement()
1116
-
1117
-
1118
-    /**
1119
-     * Loads and stores sniffs objects used for sniffing files.
1120
-     *
1121
-     * @param array $files        Paths to the sniff files to register.
1122
-     * @param array $restrictions The sniff class names to restrict the allowed
1123
-     *                            listeners to.
1124
-     * @param array $exclusions   The sniff class names to exclude from the
1125
-     *                            listeners list.
1126
-     *
1127
-     * @return void
1128
-     */
1129
-    public function registerSniffs($files, $restrictions, $exclusions)
1130
-    {
1131
-        $listeners = [];
1132
-
1133
-        foreach ($files as $file) {
1134
-            // Work out where the position of /StandardName/Sniffs/... is
1135
-            // so we can determine what the class will be called.
1136
-            $sniffPos = strrpos($file, DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR);
1137
-            if ($sniffPos === false) {
1138
-                continue;
1139
-            }
1140
-
1141
-            $slashPos = strrpos(substr($file, 0, $sniffPos), DIRECTORY_SEPARATOR);
1142
-            if ($slashPos === false) {
1143
-                continue;
1144
-            }
1145
-
1146
-            $className   = Autoload::loadFile($file);
1147
-            $compareName = Util\Common::cleanSniffClass($className);
1148
-
1149
-            // If they have specified a list of sniffs to restrict to, check
1150
-            // to see if this sniff is allowed.
1151
-            if (empty($restrictions) === false
1152
-                && isset($restrictions[$compareName]) === false
1153
-            ) {
1154
-                continue;
1155
-            }
1156
-
1157
-            // If they have specified a list of sniffs to exclude, check
1158
-            // to see if this sniff is allowed.
1159
-            if (empty($exclusions) === false
1160
-                && isset($exclusions[$compareName]) === true
1161
-            ) {
1162
-                continue;
1163
-            }
1164
-
1165
-            // Skip abstract classes.
1166
-            $reflection = new \ReflectionClass($className);
1167
-            if ($reflection->isAbstract() === true) {
1168
-                continue;
1169
-            }
1170
-
1171
-            $listeners[$className] = $className;
1172
-
1173
-            if (PHP_CODESNIFFER_VERBOSITY > 2) {
1174
-                echo "Registered $className".PHP_EOL;
1175
-            }
1176
-        }//end foreach
1177
-
1178
-        $this->sniffs = $listeners;
1179
-
1180
-    }//end registerSniffs()
1181
-
1182
-
1183
-    /**
1184
-     * Populates the array of PHP_CodeSniffer_Sniff's for this file.
1185
-     *
1186
-     * @return void
1187
-     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If sniff registration fails.
1188
-     */
1189
-    public function populateTokenListeners()
1190
-    {
1191
-        // Construct a list of listeners indexed by token being listened for.
1192
-        $this->tokenListeners = [];
1193
-
1194
-        foreach ($this->sniffs as $sniffClass => $sniffObject) {
1195
-            $this->sniffs[$sniffClass] = null;
1196
-            $this->sniffs[$sniffClass] = new $sniffClass();
1197
-
1198
-            $sniffCode = Util\Common::getSniffCode($sniffClass);
1199
-            $this->sniffCodes[$sniffCode] = $sniffClass;
1200
-
1201
-            // Set custom properties.
1202
-            if (isset($this->ruleset[$sniffCode]['properties']) === true) {
1203
-                foreach ($this->ruleset[$sniffCode]['properties'] as $name => $value) {
1204
-                    $this->setSniffProperty($sniffClass, $name, $value);
1205
-                }
1206
-            }
1207
-
1208
-            $tokenizers = [];
1209
-            $vars       = get_class_vars($sniffClass);
1210
-            if (isset($vars['supportedTokenizers']) === true) {
1211
-                foreach ($vars['supportedTokenizers'] as $tokenizer) {
1212
-                    $tokenizers[$tokenizer] = $tokenizer;
1213
-                }
1214
-            } else {
1215
-                $tokenizers = ['PHP' => 'PHP'];
1216
-            }
1217
-
1218
-            $tokens = $this->sniffs[$sniffClass]->register();
1219
-            if (is_array($tokens) === false) {
1220
-                $msg = "Sniff $sniffClass register() method must return an array";
1221
-                throw new RuntimeException($msg);
1222
-            }
1223
-
1224
-            $ignorePatterns = [];
1225
-            $patterns       = $this->getIgnorePatterns($sniffCode);
1226
-            foreach ($patterns as $pattern => $type) {
1227
-                $replacements = [
1228
-                    '\\,' => ',',
1229
-                    '*'   => '.*',
1230
-                ];
1231
-
1232
-                $ignorePatterns[] = strtr($pattern, $replacements);
1233
-            }
1234
-
1235
-            $includePatterns = [];
1236
-            $patterns        = $this->getIncludePatterns($sniffCode);
1237
-            foreach ($patterns as $pattern => $type) {
1238
-                $replacements = [
1239
-                    '\\,' => ',',
1240
-                    '*'   => '.*',
1241
-                ];
1242
-
1243
-                $includePatterns[] = strtr($pattern, $replacements);
1244
-            }
1245
-
1246
-            foreach ($tokens as $token) {
1247
-                if (isset($this->tokenListeners[$token]) === false) {
1248
-                    $this->tokenListeners[$token] = [];
1249
-                }
1250
-
1251
-                if (isset($this->tokenListeners[$token][$sniffClass]) === false) {
1252
-                    $this->tokenListeners[$token][$sniffClass] = [
1253
-                        'class'      => $sniffClass,
1254
-                        'source'     => $sniffCode,
1255
-                        'tokenizers' => $tokenizers,
1256
-                        'ignore'     => $ignorePatterns,
1257
-                        'include'    => $includePatterns,
1258
-                    ];
1259
-                }
1260
-            }
1261
-        }//end foreach
1262
-
1263
-    }//end populateTokenListeners()
1264
-
1265
-
1266
-    /**
1267
-     * Set a single property for a sniff.
1268
-     *
1269
-     * @param string $sniffClass The class name of the sniff.
1270
-     * @param string $name       The name of the property to change.
1271
-     * @param string $value      The new value of the property.
1272
-     *
1273
-     * @return void
1274
-     */
1275
-    public function setSniffProperty($sniffClass, $name, $value)
1276
-    {
1277
-        // Setting a property for a sniff we are not using.
1278
-        if (isset($this->sniffs[$sniffClass]) === false) {
1279
-            return;
1280
-        }
1281
-
1282
-        $name = trim($name);
1283
-        if (is_string($value) === true) {
1284
-            $value = trim($value);
1285
-        }
1286
-
1287
-        if ($value === '') {
1288
-            $value = null;
1289
-        }
1290
-
1291
-        // Special case for booleans.
1292
-        if ($value === 'true') {
1293
-            $value = true;
1294
-        } else if ($value === 'false') {
1295
-            $value = false;
1296
-        } else if (substr($name, -2) === '[]') {
1297
-            $name   = substr($name, 0, -2);
1298
-            $values = [];
1299
-            if ($value !== null) {
1300
-                foreach (explode(',', $value) as $val) {
1301
-                    list($k, $v) = explode('=>', $val.'=>');
1302
-                    if ($v !== '') {
1303
-                        $values[trim($k)] = trim($v);
1304
-                    } else {
1305
-                        $values[] = trim($k);
1306
-                    }
1307
-                }
1308
-            }
1309
-
1310
-            $value = $values;
1311
-        }
1312
-
1313
-        $this->sniffs[$sniffClass]->$name = $value;
1314
-
1315
-    }//end setSniffProperty()
1316
-
1317
-
1318
-    /**
1319
-     * Gets the array of ignore patterns.
1320
-     *
1321
-     * Optionally takes a listener to get ignore patterns specified
1322
-     * for that sniff only.
1323
-     *
1324
-     * @param string $listener The listener to get patterns for. If NULL, all
1325
-     *                         patterns are returned.
1326
-     *
1327
-     * @return array
1328
-     */
1329
-    public function getIgnorePatterns($listener=null)
1330
-    {
1331
-        if ($listener === null) {
1332
-            return $this->ignorePatterns;
1333
-        }
1334
-
1335
-        if (isset($this->ignorePatterns[$listener]) === true) {
1336
-            return $this->ignorePatterns[$listener];
1337
-        }
1338
-
1339
-        return [];
1340
-
1341
-    }//end getIgnorePatterns()
1342
-
1343
-
1344
-    /**
1345
-     * Gets the array of include patterns.
1346
-     *
1347
-     * Optionally takes a listener to get include patterns specified
1348
-     * for that sniff only.
1349
-     *
1350
-     * @param string $listener The listener to get patterns for. If NULL, all
1351
-     *                         patterns are returned.
1352
-     *
1353
-     * @return array
1354
-     */
1355
-    public function getIncludePatterns($listener=null)
1356
-    {
1357
-        if ($listener === null) {
1358
-            return $this->includePatterns;
1359
-        }
1360
-
1361
-        if (isset($this->includePatterns[$listener]) === true) {
1362
-            return $this->includePatterns[$listener];
1363
-        }
1364
-
1365
-        return [];
1366
-
1367
-    }//end getIncludePatterns()
20
+	/**
21
+	 * The name of the coding standard being used.
22
+	 *
23
+	 * If a top-level standard includes other standards, or sniffs
24
+	 * from other standards, only the name of the top-level standard
25
+	 * will be stored in here.
26
+	 *
27
+	 * If multiple top-level standards are being loaded into
28
+	 * a single ruleset object, this will store a comma separated list
29
+	 * of the top-level standard names.
30
+	 *
31
+	 * @var string
32
+	 */
33
+	public $name = '';
34
+
35
+	/**
36
+	 * A list of file paths for the ruleset files being used.
37
+	 *
38
+	 * @var string[]
39
+	 */
40
+	public $paths = [];
41
+
42
+	/**
43
+	 * A list of regular expressions used to ignore specific sniffs for files and folders.
44
+	 *
45
+	 * Is also used to set global exclude patterns.
46
+	 * The key is the regular expression and the value is the type
47
+	 * of ignore pattern (absolute or relative).
48
+	 *
49
+	 * @var array<string, string>
50
+	 */
51
+	public $ignorePatterns = [];
52
+
53
+	/**
54
+	 * A list of regular expressions used to include specific sniffs for files and folders.
55
+	 *
56
+	 * The key is the sniff code and the value is an array with
57
+	 * the key being a regular expression and the value is the type
58
+	 * of ignore pattern (absolute or relative).
59
+	 *
60
+	 * @var array<string, array<string, string>>
61
+	 */
62
+	public $includePatterns = [];
63
+
64
+	/**
65
+	 * An array of sniff objects that are being used to check files.
66
+	 *
67
+	 * The key is the fully qualified name of the sniff class
68
+	 * and the value is the sniff object.
69
+	 *
70
+	 * @var array<string, \PHP_CodeSniffer\Sniffs\Sniff>
71
+	 */
72
+	public $sniffs = [];
73
+
74
+	/**
75
+	 * A mapping of sniff codes to fully qualified class names.
76
+	 *
77
+	 * The key is the sniff code and the value
78
+	 * is the fully qualified name of the sniff class.
79
+	 *
80
+	 * @var array<string, string>
81
+	 */
82
+	public $sniffCodes = [];
83
+
84
+	/**
85
+	 * An array of token types and the sniffs that are listening for them.
86
+	 *
87
+	 * The key is the token name being listened for and the value
88
+	 * is the sniff object.
89
+	 *
90
+	 * @var array<int, \PHP_CodeSniffer\Sniffs\Sniff>
91
+	 */
92
+	public $tokenListeners = [];
93
+
94
+	/**
95
+	 * An array of rules from the ruleset.xml file.
96
+	 *
97
+	 * It may be empty, indicating that the ruleset does not override
98
+	 * any of the default sniff settings.
99
+	 *
100
+	 * @var array<string, mixed>
101
+	 */
102
+	public $ruleset = [];
103
+
104
+	/**
105
+	 * The directories that the processed rulesets are in.
106
+	 *
107
+	 * @var string[]
108
+	 */
109
+	protected $rulesetDirs = [];
110
+
111
+	/**
112
+	 * The config data for the run.
113
+	 *
114
+	 * @var \PHP_CodeSniffer\Config
115
+	 */
116
+	private $config = null;
117
+
118
+
119
+	/**
120
+	 * Initialise the ruleset that the run will use.
121
+	 *
122
+	 * @param \PHP_CodeSniffer\Config $config The config data for the run.
123
+	 *
124
+	 * @return void
125
+	 */
126
+	public function __construct(Config $config)
127
+	{
128
+		// Ignore sniff restrictions if caching is on.
129
+		$restrictions = [];
130
+		$exclusions   = [];
131
+		if ($config->cache === false) {
132
+			$restrictions = $config->sniffs;
133
+			$exclusions   = $config->exclude;
134
+		}
135
+
136
+		$this->config = $config;
137
+		$sniffs       = [];
138
+
139
+		$standardPaths = [];
140
+		foreach ($config->standards as $standard) {
141
+			$installed = Util\Standards::getInstalledStandardPath($standard);
142
+			if ($installed === null) {
143
+				$standard = Util\Common::realpath($standard);
144
+				if (is_dir($standard) === true
145
+					&& is_file(Util\Common::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml')) === true
146
+				) {
147
+					$standard = Util\Common::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml');
148
+				}
149
+			} else {
150
+				$standard = $installed;
151
+			}
152
+
153
+			$standardPaths[] = $standard;
154
+		}
155
+
156
+		foreach ($standardPaths as $standard) {
157
+			$ruleset = @simplexml_load_string(file_get_contents($standard));
158
+			if ($ruleset !== false) {
159
+				$standardName = (string) $ruleset['name'];
160
+				if ($this->name !== '') {
161
+					$this->name .= ', ';
162
+				}
163
+
164
+				$this->name .= $standardName;
165
+
166
+				// Allow autoloading of custom files inside this standard.
167
+				if (isset($ruleset['namespace']) === true) {
168
+					$namespace = (string) $ruleset['namespace'];
169
+				} else {
170
+					$namespace = basename(dirname($standard));
171
+				}
172
+
173
+				Autoload::addSearchPath(dirname($standard), $namespace);
174
+			}
175
+
176
+			if (defined('PHP_CODESNIFFER_IN_TESTS') === true && empty($restrictions) === false) {
177
+				// In unit tests, only register the sniffs that the test wants and not the entire standard.
178
+				try {
179
+					foreach ($restrictions as $restriction) {
180
+						$sniffs = array_merge($sniffs, $this->expandRulesetReference($restriction, dirname($standard)));
181
+					}
182
+				} catch (RuntimeException $e) {
183
+					// Sniff reference could not be expanded, which probably means this
184
+					// is an installed standard. Let the unit test system take care of
185
+					// setting the correct sniff for testing.
186
+					return;
187
+				}
188
+
189
+				break;
190
+			}
191
+
192
+			if (PHP_CODESNIFFER_VERBOSITY === 1) {
193
+				echo "Registering sniffs in the $standardName standard... ";
194
+				if (count($config->standards) > 1 || PHP_CODESNIFFER_VERBOSITY > 2) {
195
+					echo PHP_EOL;
196
+				}
197
+			}
198
+
199
+			$sniffs = array_merge($sniffs, $this->processRuleset($standard));
200
+		}//end foreach
201
+
202
+		$sniffRestrictions = [];
203
+		foreach ($restrictions as $sniffCode) {
204
+			$parts     = explode('.', strtolower($sniffCode));
205
+			$sniffName = $parts[0].'\sniffs\\'.$parts[1].'\\'.$parts[2].'sniff';
206
+			$sniffRestrictions[$sniffName] = true;
207
+		}
208
+
209
+		$sniffExclusions = [];
210
+		foreach ($exclusions as $sniffCode) {
211
+			$parts     = explode('.', strtolower($sniffCode));
212
+			$sniffName = $parts[0].'\sniffs\\'.$parts[1].'\\'.$parts[2].'sniff';
213
+			$sniffExclusions[$sniffName] = true;
214
+		}
215
+
216
+		$this->registerSniffs($sniffs, $sniffRestrictions, $sniffExclusions);
217
+		$this->populateTokenListeners();
218
+
219
+		$numSniffs = count($this->sniffs);
220
+		if (PHP_CODESNIFFER_VERBOSITY === 1) {
221
+			echo "DONE ($numSniffs sniffs registered)".PHP_EOL;
222
+		}
223
+
224
+		if ($numSniffs === 0) {
225
+			throw new RuntimeException('No sniffs were registered');
226
+		}
227
+
228
+	}//end __construct()
229
+
230
+
231
+	/**
232
+	 * Prints a report showing the sniffs contained in a standard.
233
+	 *
234
+	 * @return void
235
+	 */
236
+	public function explain()
237
+	{
238
+		$sniffs = array_keys($this->sniffCodes);
239
+		sort($sniffs);
240
+
241
+		ob_start();
242
+
243
+		$lastStandard = null;
244
+		$lastCount    = '';
245
+		$sniffCount   = count($sniffs);
246
+
247
+		// Add a dummy entry to the end so we loop
248
+		// one last time and clear the output buffer.
249
+		$sniffs[] = '';
250
+
251
+		echo PHP_EOL."The $this->name standard contains $sniffCount sniffs".PHP_EOL;
252
+
253
+		ob_start();
254
+
255
+		foreach ($sniffs as $i => $sniff) {
256
+			if ($i === $sniffCount) {
257
+				$currentStandard = null;
258
+			} else {
259
+				$currentStandard = substr($sniff, 0, strpos($sniff, '.'));
260
+				if ($lastStandard === null) {
261
+					$lastStandard = $currentStandard;
262
+				}
263
+			}
264
+
265
+			if ($currentStandard !== $lastStandard) {
266
+				$sniffList = ob_get_contents();
267
+				ob_end_clean();
268
+
269
+				echo PHP_EOL.$lastStandard.' ('.$lastCount.' sniff';
270
+				if ($lastCount > 1) {
271
+					echo 's';
272
+				}
273
+
274
+				echo ')'.PHP_EOL;
275
+				echo str_repeat('-', (strlen($lastStandard.$lastCount) + 10));
276
+				echo PHP_EOL;
277
+				echo $sniffList;
278
+
279
+				$lastStandard = $currentStandard;
280
+				$lastCount    = 0;
281
+
282
+				if ($currentStandard === null) {
283
+					break;
284
+				}
285
+
286
+				ob_start();
287
+			}//end if
288
+
289
+			echo '  '.$sniff.PHP_EOL;
290
+			$lastCount++;
291
+		}//end foreach
292
+
293
+	}//end explain()
294
+
295
+
296
+	/**
297
+	 * Processes a single ruleset and returns a list of the sniffs it represents.
298
+	 *
299
+	 * Rules founds within the ruleset are processed immediately, but sniff classes
300
+	 * are not registered by this method.
301
+	 *
302
+	 * @param string $rulesetPath The path to a ruleset XML file.
303
+	 * @param int    $depth       How many nested processing steps we are in. This
304
+	 *                            is only used for debug output.
305
+	 *
306
+	 * @return string[]
307
+	 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the ruleset path is invalid.
308
+	 */
309
+	public function processRuleset($rulesetPath, $depth=0)
310
+	{
311
+		$rulesetPath = Util\Common::realpath($rulesetPath);
312
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
313
+			echo str_repeat("\t", $depth);
314
+			echo 'Processing ruleset '.Util\Common::stripBasepath($rulesetPath, $this->config->basepath).PHP_EOL;
315
+		}
316
+
317
+		libxml_use_internal_errors(true);
318
+		$ruleset = simplexml_load_string(file_get_contents($rulesetPath));
319
+		if ($ruleset === false) {
320
+			$errorMsg = "Ruleset $rulesetPath is not valid".PHP_EOL;
321
+			$errors   = libxml_get_errors();
322
+			foreach ($errors as $error) {
323
+				$errorMsg .= '- On line '.$error->line.', column '.$error->column.': '.$error->message;
324
+			}
325
+
326
+			libxml_clear_errors();
327
+			throw new RuntimeException($errorMsg);
328
+		}
329
+
330
+		libxml_use_internal_errors(false);
331
+
332
+		$ownSniffs      = [];
333
+		$includedSniffs = [];
334
+		$excludedSniffs = [];
335
+
336
+		$this->paths[]       = $rulesetPath;
337
+		$rulesetDir          = dirname($rulesetPath);
338
+		$this->rulesetDirs[] = $rulesetDir;
339
+
340
+		$sniffDir = $rulesetDir.DIRECTORY_SEPARATOR.'Sniffs';
341
+		if (is_dir($sniffDir) === true) {
342
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
343
+				echo str_repeat("\t", $depth);
344
+				echo "\tAdding sniff files from ".Util\Common::stripBasepath($sniffDir, $this->config->basepath).' directory'.PHP_EOL;
345
+			}
346
+
347
+			$ownSniffs = $this->expandSniffDirectory($sniffDir, $depth);
348
+		}
349
+
350
+		// Included custom autoloaders.
351
+		foreach ($ruleset->{'autoload'} as $autoload) {
352
+			if ($this->shouldProcessElement($autoload) === false) {
353
+				continue;
354
+			}
355
+
356
+			$autoloadPath = (string) $autoload;
357
+			if (is_file($autoloadPath) === false) {
358
+				$autoloadPath = Util\Common::realPath(dirname($rulesetPath).DIRECTORY_SEPARATOR.$autoloadPath);
359
+			}
360
+
361
+			if ($autoloadPath === false) {
362
+				throw new RuntimeException('The specified autoload file "'.$autoload.'" does not exist');
363
+			}
364
+
365
+			include_once $autoloadPath;
366
+
367
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
368
+				echo str_repeat("\t", $depth);
369
+				echo "\t=> included autoloader $autoloadPath".PHP_EOL;
370
+			}
371
+		}//end foreach
372
+
373
+		// Process custom sniff config settings.
374
+		foreach ($ruleset->{'config'} as $config) {
375
+			if ($this->shouldProcessElement($config) === false) {
376
+				continue;
377
+			}
378
+
379
+			Config::setConfigData((string) $config['name'], (string) $config['value'], true);
380
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
381
+				echo str_repeat("\t", $depth);
382
+				echo "\t=> set config value ".(string) $config['name'].': '.(string) $config['value'].PHP_EOL;
383
+			}
384
+		}
385
+
386
+		foreach ($ruleset->rule as $rule) {
387
+			if (isset($rule['ref']) === false
388
+				|| $this->shouldProcessElement($rule) === false
389
+			) {
390
+				continue;
391
+			}
392
+
393
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
394
+				echo str_repeat("\t", $depth);
395
+				echo "\tProcessing rule \"".$rule['ref'].'"'.PHP_EOL;
396
+			}
397
+
398
+			$expandedSniffs = $this->expandRulesetReference((string) $rule['ref'], $rulesetDir, $depth);
399
+			$newSniffs      = array_diff($expandedSniffs, $includedSniffs);
400
+			$includedSniffs = array_merge($includedSniffs, $expandedSniffs);
401
+
402
+			$parts = explode('.', $rule['ref']);
403
+			if (count($parts) === 4
404
+				&& $parts[0] !== ''
405
+				&& $parts[1] !== ''
406
+				&& $parts[2] !== ''
407
+			) {
408
+				$sniffCode = $parts[0].'.'.$parts[1].'.'.$parts[2];
409
+				if (isset($this->ruleset[$sniffCode]['severity']) === true
410
+					&& $this->ruleset[$sniffCode]['severity'] === 0
411
+				) {
412
+					// This sniff code has already been turned off, but now
413
+					// it is being explicitly included again, so turn it back on.
414
+					$this->ruleset[(string) $rule['ref']]['severity'] = 5;
415
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
416
+						echo str_repeat("\t", $depth);
417
+						echo "\t\t* disabling sniff exclusion for specific message code *".PHP_EOL;
418
+						echo str_repeat("\t", $depth);
419
+						echo "\t\t=> severity set to 5".PHP_EOL;
420
+					}
421
+				} else if (empty($newSniffs) === false) {
422
+					$newSniff = $newSniffs[0];
423
+					if (in_array($newSniff, $ownSniffs, true) === false) {
424
+						// Including a sniff that hasn't been included higher up, but
425
+						// only including a single message from it. So turn off all messages in
426
+						// the sniff, except this one.
427
+						$this->ruleset[$sniffCode]['severity']            = 0;
428
+						$this->ruleset[(string) $rule['ref']]['severity'] = 5;
429
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
430
+							echo str_repeat("\t", $depth);
431
+							echo "\t\tExcluding sniff \"".$sniffCode.'" except for "'.$parts[3].'"'.PHP_EOL;
432
+						}
433
+					}
434
+				}//end if
435
+			}//end if
436
+
437
+			if (isset($rule->exclude) === true) {
438
+				foreach ($rule->exclude as $exclude) {
439
+					if (isset($exclude['name']) === false) {
440
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
441
+							echo str_repeat("\t", $depth);
442
+							echo "\t\t* ignoring empty exclude rule *".PHP_EOL;
443
+							echo "\t\t\t=> ".$exclude->asXML().PHP_EOL;
444
+						}
445
+
446
+						continue;
447
+					}
448
+
449
+					if ($this->shouldProcessElement($exclude) === false) {
450
+						continue;
451
+					}
452
+
453
+					if (PHP_CODESNIFFER_VERBOSITY > 1) {
454
+						echo str_repeat("\t", $depth);
455
+						echo "\t\tExcluding rule \"".$exclude['name'].'"'.PHP_EOL;
456
+					}
457
+
458
+					// Check if a single code is being excluded, which is a shortcut
459
+					// for setting the severity of the message to 0.
460
+					$parts = explode('.', $exclude['name']);
461
+					if (count($parts) === 4) {
462
+						$this->ruleset[(string) $exclude['name']]['severity'] = 0;
463
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
464
+							echo str_repeat("\t", $depth);
465
+							echo "\t\t=> severity set to 0".PHP_EOL;
466
+						}
467
+					} else {
468
+						$excludedSniffs = array_merge(
469
+							$excludedSniffs,
470
+							$this->expandRulesetReference((string) $exclude['name'], $rulesetDir, ($depth + 1))
471
+						);
472
+					}
473
+				}//end foreach
474
+			}//end if
475
+
476
+			$this->processRule($rule, $newSniffs, $depth);
477
+		}//end foreach
478
+
479
+		// Process custom command line arguments.
480
+		$cliArgs = [];
481
+		foreach ($ruleset->{'arg'} as $arg) {
482
+			if ($this->shouldProcessElement($arg) === false) {
483
+				continue;
484
+			}
485
+
486
+			if (isset($arg['name']) === true) {
487
+				$argString = '--'.(string) $arg['name'];
488
+				if (isset($arg['value']) === true) {
489
+					$argString .= '='.(string) $arg['value'];
490
+				}
491
+			} else {
492
+				$argString = '-'.(string) $arg['value'];
493
+			}
494
+
495
+			$cliArgs[] = $argString;
496
+
497
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
498
+				echo str_repeat("\t", $depth);
499
+				echo "\t=> set command line value $argString".PHP_EOL;
500
+			}
501
+		}//end foreach
502
+
503
+		// Set custom php ini values as CLI args.
504
+		foreach ($ruleset->{'ini'} as $arg) {
505
+			if ($this->shouldProcessElement($arg) === false) {
506
+				continue;
507
+			}
508
+
509
+			if (isset($arg['name']) === false) {
510
+				continue;
511
+			}
512
+
513
+			$name      = (string) $arg['name'];
514
+			$argString = $name;
515
+			if (isset($arg['value']) === true) {
516
+				$value      = (string) $arg['value'];
517
+				$argString .= "=$value";
518
+			} else {
519
+				$value = 'true';
520
+			}
521
+
522
+			$cliArgs[] = '-d';
523
+			$cliArgs[] = $argString;
524
+
525
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
526
+				echo str_repeat("\t", $depth);
527
+				echo "\t=> set PHP ini value $name to $value".PHP_EOL;
528
+			}
529
+		}//end foreach
530
+
531
+		if (empty($this->config->files) === true) {
532
+			// Process hard-coded file paths.
533
+			foreach ($ruleset->{'file'} as $file) {
534
+				$file      = (string) $file;
535
+				$cliArgs[] = $file;
536
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
537
+					echo str_repeat("\t", $depth);
538
+					echo "\t=> added \"$file\" to the file list".PHP_EOL;
539
+				}
540
+			}
541
+		}
542
+
543
+		if (empty($cliArgs) === false) {
544
+			// Change the directory so all relative paths are worked
545
+			// out based on the location of the ruleset instead of
546
+			// the location of the user.
547
+			$inPhar = Util\Common::isPharFile($rulesetDir);
548
+			if ($inPhar === false) {
549
+				$currentDir = getcwd();
550
+				chdir($rulesetDir);
551
+			}
552
+
553
+			$this->config->setCommandLineValues($cliArgs);
554
+
555
+			if ($inPhar === false) {
556
+				chdir($currentDir);
557
+			}
558
+		}
559
+
560
+		// Process custom ignore pattern rules.
561
+		foreach ($ruleset->{'exclude-pattern'} as $pattern) {
562
+			if ($this->shouldProcessElement($pattern) === false) {
563
+				continue;
564
+			}
565
+
566
+			if (isset($pattern['type']) === false) {
567
+				$pattern['type'] = 'absolute';
568
+			}
569
+
570
+			$this->ignorePatterns[(string) $pattern] = (string) $pattern['type'];
571
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
572
+				echo str_repeat("\t", $depth);
573
+				echo "\t=> added global ".(string) $pattern['type'].' ignore pattern: '.(string) $pattern.PHP_EOL;
574
+			}
575
+		}
576
+
577
+		$includedSniffs = array_unique(array_merge($ownSniffs, $includedSniffs));
578
+		$excludedSniffs = array_unique($excludedSniffs);
579
+
580
+		if (PHP_CODESNIFFER_VERBOSITY > 1) {
581
+			$included = count($includedSniffs);
582
+			$excluded = count($excludedSniffs);
583
+			echo str_repeat("\t", $depth);
584
+			echo "=> Ruleset processing complete; included $included sniffs and excluded $excluded".PHP_EOL;
585
+		}
586
+
587
+		// Merge our own sniff list with our externally included
588
+		// sniff list, but filter out any excluded sniffs.
589
+		$files = [];
590
+		foreach ($includedSniffs as $sniff) {
591
+			if (in_array($sniff, $excludedSniffs, true) === true) {
592
+				continue;
593
+			} else {
594
+				$files[] = Util\Common::realpath($sniff);
595
+			}
596
+		}
597
+
598
+		return $files;
599
+
600
+	}//end processRuleset()
601
+
602
+
603
+	/**
604
+	 * Expands a directory into a list of sniff files within.
605
+	 *
606
+	 * @param string $directory The path to a directory.
607
+	 * @param int    $depth     How many nested processing steps we are in. This
608
+	 *                          is only used for debug output.
609
+	 *
610
+	 * @return array
611
+	 */
612
+	private function expandSniffDirectory($directory, $depth=0)
613
+	{
614
+		$sniffs = [];
615
+
616
+		$rdi = new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS);
617
+		$di  = new \RecursiveIteratorIterator($rdi, 0, \RecursiveIteratorIterator::CATCH_GET_CHILD);
618
+
619
+		$dirLen = strlen($directory);
620
+
621
+		foreach ($di as $file) {
622
+			$filename = $file->getFilename();
623
+
624
+			// Skip hidden files.
625
+			if (substr($filename, 0, 1) === '.') {
626
+				continue;
627
+			}
628
+
629
+			// We are only interested in PHP and sniff files.
630
+			$fileParts = explode('.', $filename);
631
+			if (array_pop($fileParts) !== 'php') {
632
+				continue;
633
+			}
634
+
635
+			$basename = basename($filename, '.php');
636
+			if (substr($basename, -5) !== 'Sniff') {
637
+				continue;
638
+			}
639
+
640
+			$path = $file->getPathname();
641
+
642
+			// Skip files in hidden directories within the Sniffs directory of this
643
+			// standard. We use the offset with strpos() to allow hidden directories
644
+			// before, valid example:
645
+			// /home/foo/.composer/vendor/squiz/custom_tool/MyStandard/Sniffs/...
646
+			if (strpos($path, DIRECTORY_SEPARATOR.'.', $dirLen) !== false) {
647
+				continue;
648
+			}
649
+
650
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
651
+				echo str_repeat("\t", $depth);
652
+				echo "\t\t=> ".Util\Common::stripBasepath($path, $this->config->basepath).PHP_EOL;
653
+			}
654
+
655
+			$sniffs[] = $path;
656
+		}//end foreach
657
+
658
+		return $sniffs;
659
+
660
+	}//end expandSniffDirectory()
661
+
662
+
663
+	/**
664
+	 * Expands a ruleset reference into a list of sniff files.
665
+	 *
666
+	 * @param string $ref        The reference from the ruleset XML file.
667
+	 * @param string $rulesetDir The directory of the ruleset XML file, used to
668
+	 *                           evaluate relative paths.
669
+	 * @param int    $depth      How many nested processing steps we are in. This
670
+	 *                           is only used for debug output.
671
+	 *
672
+	 * @return array
673
+	 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the reference is invalid.
674
+	 */
675
+	private function expandRulesetReference($ref, $rulesetDir, $depth=0)
676
+	{
677
+		// Ignore internal sniffs codes as they are used to only
678
+		// hide and change internal messages.
679
+		if (substr($ref, 0, 9) === 'Internal.') {
680
+			if (PHP_CODESNIFFER_VERBOSITY > 1) {
681
+				echo str_repeat("\t", $depth);
682
+				echo "\t\t* ignoring internal sniff code *".PHP_EOL;
683
+			}
684
+
685
+			return [];
686
+		}
687
+
688
+		// As sniffs can't begin with a full stop, assume references in
689
+		// this format are relative paths and attempt to convert them
690
+		// to absolute paths. If this fails, let the reference run through
691
+		// the normal checks and have it fail as normal.
692
+		if (substr($ref, 0, 1) === '.') {
693
+			$realpath = Util\Common::realpath($rulesetDir.'/'.$ref);
694
+			if ($realpath !== false) {
695
+				$ref = $realpath;
696
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
697
+					echo str_repeat("\t", $depth);
698
+					echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
699
+				}
700
+			}
701
+		}
702
+
703
+		// As sniffs can't begin with a tilde, assume references in
704
+		// this format are relative to the user's home directory.
705
+		if (substr($ref, 0, 2) === '~/') {
706
+			$realpath = Util\Common::realpath($ref);
707
+			if ($realpath !== false) {
708
+				$ref = $realpath;
709
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
710
+					echo str_repeat("\t", $depth);
711
+					echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
712
+				}
713
+			}
714
+		}
715
+
716
+		if (is_file($ref) === true) {
717
+			if (substr($ref, -9) === 'Sniff.php') {
718
+				// A single external sniff.
719
+				$this->rulesetDirs[] = dirname(dirname(dirname($ref)));
720
+				return [$ref];
721
+			}
722
+		} else {
723
+			// See if this is a whole standard being referenced.
724
+			$path = Util\Standards::getInstalledStandardPath($ref);
725
+			if (Util\Common::isPharFile($path) === true && strpos($path, 'ruleset.xml') === false) {
726
+				// If the ruleset exists inside the phar file, use it.
727
+				if (file_exists($path.DIRECTORY_SEPARATOR.'ruleset.xml') === true) {
728
+					$path .= DIRECTORY_SEPARATOR.'ruleset.xml';
729
+				} else {
730
+					$path = null;
731
+				}
732
+			}
733
+
734
+			if ($path !== null) {
735
+				$ref = $path;
736
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
737
+					echo str_repeat("\t", $depth);
738
+					echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
739
+				}
740
+			} else if (is_dir($ref) === false) {
741
+				// Work out the sniff path.
742
+				$sepPos = strpos($ref, DIRECTORY_SEPARATOR);
743
+				if ($sepPos !== false) {
744
+					$stdName = substr($ref, 0, $sepPos);
745
+					$path    = substr($ref, $sepPos);
746
+				} else {
747
+					$parts   = explode('.', $ref);
748
+					$stdName = $parts[0];
749
+					if (count($parts) === 1) {
750
+						// A whole standard?
751
+						$path = '';
752
+					} else if (count($parts) === 2) {
753
+						// A directory of sniffs?
754
+						$path = DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$parts[1];
755
+					} else {
756
+						// A single sniff?
757
+						$path = DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$parts[1].DIRECTORY_SEPARATOR.$parts[2].'Sniff.php';
758
+					}
759
+				}
760
+
761
+				$newRef  = false;
762
+				$stdPath = Util\Standards::getInstalledStandardPath($stdName);
763
+				if ($stdPath !== null && $path !== '') {
764
+					if (Util\Common::isPharFile($stdPath) === true
765
+						&& strpos($stdPath, 'ruleset.xml') === false
766
+					) {
767
+						// Phar files can only return the directory,
768
+						// since ruleset can be omitted if building one standard.
769
+						$newRef = Util\Common::realpath($stdPath.$path);
770
+					} else {
771
+						$newRef = Util\Common::realpath(dirname($stdPath).$path);
772
+					}
773
+				}
774
+
775
+				if ($newRef === false) {
776
+					// The sniff is not locally installed, so check if it is being
777
+					// referenced as a remote sniff outside the install. We do this
778
+					// by looking through all directories where we have found ruleset
779
+					// files before, looking for ones for this particular standard,
780
+					// and seeing if it is in there.
781
+					foreach ($this->rulesetDirs as $dir) {
782
+						if (strtolower(basename($dir)) !== strtolower($stdName)) {
783
+							continue;
784
+						}
785
+
786
+						$newRef = Util\Common::realpath($dir.$path);
787
+
788
+						if ($newRef !== false) {
789
+							$ref = $newRef;
790
+						}
791
+					}
792
+				} else {
793
+					$ref = $newRef;
794
+				}
795
+
796
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
797
+					echo str_repeat("\t", $depth);
798
+					echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
799
+				}
800
+			}//end if
801
+		}//end if
802
+
803
+		if (is_dir($ref) === true) {
804
+			if (is_file($ref.DIRECTORY_SEPARATOR.'ruleset.xml') === true) {
805
+				// We are referencing an external coding standard.
806
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
807
+					echo str_repeat("\t", $depth);
808
+					echo "\t\t* rule is referencing a standard using directory name; processing *".PHP_EOL;
809
+				}
810
+
811
+				return $this->processRuleset($ref.DIRECTORY_SEPARATOR.'ruleset.xml', ($depth + 2));
812
+			} else {
813
+				// We are referencing a whole directory of sniffs.
814
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
815
+					echo str_repeat("\t", $depth);
816
+					echo "\t\t* rule is referencing a directory of sniffs *".PHP_EOL;
817
+					echo str_repeat("\t", $depth);
818
+					echo "\t\tAdding sniff files from directory".PHP_EOL;
819
+				}
820
+
821
+				return $this->expandSniffDirectory($ref, ($depth + 1));
822
+			}
823
+		} else {
824
+			if (is_file($ref) === false) {
825
+				$error = "Referenced sniff \"$ref\" does not exist";
826
+				throw new RuntimeException($error);
827
+			}
828
+
829
+			if (substr($ref, -9) === 'Sniff.php') {
830
+				// A single sniff.
831
+				return [$ref];
832
+			} else {
833
+				// Assume an external ruleset.xml file.
834
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
835
+					echo str_repeat("\t", $depth);
836
+					echo "\t\t* rule is referencing a standard using ruleset path; processing *".PHP_EOL;
837
+				}
838
+
839
+				return $this->processRuleset($ref, ($depth + 2));
840
+			}
841
+		}//end if
842
+
843
+	}//end expandRulesetReference()
844
+
845
+
846
+	/**
847
+	 * Processes a rule from a ruleset XML file, overriding built-in defaults.
848
+	 *
849
+	 * @param \SimpleXMLElement $rule      The rule object from a ruleset XML file.
850
+	 * @param string[]          $newSniffs An array of sniffs that got included by this rule.
851
+	 * @param int               $depth     How many nested processing steps we are in.
852
+	 *                                     This is only used for debug output.
853
+	 *
854
+	 * @return void
855
+	 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If rule settings are invalid.
856
+	 */
857
+	private function processRule($rule, $newSniffs, $depth=0)
858
+	{
859
+		$ref  = (string) $rule['ref'];
860
+		$todo = [$ref];
861
+
862
+		$parts = explode('.', $ref);
863
+		if (count($parts) <= 2) {
864
+			// We are processing a standard or a category of sniffs.
865
+			foreach ($newSniffs as $sniffFile) {
866
+				$parts         = explode(DIRECTORY_SEPARATOR, $sniffFile);
867
+				$sniffName     = array_pop($parts);
868
+				$sniffCategory = array_pop($parts);
869
+				array_pop($parts);
870
+				$sniffStandard = array_pop($parts);
871
+				$todo[]        = $sniffStandard.'.'.$sniffCategory.'.'.substr($sniffName, 0, -9);
872
+			}
873
+		}
874
+
875
+		foreach ($todo as $code) {
876
+			// Custom severity.
877
+			if (isset($rule->severity) === true
878
+				&& $this->shouldProcessElement($rule->severity) === true
879
+			) {
880
+				if (isset($this->ruleset[$code]) === false) {
881
+					$this->ruleset[$code] = [];
882
+				}
883
+
884
+				$this->ruleset[$code]['severity'] = (int) $rule->severity;
885
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
886
+					echo str_repeat("\t", $depth);
887
+					echo "\t\t=> severity set to ".(int) $rule->severity;
888
+					if ($code !== $ref) {
889
+						echo " for $code";
890
+					}
891
+
892
+					echo PHP_EOL;
893
+				}
894
+			}
895
+
896
+			// Custom message type.
897
+			if (isset($rule->type) === true
898
+				&& $this->shouldProcessElement($rule->type) === true
899
+			) {
900
+				if (isset($this->ruleset[$code]) === false) {
901
+					$this->ruleset[$code] = [];
902
+				}
903
+
904
+				$type = strtolower((string) $rule->type);
905
+				if ($type !== 'error' && $type !== 'warning') {
906
+					throw new RuntimeException("Message type \"$type\" is invalid; must be \"error\" or \"warning\"");
907
+				}
908
+
909
+				$this->ruleset[$code]['type'] = $type;
910
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
911
+					echo str_repeat("\t", $depth);
912
+					echo "\t\t=> message type set to ".(string) $rule->type;
913
+					if ($code !== $ref) {
914
+						echo " for $code";
915
+					}
916
+
917
+					echo PHP_EOL;
918
+				}
919
+			}//end if
920
+
921
+			// Custom message.
922
+			if (isset($rule->message) === true
923
+				&& $this->shouldProcessElement($rule->message) === true
924
+			) {
925
+				if (isset($this->ruleset[$code]) === false) {
926
+					$this->ruleset[$code] = [];
927
+				}
928
+
929
+				$this->ruleset[$code]['message'] = (string) $rule->message;
930
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
931
+					echo str_repeat("\t", $depth);
932
+					echo "\t\t=> message set to ".(string) $rule->message;
933
+					if ($code !== $ref) {
934
+						echo " for $code";
935
+					}
936
+
937
+					echo PHP_EOL;
938
+				}
939
+			}
940
+
941
+			// Custom properties.
942
+			if (isset($rule->properties) === true
943
+				&& $this->shouldProcessElement($rule->properties) === true
944
+			) {
945
+				foreach ($rule->properties->property as $prop) {
946
+					if ($this->shouldProcessElement($prop) === false) {
947
+						continue;
948
+					}
949
+
950
+					if (isset($this->ruleset[$code]) === false) {
951
+						$this->ruleset[$code] = [
952
+							'properties' => [],
953
+						];
954
+					} else if (isset($this->ruleset[$code]['properties']) === false) {
955
+						$this->ruleset[$code]['properties'] = [];
956
+					}
957
+
958
+					$name = (string) $prop['name'];
959
+					if (isset($prop['type']) === true
960
+						&& (string) $prop['type'] === 'array'
961
+					) {
962
+						$values = [];
963
+						if (isset($prop['extend']) === true
964
+							&& (string) $prop['extend'] === 'true'
965
+							&& isset($this->ruleset[$code]['properties'][$name]) === true
966
+						) {
967
+							$values = $this->ruleset[$code]['properties'][$name];
968
+						}
969
+
970
+						if (isset($prop->element) === true) {
971
+							$printValue = '';
972
+							foreach ($prop->element as $element) {
973
+								if ($this->shouldProcessElement($element) === false) {
974
+									continue;
975
+								}
976
+
977
+								$value = (string) $element['value'];
978
+								if (isset($element['key']) === true) {
979
+									$key          = (string) $element['key'];
980
+									$values[$key] = $value;
981
+									$printValue  .= $key.'=>'.$value.',';
982
+								} else {
983
+									$values[]    = $value;
984
+									$printValue .= $value.',';
985
+								}
986
+							}
987
+
988
+							$printValue = rtrim($printValue, ',');
989
+						} else {
990
+							$value      = (string) $prop['value'];
991
+							$printValue = $value;
992
+							foreach (explode(',', $value) as $val) {
993
+								list($k, $v) = explode('=>', $val.'=>');
994
+								if ($v !== '') {
995
+									$values[trim($k)] = trim($v);
996
+								} else {
997
+									$values[] = trim($k);
998
+								}
999
+							}
1000
+						}//end if
1001
+
1002
+						$this->ruleset[$code]['properties'][$name] = $values;
1003
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1004
+							echo str_repeat("\t", $depth);
1005
+							echo "\t\t=> array property \"$name\" set to \"$printValue\"";
1006
+							if ($code !== $ref) {
1007
+								echo " for $code";
1008
+							}
1009
+
1010
+							echo PHP_EOL;
1011
+						}
1012
+					} else {
1013
+						$this->ruleset[$code]['properties'][$name] = (string) $prop['value'];
1014
+						if (PHP_CODESNIFFER_VERBOSITY > 1) {
1015
+							echo str_repeat("\t", $depth);
1016
+							echo "\t\t=> property \"$name\" set to \"".(string) $prop['value'].'"';
1017
+							if ($code !== $ref) {
1018
+								echo " for $code";
1019
+							}
1020
+
1021
+							echo PHP_EOL;
1022
+						}
1023
+					}//end if
1024
+				}//end foreach
1025
+			}//end if
1026
+
1027
+			// Ignore patterns.
1028
+			foreach ($rule->{'exclude-pattern'} as $pattern) {
1029
+				if ($this->shouldProcessElement($pattern) === false) {
1030
+					continue;
1031
+				}
1032
+
1033
+				if (isset($this->ignorePatterns[$code]) === false) {
1034
+					$this->ignorePatterns[$code] = [];
1035
+				}
1036
+
1037
+				if (isset($pattern['type']) === false) {
1038
+					$pattern['type'] = 'absolute';
1039
+				}
1040
+
1041
+				$this->ignorePatterns[$code][(string) $pattern] = (string) $pattern['type'];
1042
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
1043
+					echo str_repeat("\t", $depth);
1044
+					echo "\t\t=> added rule-specific ".(string) $pattern['type'].' ignore pattern';
1045
+					if ($code !== $ref) {
1046
+						echo " for $code";
1047
+					}
1048
+
1049
+					echo ': '.(string) $pattern.PHP_EOL;
1050
+				}
1051
+			}//end foreach
1052
+
1053
+			// Include patterns.
1054
+			foreach ($rule->{'include-pattern'} as $pattern) {
1055
+				if ($this->shouldProcessElement($pattern) === false) {
1056
+					continue;
1057
+				}
1058
+
1059
+				if (isset($this->includePatterns[$code]) === false) {
1060
+					$this->includePatterns[$code] = [];
1061
+				}
1062
+
1063
+				if (isset($pattern['type']) === false) {
1064
+					$pattern['type'] = 'absolute';
1065
+				}
1066
+
1067
+				$this->includePatterns[$code][(string) $pattern] = (string) $pattern['type'];
1068
+				if (PHP_CODESNIFFER_VERBOSITY > 1) {
1069
+					echo str_repeat("\t", $depth);
1070
+					echo "\t\t=> added rule-specific ".(string) $pattern['type'].' include pattern';
1071
+					if ($code !== $ref) {
1072
+						echo " for $code";
1073
+					}
1074
+
1075
+					echo ': '.(string) $pattern.PHP_EOL;
1076
+				}
1077
+			}//end foreach
1078
+		}//end foreach
1079
+
1080
+	}//end processRule()
1081
+
1082
+
1083
+	/**
1084
+	 * Determine if an element should be processed or ignored.
1085
+	 *
1086
+	 * @param \SimpleXMLElement $element An object from a ruleset XML file.
1087
+	 *
1088
+	 * @return bool
1089
+	 */
1090
+	private function shouldProcessElement($element)
1091
+	{
1092
+		if (isset($element['phpcbf-only']) === false
1093
+			&& isset($element['phpcs-only']) === false
1094
+		) {
1095
+			// No exceptions are being made.
1096
+			return true;
1097
+		}
1098
+
1099
+		if (PHP_CODESNIFFER_CBF === true
1100
+			&& isset($element['phpcbf-only']) === true
1101
+			&& (string) $element['phpcbf-only'] === 'true'
1102
+		) {
1103
+			return true;
1104
+		}
1105
+
1106
+		if (PHP_CODESNIFFER_CBF === false
1107
+			&& isset($element['phpcs-only']) === true
1108
+			&& (string) $element['phpcs-only'] === 'true'
1109
+		) {
1110
+			return true;
1111
+		}
1112
+
1113
+		return false;
1114
+
1115
+	}//end shouldProcessElement()
1116
+
1117
+
1118
+	/**
1119
+	 * Loads and stores sniffs objects used for sniffing files.
1120
+	 *
1121
+	 * @param array $files        Paths to the sniff files to register.
1122
+	 * @param array $restrictions The sniff class names to restrict the allowed
1123
+	 *                            listeners to.
1124
+	 * @param array $exclusions   The sniff class names to exclude from the
1125
+	 *                            listeners list.
1126
+	 *
1127
+	 * @return void
1128
+	 */
1129
+	public function registerSniffs($files, $restrictions, $exclusions)
1130
+	{
1131
+		$listeners = [];
1132
+
1133
+		foreach ($files as $file) {
1134
+			// Work out where the position of /StandardName/Sniffs/... is
1135
+			// so we can determine what the class will be called.
1136
+			$sniffPos = strrpos($file, DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR);
1137
+			if ($sniffPos === false) {
1138
+				continue;
1139
+			}
1140
+
1141
+			$slashPos = strrpos(substr($file, 0, $sniffPos), DIRECTORY_SEPARATOR);
1142
+			if ($slashPos === false) {
1143
+				continue;
1144
+			}
1145
+
1146
+			$className   = Autoload::loadFile($file);
1147
+			$compareName = Util\Common::cleanSniffClass($className);
1148
+
1149
+			// If they have specified a list of sniffs to restrict to, check
1150
+			// to see if this sniff is allowed.
1151
+			if (empty($restrictions) === false
1152
+				&& isset($restrictions[$compareName]) === false
1153
+			) {
1154
+				continue;
1155
+			}
1156
+
1157
+			// If they have specified a list of sniffs to exclude, check
1158
+			// to see if this sniff is allowed.
1159
+			if (empty($exclusions) === false
1160
+				&& isset($exclusions[$compareName]) === true
1161
+			) {
1162
+				continue;
1163
+			}
1164
+
1165
+			// Skip abstract classes.
1166
+			$reflection = new \ReflectionClass($className);
1167
+			if ($reflection->isAbstract() === true) {
1168
+				continue;
1169
+			}
1170
+
1171
+			$listeners[$className] = $className;
1172
+
1173
+			if (PHP_CODESNIFFER_VERBOSITY > 2) {
1174
+				echo "Registered $className".PHP_EOL;
1175
+			}
1176
+		}//end foreach
1177
+
1178
+		$this->sniffs = $listeners;
1179
+
1180
+	}//end registerSniffs()
1181
+
1182
+
1183
+	/**
1184
+	 * Populates the array of PHP_CodeSniffer_Sniff's for this file.
1185
+	 *
1186
+	 * @return void
1187
+	 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If sniff registration fails.
1188
+	 */
1189
+	public function populateTokenListeners()
1190
+	{
1191
+		// Construct a list of listeners indexed by token being listened for.
1192
+		$this->tokenListeners = [];
1193
+
1194
+		foreach ($this->sniffs as $sniffClass => $sniffObject) {
1195
+			$this->sniffs[$sniffClass] = null;
1196
+			$this->sniffs[$sniffClass] = new $sniffClass();
1197
+
1198
+			$sniffCode = Util\Common::getSniffCode($sniffClass);
1199
+			$this->sniffCodes[$sniffCode] = $sniffClass;
1200
+
1201
+			// Set custom properties.
1202
+			if (isset($this->ruleset[$sniffCode]['properties']) === true) {
1203
+				foreach ($this->ruleset[$sniffCode]['properties'] as $name => $value) {
1204
+					$this->setSniffProperty($sniffClass, $name, $value);
1205
+				}
1206
+			}
1207
+
1208
+			$tokenizers = [];
1209
+			$vars       = get_class_vars($sniffClass);
1210
+			if (isset($vars['supportedTokenizers']) === true) {
1211
+				foreach ($vars['supportedTokenizers'] as $tokenizer) {
1212
+					$tokenizers[$tokenizer] = $tokenizer;
1213
+				}
1214
+			} else {
1215
+				$tokenizers = ['PHP' => 'PHP'];
1216
+			}
1217
+
1218
+			$tokens = $this->sniffs[$sniffClass]->register();
1219
+			if (is_array($tokens) === false) {
1220
+				$msg = "Sniff $sniffClass register() method must return an array";
1221
+				throw new RuntimeException($msg);
1222
+			}
1223
+
1224
+			$ignorePatterns = [];
1225
+			$patterns       = $this->getIgnorePatterns($sniffCode);
1226
+			foreach ($patterns as $pattern => $type) {
1227
+				$replacements = [
1228
+					'\\,' => ',',
1229
+					'*'   => '.*',
1230
+				];
1231
+
1232
+				$ignorePatterns[] = strtr($pattern, $replacements);
1233
+			}
1234
+
1235
+			$includePatterns = [];
1236
+			$patterns        = $this->getIncludePatterns($sniffCode);
1237
+			foreach ($patterns as $pattern => $type) {
1238
+				$replacements = [
1239
+					'\\,' => ',',
1240
+					'*'   => '.*',
1241
+				];
1242
+
1243
+				$includePatterns[] = strtr($pattern, $replacements);
1244
+			}
1245
+
1246
+			foreach ($tokens as $token) {
1247
+				if (isset($this->tokenListeners[$token]) === false) {
1248
+					$this->tokenListeners[$token] = [];
1249
+				}
1250
+
1251
+				if (isset($this->tokenListeners[$token][$sniffClass]) === false) {
1252
+					$this->tokenListeners[$token][$sniffClass] = [
1253
+						'class'      => $sniffClass,
1254
+						'source'     => $sniffCode,
1255
+						'tokenizers' => $tokenizers,
1256
+						'ignore'     => $ignorePatterns,
1257
+						'include'    => $includePatterns,
1258
+					];
1259
+				}
1260
+			}
1261
+		}//end foreach
1262
+
1263
+	}//end populateTokenListeners()
1264
+
1265
+
1266
+	/**
1267
+	 * Set a single property for a sniff.
1268
+	 *
1269
+	 * @param string $sniffClass The class name of the sniff.
1270
+	 * @param string $name       The name of the property to change.
1271
+	 * @param string $value      The new value of the property.
1272
+	 *
1273
+	 * @return void
1274
+	 */
1275
+	public function setSniffProperty($sniffClass, $name, $value)
1276
+	{
1277
+		// Setting a property for a sniff we are not using.
1278
+		if (isset($this->sniffs[$sniffClass]) === false) {
1279
+			return;
1280
+		}
1281
+
1282
+		$name = trim($name);
1283
+		if (is_string($value) === true) {
1284
+			$value = trim($value);
1285
+		}
1286
+
1287
+		if ($value === '') {
1288
+			$value = null;
1289
+		}
1290
+
1291
+		// Special case for booleans.
1292
+		if ($value === 'true') {
1293
+			$value = true;
1294
+		} else if ($value === 'false') {
1295
+			$value = false;
1296
+		} else if (substr($name, -2) === '[]') {
1297
+			$name   = substr($name, 0, -2);
1298
+			$values = [];
1299
+			if ($value !== null) {
1300
+				foreach (explode(',', $value) as $val) {
1301
+					list($k, $v) = explode('=>', $val.'=>');
1302
+					if ($v !== '') {
1303
+						$values[trim($k)] = trim($v);
1304
+					} else {
1305
+						$values[] = trim($k);
1306
+					}
1307
+				}
1308
+			}
1309
+
1310
+			$value = $values;
1311
+		}
1312
+
1313
+		$this->sniffs[$sniffClass]->$name = $value;
1314
+
1315
+	}//end setSniffProperty()
1316
+
1317
+
1318
+	/**
1319
+	 * Gets the array of ignore patterns.
1320
+	 *
1321
+	 * Optionally takes a listener to get ignore patterns specified
1322
+	 * for that sniff only.
1323
+	 *
1324
+	 * @param string $listener The listener to get patterns for. If NULL, all
1325
+	 *                         patterns are returned.
1326
+	 *
1327
+	 * @return array
1328
+	 */
1329
+	public function getIgnorePatterns($listener=null)
1330
+	{
1331
+		if ($listener === null) {
1332
+			return $this->ignorePatterns;
1333
+		}
1334
+
1335
+		if (isset($this->ignorePatterns[$listener]) === true) {
1336
+			return $this->ignorePatterns[$listener];
1337
+		}
1338
+
1339
+		return [];
1340
+
1341
+	}//end getIgnorePatterns()
1342
+
1343
+
1344
+	/**
1345
+	 * Gets the array of include patterns.
1346
+	 *
1347
+	 * Optionally takes a listener to get include patterns specified
1348
+	 * for that sniff only.
1349
+	 *
1350
+	 * @param string $listener The listener to get patterns for. If NULL, all
1351
+	 *                         patterns are returned.
1352
+	 *
1353
+	 * @return array
1354
+	 */
1355
+	public function getIncludePatterns($listener=null)
1356
+	{
1357
+		if ($listener === null) {
1358
+			return $this->includePatterns;
1359
+		}
1360
+
1361
+		if (isset($this->includePatterns[$listener]) === true) {
1362
+			return $this->includePatterns[$listener];
1363
+		}
1364
+
1365
+		return [];
1366
+
1367
+	}//end getIncludePatterns()
1368 1368
 
1369 1369
 
1370 1370
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Config.php 1 patch
Indentation   +1691 added lines, -1691 removed lines patch added patch discarded remove patch
@@ -18,1697 +18,1697 @@
 block discarded – undo
18 18
 class Config
19 19
 {
20 20
 
21
-    /**
22
-     * The current version.
23
-     *
24
-     * @var string
25
-     */
26
-    const VERSION = '3.4.2';
27
-
28
-    /**
29
-     * Package stability; either stable, beta or alpha.
30
-     *
31
-     * @var string
32
-     */
33
-    const STABILITY = 'stable';
34
-
35
-    /**
36
-     * An array of settings that PHPCS and PHPCBF accept.
37
-     *
38
-     * This array is not meant to be accessed directly. Instead, use the settings
39
-     * as if they are class member vars so the __get() and __set() magic methods
40
-     * can be used to validate the values. For example, to set the verbosity level to
41
-     * level 2, use $this->verbosity = 2; instead of accessing this property directly.
42
-     *
43
-     * The list of settings are:
44
-     *
45
-     * string[] files           The files and directories to check.
46
-     * string[] standards       The standards being used for checking.
47
-     * int      verbosity       How verbose the output should be.
48
-     *                          0: no unnecessary output
49
-     *                          1: basic output for files being checked
50
-     *                          2: ruleset and file parsing output
51
-     *                          3: sniff execution output
52
-     * bool     interactive     Enable interactive checking mode.
53
-     * bool     parallel        Check files in parallel.
54
-     * bool     cache           Enable the use of the file cache.
55
-     * bool     cacheFile       A file where the cache data should be written
56
-     * bool     colors          Display colours in output.
57
-     * bool     explain         Explain the coding standards.
58
-     * bool     local           Process local files in directories only (no recursion).
59
-     * bool     showSources     Show sniff source codes in report output.
60
-     * bool     showProgress    Show basic progress information while running.
61
-     * bool     quiet           Quiet mode; disables progress and verbose output.
62
-     * bool     annotations     Process phpcs: annotations.
63
-     * int      tabWidth        How many spaces each tab is worth.
64
-     * string   encoding        The encoding of the files being checked.
65
-     * string[] sniffs          The sniffs that should be used for checking.
66
-     *                          If empty, all sniffs in the supplied standards will be used.
67
-     * string[] exclude         The sniffs that should be excluded from checking.
68
-     *                          If empty, all sniffs in the supplied standards will be used.
69
-     * string[] ignored         Regular expressions used to ignore files and folders during checking.
70
-     * string   reportFile      A file where the report output should be written.
71
-     * string   generator       The documentation generator to use.
72
-     * string   filter          The filter to use for the run.
73
-     * string[] bootstrap       One of more files to include before the run begins.
74
-     * int      reportWidth     The maximum number of columns that reports should use for output.
75
-     *                          Set to "auto" for have this value changed to the width of the terminal.
76
-     * int      errorSeverity   The minimum severity an error must have to be displayed.
77
-     * int      warningSeverity The minimum severity a warning must have to be displayed.
78
-     * bool     recordErrors    Record the content of error messages as well as error counts.
79
-     * string   suffix          A suffix to add to fixed files.
80
-     * string   basepath        A file system location to strip from the paths of files shown in reports.
81
-     * bool     stdin           Read content from STDIN instead of supplied files.
82
-     * string   stdinContent    Content passed directly to PHPCS on STDIN.
83
-     * string   stdinPath       The path to use for content passed on STDIN.
84
-     *
85
-     * array<string, string>      extensions File extensions that should be checked, and what tokenizer to use.
86
-     *                                       E.g., array('inc' => 'PHP');
87
-     * array<string, string|null> reports    The reports to use for printing output after the run.
88
-     *                                       The format of the array is:
89
-     *                                           array(
90
-     *                                            'reportName1' => 'outputFile',
91
-     *                                            'reportName2' => null,
92
-     *                                           );
93
-     *                                       If the array value is NULL, the report will be written to the screen.
94
-     *
95
-     * string[] unknown Any arguments gathered on the command line that are unknown to us.
96
-     *                  E.g., using `phpcs -c` will give array('c');
97
-     *
98
-     * @var array<string, mixed>
99
-     */
100
-    private $settings = [
101
-        'files'           => null,
102
-        'standards'       => null,
103
-        'verbosity'       => null,
104
-        'interactive'     => null,
105
-        'parallel'        => null,
106
-        'cache'           => null,
107
-        'cacheFile'       => null,
108
-        'colors'          => null,
109
-        'explain'         => null,
110
-        'local'           => null,
111
-        'showSources'     => null,
112
-        'showProgress'    => null,
113
-        'quiet'           => null,
114
-        'annotations'     => null,
115
-        'tabWidth'        => null,
116
-        'encoding'        => null,
117
-        'extensions'      => null,
118
-        'sniffs'          => null,
119
-        'exclude'         => null,
120
-        'ignored'         => null,
121
-        'reportFile'      => null,
122
-        'generator'       => null,
123
-        'filter'          => null,
124
-        'bootstrap'       => null,
125
-        'reports'         => null,
126
-        'basepath'        => null,
127
-        'reportWidth'     => null,
128
-        'errorSeverity'   => null,
129
-        'warningSeverity' => null,
130
-        'recordErrors'    => null,
131
-        'suffix'          => null,
132
-        'stdin'           => null,
133
-        'stdinContent'    => null,
134
-        'stdinPath'       => null,
135
-        'unknown'         => null,
136
-    ];
137
-
138
-    /**
139
-     * Whether or not to kill the process when an unknown command line arg is found.
140
-     *
141
-     * If FALSE, arguments that are not command line options or file/directory paths
142
-     * will be ignored and execution will continue. These values will be stored in
143
-     * $this->unknown.
144
-     *
145
-     * @var boolean
146
-     */
147
-    public $dieOnUnknownArg;
148
-
149
-    /**
150
-     * The current command line arguments we are processing.
151
-     *
152
-     * @var string[]
153
-     */
154
-    private $cliArgs = [];
155
-
156
-    /**
157
-     * Command line values that the user has supplied directly.
158
-     *
159
-     * @var array<string, TRUE>
160
-     */
161
-    private static $overriddenDefaults = [];
162
-
163
-    /**
164
-     * Config file data that has been loaded for the run.
165
-     *
166
-     * @var array<string, string>
167
-     */
168
-    private static $configData = null;
169
-
170
-    /**
171
-     * The full path to the config data file that has been loaded.
172
-     *
173
-     * @var string
174
-     */
175
-    private static $configDataFile = null;
176
-
177
-    /**
178
-     * Automatically discovered executable utility paths.
179
-     *
180
-     * @var array<string, string>
181
-     */
182
-    private static $executablePaths = [];
183
-
184
-
185
-    /**
186
-     * Get the value of an inaccessible property.
187
-     *
188
-     * @param string $name The name of the property.
189
-     *
190
-     * @return mixed
191
-     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the setting name is invalid.
192
-     */
193
-    public function __get($name)
194
-    {
195
-        if (array_key_exists($name, $this->settings) === false) {
196
-            throw new RuntimeException("ERROR: unable to get value of property \"$name\"");
197
-        }
198
-
199
-        return $this->settings[$name];
200
-
201
-    }//end __get()
202
-
203
-
204
-    /**
205
-     * Set the value of an inaccessible property.
206
-     *
207
-     * @param string $name  The name of the property.
208
-     * @param mixed  $value The value of the property.
209
-     *
210
-     * @return void
211
-     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the setting name is invalid.
212
-     */
213
-    public function __set($name, $value)
214
-    {
215
-        if (array_key_exists($name, $this->settings) === false) {
216
-            throw new RuntimeException("Can't __set() $name; setting doesn't exist");
217
-        }
218
-
219
-        switch ($name) {
220
-        case 'reportWidth' :
221
-            // Support auto terminal width.
222
-            if ($value === 'auto' && preg_match('|\d+ (\d+)|', shell_exec('stty size 2>&1'), $matches) === 1) {
223
-                $value = (int) $matches[1];
224
-            } else {
225
-                $value = (int) $value;
226
-            }
227
-            break;
228
-        case 'standards' :
229
-            $cleaned = [];
230
-
231
-            // Check if the standard name is valid, or if the case is invalid.
232
-            $installedStandards = Util\Standards::getInstalledStandards();
233
-            foreach ($value as $standard) {
234
-                foreach ($installedStandards as $validStandard) {
235
-                    if (strtolower($standard) === strtolower($validStandard)) {
236
-                        $standard = $validStandard;
237
-                        break;
238
-                    }
239
-                }
240
-
241
-                $cleaned[] = $standard;
242
-            }
243
-
244
-            $value = $cleaned;
245
-            break;
246
-        default :
247
-            // No validation required.
248
-            break;
249
-        }//end switch
250
-
251
-        $this->settings[$name] = $value;
252
-
253
-    }//end __set()
254
-
255
-
256
-    /**
257
-     * Check if the value of an inaccessible property is set.
258
-     *
259
-     * @param string $name The name of the property.
260
-     *
261
-     * @return bool
262
-     */
263
-    public function __isset($name)
264
-    {
265
-        return isset($this->settings[$name]);
266
-
267
-    }//end __isset()
268
-
269
-
270
-    /**
271
-     * Unset the value of an inaccessible property.
272
-     *
273
-     * @param string $name The name of the property.
274
-     *
275
-     * @return void
276
-     */
277
-    public function __unset($name)
278
-    {
279
-        $this->settings[$name] = null;
280
-
281
-    }//end __unset()
282
-
283
-
284
-    /**
285
-     * Get the array of all config settings.
286
-     *
287
-     * @return array<string, mixed>
288
-     */
289
-    public function getSettings()
290
-    {
291
-        return $this->settings;
292
-
293
-    }//end getSettings()
294
-
295
-
296
-    /**
297
-     * Set the array of all config settings.
298
-     *
299
-     * @param array<string, mixed> $settings The array of config settings.
300
-     *
301
-     * @return void
302
-     */
303
-    public function setSettings($settings)
304
-    {
305
-        return $this->settings = $settings;
306
-
307
-    }//end setSettings()
308
-
309
-
310
-    /**
311
-     * Creates a Config object and populates it with command line values.
312
-     *
313
-     * @param array $cliArgs         An array of values gathered from CLI args.
314
-     * @param bool  $dieOnUnknownArg Whether or not to kill the process when an
315
-     *                               unknown command line arg is found.
316
-     *
317
-     * @return void
318
-     */
319
-    public function __construct(array $cliArgs=[], $dieOnUnknownArg=true)
320
-    {
321
-        if (defined('PHP_CODESNIFFER_IN_TESTS') === true) {
322
-            // Let everything through during testing so that we can
323
-            // make use of PHPUnit command line arguments as well.
324
-            $this->dieOnUnknownArg = false;
325
-        } else {
326
-            $this->dieOnUnknownArg = $dieOnUnknownArg;
327
-        }
328
-
329
-        if (empty($cliArgs) === true) {
330
-            $cliArgs = $_SERVER['argv'];
331
-            array_shift($cliArgs);
332
-        }
333
-
334
-        $this->restoreDefaults();
335
-        $this->setCommandLineValues($cliArgs);
336
-
337
-        if (isset(self::$overriddenDefaults['standards']) === false) {
338
-            // They did not supply a standard to use.
339
-            // Look for a default ruleset in the current directory or higher.
340
-            $currentDir = getcwd();
341
-
342
-            $defaultFiles = [
343
-                '.phpcs.xml',
344
-                'phpcs.xml',
345
-                '.phpcs.xml.dist',
346
-                'phpcs.xml.dist',
347
-            ];
348
-
349
-            do {
350
-                foreach ($defaultFiles as $defaultFilename) {
351
-                    $default = $currentDir.DIRECTORY_SEPARATOR.$defaultFilename;
352
-                    if (is_file($default) === true) {
353
-                        $this->standards = [$default];
354
-                        break(2);
355
-                    }
356
-                }
357
-
358
-                $lastDir    = $currentDir;
359
-                $currentDir = dirname($currentDir);
360
-            } while ($currentDir !== '.' && $currentDir !== $lastDir);
361
-        }//end if
362
-
363
-        if (defined('STDIN') === false
364
-            || strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'
365
-        ) {
366
-            return;
367
-        }
368
-
369
-        $handle = fopen('php://stdin', 'r');
370
-
371
-        // Check for content on STDIN.
372
-        if ($this->stdin === true
373
-            || (Util\Common::isStdinATTY() === false
374
-            && feof($handle) === false)
375
-        ) {
376
-            $readStreams = [$handle];
377
-            $writeSteams = null;
378
-
379
-            $fileContents = '';
380
-            while (is_resource($handle) === true && feof($handle) === false) {
381
-                // Set a timeout of 200ms.
382
-                if (stream_select($readStreams, $writeSteams, $writeSteams, 0, 200000) === 0) {
383
-                    break;
384
-                }
385
-
386
-                $fileContents .= fgets($handle);
387
-            }
388
-
389
-            if (trim($fileContents) !== '') {
390
-                $this->stdin        = true;
391
-                $this->stdinContent = $fileContents;
392
-                self::$overriddenDefaults['stdin']        = true;
393
-                self::$overriddenDefaults['stdinContent'] = true;
394
-            }
395
-        }//end if
396
-
397
-        fclose($handle);
398
-
399
-    }//end __construct()
400
-
401
-
402
-    /**
403
-     * Set the command line values.
404
-     *
405
-     * @param array $args An array of command line arguments to set.
406
-     *
407
-     * @return void
408
-     */
409
-    public function setCommandLineValues($args)
410
-    {
411
-        $this->cliArgs = $args;
412
-        $numArgs       = count($args);
413
-
414
-        for ($i = 0; $i < $numArgs; $i++) {
415
-            $arg = $this->cliArgs[$i];
416
-            if ($arg === '') {
417
-                continue;
418
-            }
419
-
420
-            if ($arg{0} === '-') {
421
-                if ($arg === '-') {
422
-                    // Asking to read from STDIN.
423
-                    $this->stdin = true;
424
-                    self::$overriddenDefaults['stdin'] = true;
425
-                    continue;
426
-                }
427
-
428
-                if ($arg === '--') {
429
-                    // Empty argument, ignore it.
430
-                    continue;
431
-                }
432
-
433
-                if ($arg{1} === '-') {
434
-                    $this->processLongArgument(substr($arg, 2), $i);
435
-                } else {
436
-                    $switches = str_split($arg);
437
-                    foreach ($switches as $switch) {
438
-                        if ($switch === '-') {
439
-                            continue;
440
-                        }
441
-
442
-                        $this->processShortArgument($switch, $i);
443
-                    }
444
-                }
445
-            } else {
446
-                $this->processUnknownArgument($arg, $i);
447
-            }//end if
448
-        }//end for
449
-
450
-    }//end setCommandLineValues()
451
-
452
-
453
-    /**
454
-     * Restore default values for all possible command line arguments.
455
-     *
456
-     * @return array
457
-     */
458
-    public function restoreDefaults()
459
-    {
460
-        $this->files           = [];
461
-        $this->standards       = ['PEAR'];
462
-        $this->verbosity       = 0;
463
-        $this->interactive     = false;
464
-        $this->cache           = false;
465
-        $this->cacheFile       = null;
466
-        $this->colors          = false;
467
-        $this->explain         = false;
468
-        $this->local           = false;
469
-        $this->showSources     = false;
470
-        $this->showProgress    = false;
471
-        $this->quiet           = false;
472
-        $this->annotations     = true;
473
-        $this->parallel        = 1;
474
-        $this->tabWidth        = 0;
475
-        $this->encoding        = 'utf-8';
476
-        $this->extensions      = [
477
-            'php' => 'PHP',
478
-            'inc' => 'PHP',
479
-            'js'  => 'JS',
480
-            'css' => 'CSS',
481
-        ];
482
-        $this->sniffs          = [];
483
-        $this->exclude         = [];
484
-        $this->ignored         = [];
485
-        $this->reportFile      = null;
486
-        $this->generator       = null;
487
-        $this->filter          = null;
488
-        $this->bootstrap       = [];
489
-        $this->basepath        = null;
490
-        $this->reports         = ['full' => null];
491
-        $this->reportWidth     = 'auto';
492
-        $this->errorSeverity   = 5;
493
-        $this->warningSeverity = 5;
494
-        $this->recordErrors    = true;
495
-        $this->suffix          = '';
496
-        $this->stdin           = false;
497
-        $this->stdinContent    = null;
498
-        $this->stdinPath       = null;
499
-        $this->unknown         = [];
500
-
501
-        $standard = self::getConfigData('default_standard');
502
-        if ($standard !== null) {
503
-            $this->standards = explode(',', $standard);
504
-        }
505
-
506
-        $reportFormat = self::getConfigData('report_format');
507
-        if ($reportFormat !== null) {
508
-            $this->reports = [$reportFormat => null];
509
-        }
510
-
511
-        $tabWidth = self::getConfigData('tab_width');
512
-        if ($tabWidth !== null) {
513
-            $this->tabWidth = (int) $tabWidth;
514
-        }
515
-
516
-        $encoding = self::getConfigData('encoding');
517
-        if ($encoding !== null) {
518
-            $this->encoding = strtolower($encoding);
519
-        }
520
-
521
-        $severity = self::getConfigData('severity');
522
-        if ($severity !== null) {
523
-            $this->errorSeverity   = (int) $severity;
524
-            $this->warningSeverity = (int) $severity;
525
-        }
526
-
527
-        $severity = self::getConfigData('error_severity');
528
-        if ($severity !== null) {
529
-            $this->errorSeverity = (int) $severity;
530
-        }
531
-
532
-        $severity = self::getConfigData('warning_severity');
533
-        if ($severity !== null) {
534
-            $this->warningSeverity = (int) $severity;
535
-        }
536
-
537
-        $showWarnings = self::getConfigData('show_warnings');
538
-        if ($showWarnings !== null) {
539
-            $showWarnings = (bool) $showWarnings;
540
-            if ($showWarnings === false) {
541
-                $this->warningSeverity = 0;
542
-            }
543
-        }
544
-
545
-        $reportWidth = self::getConfigData('report_width');
546
-        if ($reportWidth !== null) {
547
-            $this->reportWidth = $reportWidth;
548
-        }
549
-
550
-        $showProgress = self::getConfigData('show_progress');
551
-        if ($showProgress !== null) {
552
-            $this->showProgress = (bool) $showProgress;
553
-        }
554
-
555
-        $quiet = self::getConfigData('quiet');
556
-        if ($quiet !== null) {
557
-            $this->quiet = (bool) $quiet;
558
-        }
559
-
560
-        $colors = self::getConfigData('colors');
561
-        if ($colors !== null) {
562
-            $this->colors = (bool) $colors;
563
-        }
564
-
565
-        if (defined('PHP_CODESNIFFER_IN_TESTS') === false) {
566
-            $cache = self::getConfigData('cache');
567
-            if ($cache !== null) {
568
-                $this->cache = (bool) $cache;
569
-            }
570
-
571
-            $parallel = self::getConfigData('parallel');
572
-            if ($parallel !== null) {
573
-                $this->parallel = max((int) $parallel, 1);
574
-            }
575
-        }
576
-
577
-    }//end restoreDefaults()
578
-
579
-
580
-    /**
581
-     * Processes a short (-e) command line argument.
582
-     *
583
-     * @param string $arg The command line argument.
584
-     * @param int    $pos The position of the argument on the command line.
585
-     *
586
-     * @return void
587
-     */
588
-    public function processShortArgument($arg, $pos)
589
-    {
590
-        switch ($arg) {
591
-        case 'h':
592
-        case '?':
593
-            ob_start();
594
-            $this->printUsage();
595
-            $output = ob_get_contents();
596
-            ob_end_clean();
597
-            throw new DeepExitException($output, 0);
598
-        case 'i' :
599
-            ob_start();
600
-            Util\Standards::printInstalledStandards();
601
-            $output = ob_get_contents();
602
-            ob_end_clean();
603
-            throw new DeepExitException($output, 0);
604
-        case 'v' :
605
-            if ($this->quiet === true) {
606
-                // Ignore when quiet mode is enabled.
607
-                break;
608
-            }
609
-
610
-            $this->verbosity++;
611
-            self::$overriddenDefaults['verbosity'] = true;
612
-            break;
613
-        case 'l' :
614
-            $this->local = true;
615
-            self::$overriddenDefaults['local'] = true;
616
-            break;
617
-        case 's' :
618
-            $this->showSources = true;
619
-            self::$overriddenDefaults['showSources'] = true;
620
-            break;
621
-        case 'a' :
622
-            $this->interactive = true;
623
-            self::$overriddenDefaults['interactive'] = true;
624
-            break;
625
-        case 'e':
626
-            $this->explain = true;
627
-            self::$overriddenDefaults['explain'] = true;
628
-            break;
629
-        case 'p' :
630
-            if ($this->quiet === true) {
631
-                // Ignore when quiet mode is enabled.
632
-                break;
633
-            }
634
-
635
-            $this->showProgress = true;
636
-            self::$overriddenDefaults['showProgress'] = true;
637
-            break;
638
-        case 'q' :
639
-            // Quiet mode disables a few other settings as well.
640
-            $this->quiet        = true;
641
-            $this->showProgress = false;
642
-            $this->verbosity    = 0;
643
-
644
-            self::$overriddenDefaults['quiet'] = true;
645
-            break;
646
-        case 'm' :
647
-            $this->recordErrors = false;
648
-            self::$overriddenDefaults['recordErrors'] = true;
649
-            break;
650
-        case 'd' :
651
-            $ini = explode('=', $this->cliArgs[($pos + 1)]);
652
-            $this->cliArgs[($pos + 1)] = '';
653
-            if (isset($ini[1]) === true) {
654
-                ini_set($ini[0], $ini[1]);
655
-            } else {
656
-                ini_set($ini[0], true);
657
-            }
658
-            break;
659
-        case 'n' :
660
-            if (isset(self::$overriddenDefaults['warningSeverity']) === false) {
661
-                $this->warningSeverity = 0;
662
-                self::$overriddenDefaults['warningSeverity'] = true;
663
-            }
664
-            break;
665
-        case 'w' :
666
-            if (isset(self::$overriddenDefaults['warningSeverity']) === false) {
667
-                $this->warningSeverity = $this->errorSeverity;
668
-                self::$overriddenDefaults['warningSeverity'] = true;
669
-            }
670
-            break;
671
-        default:
672
-            if ($this->dieOnUnknownArg === false) {
673
-                $unknown       = $this->unknown;
674
-                $unknown[]     = $arg;
675
-                $this->unknown = $unknown;
676
-            } else {
677
-                $this->processUnknownArgument('-'.$arg, $pos);
678
-            }
679
-        }//end switch
680
-
681
-    }//end processShortArgument()
682
-
683
-
684
-    /**
685
-     * Processes a long (--example) command line argument.
686
-     *
687
-     * @param string $arg The command line argument.
688
-     * @param int    $pos The position of the argument on the command line.
689
-     *
690
-     * @return void
691
-     */
692
-    public function processLongArgument($arg, $pos)
693
-    {
694
-        switch ($arg) {
695
-        case 'help':
696
-            ob_start();
697
-            $this->printUsage();
698
-            $output = ob_get_contents();
699
-            ob_end_clean();
700
-            throw new DeepExitException($output, 0);
701
-        case 'version':
702
-            $output  = 'PHP_CodeSniffer version '.self::VERSION.' ('.self::STABILITY.') ';
703
-            $output .= 'by Squiz (http://www.squiz.net)'.PHP_EOL;
704
-            throw new DeepExitException($output, 0);
705
-        case 'colors':
706
-            if (isset(self::$overriddenDefaults['colors']) === true) {
707
-                break;
708
-            }
709
-
710
-            $this->colors = true;
711
-            self::$overriddenDefaults['colors'] = true;
712
-            break;
713
-        case 'no-colors':
714
-            if (isset(self::$overriddenDefaults['colors']) === true) {
715
-                break;
716
-            }
717
-
718
-            $this->colors = false;
719
-            self::$overriddenDefaults['colors'] = true;
720
-            break;
721
-        case 'cache':
722
-            if (isset(self::$overriddenDefaults['cache']) === true) {
723
-                break;
724
-            }
725
-
726
-            if (defined('PHP_CODESNIFFER_IN_TESTS') === false) {
727
-                $this->cache = true;
728
-                self::$overriddenDefaults['cache'] = true;
729
-            }
730
-            break;
731
-        case 'no-cache':
732
-            if (isset(self::$overriddenDefaults['cache']) === true) {
733
-                break;
734
-            }
735
-
736
-            $this->cache = false;
737
-            self::$overriddenDefaults['cache'] = true;
738
-            break;
739
-        case 'ignore-annotations':
740
-            if (isset(self::$overriddenDefaults['annotations']) === true) {
741
-                break;
742
-            }
743
-
744
-            $this->annotations = false;
745
-            self::$overriddenDefaults['annotations'] = true;
746
-            break;
747
-        case 'config-set':
748
-            if (isset($this->cliArgs[($pos + 1)]) === false
749
-                || isset($this->cliArgs[($pos + 2)]) === false
750
-            ) {
751
-                $error  = 'ERROR: Setting a config option requires a name and value'.PHP_EOL.PHP_EOL;
752
-                $error .= $this->printShortUsage(true);
753
-                throw new DeepExitException($error, 3);
754
-            }
755
-
756
-            $key     = $this->cliArgs[($pos + 1)];
757
-            $value   = $this->cliArgs[($pos + 2)];
758
-            $current = self::getConfigData($key);
759
-
760
-            try {
761
-                $this->setConfigData($key, $value);
762
-            } catch (\Exception $e) {
763
-                throw new DeepExitException($e->getMessage().PHP_EOL, 3);
764
-            }
765
-
766
-            $output = 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
767
-
768
-            if ($current === null) {
769
-                $output .= "Config value \"$key\" added successfully".PHP_EOL;
770
-            } else {
771
-                $output .= "Config value \"$key\" updated successfully; old value was \"$current\"".PHP_EOL;
772
-            }
773
-            throw new DeepExitException($output, 0);
774
-        case 'config-delete':
775
-            if (isset($this->cliArgs[($pos + 1)]) === false) {
776
-                $error  = 'ERROR: Deleting a config option requires the name of the option'.PHP_EOL.PHP_EOL;
777
-                $error .= $this->printShortUsage(true);
778
-                throw new DeepExitException($error, 3);
779
-            }
780
-
781
-            $output = 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
782
-
783
-            $key     = $this->cliArgs[($pos + 1)];
784
-            $current = self::getConfigData($key);
785
-            if ($current === null) {
786
-                $output .= "Config value \"$key\" has not been set".PHP_EOL;
787
-            } else {
788
-                try {
789
-                    $this->setConfigData($key, null);
790
-                } catch (\Exception $e) {
791
-                    throw new DeepExitException($e->getMessage().PHP_EOL, 3);
792
-                }
793
-
794
-                $output .= "Config value \"$key\" removed successfully; old value was \"$current\"".PHP_EOL;
795
-            }
796
-            throw new DeepExitException($output, 0);
797
-        case 'config-show':
798
-            ob_start();
799
-            $data = self::getAllConfigData();
800
-            echo 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
801
-            $this->printConfigData($data);
802
-            $output = ob_get_contents();
803
-            ob_end_clean();
804
-            throw new DeepExitException($output, 0);
805
-        case 'runtime-set':
806
-            if (isset($this->cliArgs[($pos + 1)]) === false
807
-                || isset($this->cliArgs[($pos + 2)]) === false
808
-            ) {
809
-                $error  = 'ERROR: Setting a runtime config option requires a name and value'.PHP_EOL.PHP_EOL;
810
-                $error .= $this->printShortUsage(true);
811
-                throw new DeepExitException($error, 3);
812
-            }
813
-
814
-            $key   = $this->cliArgs[($pos + 1)];
815
-            $value = $this->cliArgs[($pos + 2)];
816
-            $this->cliArgs[($pos + 1)] = '';
817
-            $this->cliArgs[($pos + 2)] = '';
818
-            self::setConfigData($key, $value, true);
819
-            if (isset(self::$overriddenDefaults['runtime-set']) === false) {
820
-                self::$overriddenDefaults['runtime-set'] = [];
821
-            }
822
-
823
-            self::$overriddenDefaults['runtime-set'][$key] = true;
824
-            break;
825
-        default:
826
-            if (substr($arg, 0, 7) === 'sniffs=') {
827
-                if (isset(self::$overriddenDefaults['sniffs']) === true) {
828
-                    break;
829
-                }
830
-
831
-                $sniffs = explode(',', substr($arg, 7));
832
-                foreach ($sniffs as $sniff) {
833
-                    if (substr_count($sniff, '.') !== 2) {
834
-                        $error  = 'ERROR: The specified sniff code "'.$sniff.'" is invalid'.PHP_EOL.PHP_EOL;
835
-                        $error .= $this->printShortUsage(true);
836
-                        throw new DeepExitException($error, 3);
837
-                    }
838
-                }
839
-
840
-                $this->sniffs = $sniffs;
841
-                self::$overriddenDefaults['sniffs'] = true;
842
-            } else if (substr($arg, 0, 8) === 'exclude=') {
843
-                if (isset(self::$overriddenDefaults['exclude']) === true) {
844
-                    break;
845
-                }
846
-
847
-                $sniffs = explode(',', substr($arg, 8));
848
-                foreach ($sniffs as $sniff) {
849
-                    if (substr_count($sniff, '.') !== 2) {
850
-                        $error  = 'ERROR: The specified sniff code "'.$sniff.'" is invalid'.PHP_EOL.PHP_EOL;
851
-                        $error .= $this->printShortUsage(true);
852
-                        throw new DeepExitException($error, 3);
853
-                    }
854
-                }
855
-
856
-                $this->exclude = $sniffs;
857
-                self::$overriddenDefaults['exclude'] = true;
858
-            } else if (defined('PHP_CODESNIFFER_IN_TESTS') === false
859
-                && substr($arg, 0, 6) === 'cache='
860
-            ) {
861
-                if ((isset(self::$overriddenDefaults['cache']) === true
862
-                    && $this->cache === false)
863
-                    || isset(self::$overriddenDefaults['cacheFile']) === true
864
-                ) {
865
-                    break;
866
-                }
867
-
868
-                // Turn caching on.
869
-                $this->cache = true;
870
-                self::$overriddenDefaults['cache'] = true;
871
-
872
-                $this->cacheFile = Util\Common::realpath(substr($arg, 6));
873
-
874
-                // It may not exist and return false instead.
875
-                if ($this->cacheFile === false) {
876
-                    $this->cacheFile = substr($arg, 6);
877
-
878
-                    $dir = dirname($this->cacheFile);
879
-                    if (is_dir($dir) === false) {
880
-                        $error  = 'ERROR: The specified cache file path "'.$this->cacheFile.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
881
-                        $error .= $this->printShortUsage(true);
882
-                        throw new DeepExitException($error, 3);
883
-                    }
884
-
885
-                    if ($dir === '.') {
886
-                        // Passed cache file is a file in the current directory.
887
-                        $this->cacheFile = getcwd().'/'.basename($this->cacheFile);
888
-                    } else {
889
-                        if ($dir{0} === '/') {
890
-                            // An absolute path.
891
-                            $dir = Util\Common::realpath($dir);
892
-                        } else {
893
-                            $dir = Util\Common::realpath(getcwd().'/'.$dir);
894
-                        }
895
-
896
-                        if ($dir !== false) {
897
-                            // Cache file path is relative.
898
-                            $this->cacheFile = $dir.'/'.basename($this->cacheFile);
899
-                        }
900
-                    }
901
-                }//end if
902
-
903
-                self::$overriddenDefaults['cacheFile'] = true;
904
-
905
-                if (is_dir($this->cacheFile) === true) {
906
-                    $error  = 'ERROR: The specified cache file path "'.$this->cacheFile.'" is a directory'.PHP_EOL.PHP_EOL;
907
-                    $error .= $this->printShortUsage(true);
908
-                    throw new DeepExitException($error, 3);
909
-                }
910
-            } else if (substr($arg, 0, 10) === 'bootstrap=') {
911
-                $files     = explode(',', substr($arg, 10));
912
-                $bootstrap = [];
913
-                foreach ($files as $file) {
914
-                    $path = Util\Common::realpath($file);
915
-                    if ($path === false) {
916
-                        $error  = 'ERROR: The specified bootstrap file "'.$file.'" does not exist'.PHP_EOL.PHP_EOL;
917
-                        $error .= $this->printShortUsage(true);
918
-                        throw new DeepExitException($error, 3);
919
-                    }
920
-
921
-                    $bootstrap[] = $path;
922
-                }
923
-
924
-                $this->bootstrap = array_merge($this->bootstrap, $bootstrap);
925
-                self::$overriddenDefaults['bootstrap'] = true;
926
-            } else if (substr($arg, 0, 10) === 'file-list=') {
927
-                $fileList = substr($arg, 10);
928
-                $path     = Util\Common::realpath($fileList);
929
-                if ($path === false) {
930
-                    $error  = 'ERROR: The specified file list "'.$fileList.'" does not exist'.PHP_EOL.PHP_EOL;
931
-                    $error .= $this->printShortUsage(true);
932
-                    throw new DeepExitException($error, 3);
933
-                }
934
-
935
-                $files = file($path);
936
-                foreach ($files as $inputFile) {
937
-                    $inputFile = trim($inputFile);
938
-
939
-                    // Skip empty lines.
940
-                    if ($inputFile === '') {
941
-                        continue;
942
-                    }
943
-
944
-                    $this->processFilePath($inputFile);
945
-                }
946
-            } else if (substr($arg, 0, 11) === 'stdin-path=') {
947
-                if (isset(self::$overriddenDefaults['stdinPath']) === true) {
948
-                    break;
949
-                }
950
-
951
-                $this->stdinPath = Util\Common::realpath(substr($arg, 11));
952
-
953
-                // It may not exist and return false instead, so use whatever they gave us.
954
-                if ($this->stdinPath === false) {
955
-                    $this->stdinPath = trim(substr($arg, 11));
956
-                }
957
-
958
-                self::$overriddenDefaults['stdinPath'] = true;
959
-            } else if (PHP_CODESNIFFER_CBF === false && substr($arg, 0, 12) === 'report-file=') {
960
-                if (isset(self::$overriddenDefaults['reportFile']) === true) {
961
-                    break;
962
-                }
963
-
964
-                $this->reportFile = Util\Common::realpath(substr($arg, 12));
965
-
966
-                // It may not exist and return false instead.
967
-                if ($this->reportFile === false) {
968
-                    $this->reportFile = substr($arg, 12);
969
-
970
-                    $dir = dirname($this->reportFile);
971
-                    if (is_dir($dir) === false) {
972
-                        $error  = 'ERROR: The specified report file path "'.$this->reportFile.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
973
-                        $error .= $this->printShortUsage(true);
974
-                        throw new DeepExitException($error, 3);
975
-                    }
976
-
977
-                    if ($dir === '.') {
978
-                        // Passed report file is a file in the current directory.
979
-                        $this->reportFile = getcwd().'/'.basename($this->reportFile);
980
-                    } else {
981
-                        if ($dir{0} === '/') {
982
-                            // An absolute path.
983
-                            $dir = Util\Common::realpath($dir);
984
-                        } else {
985
-                            $dir = Util\Common::realpath(getcwd().'/'.$dir);
986
-                        }
987
-
988
-                        if ($dir !== false) {
989
-                            // Report file path is relative.
990
-                            $this->reportFile = $dir.'/'.basename($this->reportFile);
991
-                        }
992
-                    }
993
-                }//end if
994
-
995
-                self::$overriddenDefaults['reportFile'] = true;
996
-
997
-                if (is_dir($this->reportFile) === true) {
998
-                    $error  = 'ERROR: The specified report file path "'.$this->reportFile.'" is a directory'.PHP_EOL.PHP_EOL;
999
-                    $error .= $this->printShortUsage(true);
1000
-                    throw new DeepExitException($error, 3);
1001
-                }
1002
-            } else if (substr($arg, 0, 13) === 'report-width=') {
1003
-                if (isset(self::$overriddenDefaults['reportWidth']) === true) {
1004
-                    break;
1005
-                }
1006
-
1007
-                $this->reportWidth = substr($arg, 13);
1008
-                self::$overriddenDefaults['reportWidth'] = true;
1009
-            } else if (substr($arg, 0, 9) === 'basepath=') {
1010
-                if (isset(self::$overriddenDefaults['basepath']) === true) {
1011
-                    break;
1012
-                }
1013
-
1014
-                self::$overriddenDefaults['basepath'] = true;
1015
-
1016
-                if (substr($arg, 9) === '') {
1017
-                    $this->basepath = null;
1018
-                    break;
1019
-                }
1020
-
1021
-                $this->basepath = Util\Common::realpath(substr($arg, 9));
1022
-
1023
-                // It may not exist and return false instead.
1024
-                if ($this->basepath === false) {
1025
-                    $this->basepath = substr($arg, 9);
1026
-                }
1027
-
1028
-                if (is_dir($this->basepath) === false) {
1029
-                    $error  = 'ERROR: The specified basepath "'.$this->basepath.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
1030
-                    $error .= $this->printShortUsage(true);
1031
-                    throw new DeepExitException($error, 3);
1032
-                }
1033
-            } else if ((substr($arg, 0, 7) === 'report=' || substr($arg, 0, 7) === 'report-')) {
1034
-                $reports = [];
1035
-
1036
-                if ($arg[6] === '-') {
1037
-                    // This is a report with file output.
1038
-                    $split = strpos($arg, '=');
1039
-                    if ($split === false) {
1040
-                        $report = substr($arg, 7);
1041
-                        $output = null;
1042
-                    } else {
1043
-                        $report = substr($arg, 7, ($split - 7));
1044
-                        $output = substr($arg, ($split + 1));
1045
-                        if ($output === false) {
1046
-                            $output = null;
1047
-                        } else {
1048
-                            $dir = dirname($output);
1049
-                            if (is_dir($dir) === false) {
1050
-                                $error  = 'ERROR: The specified '.$report.' report file path "'.$output.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
1051
-                                $error .= $this->printShortUsage(true);
1052
-                                throw new DeepExitException($error, 3);
1053
-                            }
1054
-
1055
-                            if ($dir === '.') {
1056
-                                // Passed report file is a filename in the current directory.
1057
-                                $output = getcwd().'/'.basename($output);
1058
-                            } else {
1059
-                                if ($dir{0} === '/') {
1060
-                                    // An absolute path.
1061
-                                    $dir = Util\Common::realpath($dir);
1062
-                                } else {
1063
-                                    $dir = Util\Common::realpath(getcwd().'/'.$dir);
1064
-                                }
1065
-
1066
-                                if ($dir !== false) {
1067
-                                    // Report file path is relative.
1068
-                                    $output = $dir.'/'.basename($output);
1069
-                                }
1070
-                            }
1071
-                        }//end if
1072
-                    }//end if
1073
-
1074
-                    $reports[$report] = $output;
1075
-                } else {
1076
-                    // This is a single report.
1077
-                    if (isset(self::$overriddenDefaults['reports']) === true) {
1078
-                        break;
1079
-                    }
1080
-
1081
-                    $reportNames = explode(',', substr($arg, 7));
1082
-                    foreach ($reportNames as $report) {
1083
-                        $reports[$report] = null;
1084
-                    }
1085
-                }//end if
1086
-
1087
-                // Remove the default value so the CLI value overrides it.
1088
-                if (isset(self::$overriddenDefaults['reports']) === false) {
1089
-                    $this->reports = $reports;
1090
-                } else {
1091
-                    $this->reports = array_merge($this->reports, $reports);
1092
-                }
1093
-
1094
-                self::$overriddenDefaults['reports'] = true;
1095
-            } else if (substr($arg, 0, 7) === 'filter=') {
1096
-                if (isset(self::$overriddenDefaults['filter']) === true) {
1097
-                    break;
1098
-                }
1099
-
1100
-                $this->filter = substr($arg, 7);
1101
-                self::$overriddenDefaults['filter'] = true;
1102
-            } else if (substr($arg, 0, 9) === 'standard=') {
1103
-                $standards = trim(substr($arg, 9));
1104
-                if ($standards !== '') {
1105
-                    $this->standards = explode(',', $standards);
1106
-                }
1107
-
1108
-                self::$overriddenDefaults['standards'] = true;
1109
-            } else if (substr($arg, 0, 11) === 'extensions=') {
1110
-                if (isset(self::$overriddenDefaults['extensions']) === true) {
1111
-                    break;
1112
-                }
1113
-
1114
-                $extensions    = explode(',', substr($arg, 11));
1115
-                $newExtensions = [];
1116
-                foreach ($extensions as $ext) {
1117
-                    $slash = strpos($ext, '/');
1118
-                    if ($slash !== false) {
1119
-                        // They specified the tokenizer too.
1120
-                        list($ext, $tokenizer) = explode('/', $ext);
1121
-                        $newExtensions[$ext]   = strtoupper($tokenizer);
1122
-                        continue;
1123
-                    }
1124
-
1125
-                    if (isset($this->extensions[$ext]) === true) {
1126
-                        $newExtensions[$ext] = $this->extensions[$ext];
1127
-                    } else {
1128
-                        $newExtensions[$ext] = 'PHP';
1129
-                    }
1130
-                }
1131
-
1132
-                $this->extensions = $newExtensions;
1133
-                self::$overriddenDefaults['extensions'] = true;
1134
-            } else if (substr($arg, 0, 7) === 'suffix=') {
1135
-                if (isset(self::$overriddenDefaults['suffix']) === true) {
1136
-                    break;
1137
-                }
1138
-
1139
-                $this->suffix = substr($arg, 7);
1140
-                self::$overriddenDefaults['suffix'] = true;
1141
-            } else if (substr($arg, 0, 9) === 'parallel=') {
1142
-                if (isset(self::$overriddenDefaults['parallel']) === true) {
1143
-                    break;
1144
-                }
1145
-
1146
-                $this->parallel = max((int) substr($arg, 9), 1);
1147
-                self::$overriddenDefaults['parallel'] = true;
1148
-            } else if (substr($arg, 0, 9) === 'severity=') {
1149
-                $this->errorSeverity   = (int) substr($arg, 9);
1150
-                $this->warningSeverity = $this->errorSeverity;
1151
-                if (isset(self::$overriddenDefaults['errorSeverity']) === false) {
1152
-                    self::$overriddenDefaults['errorSeverity'] = true;
1153
-                }
1154
-
1155
-                if (isset(self::$overriddenDefaults['warningSeverity']) === false) {
1156
-                    self::$overriddenDefaults['warningSeverity'] = true;
1157
-                }
1158
-            } else if (substr($arg, 0, 15) === 'error-severity=') {
1159
-                if (isset(self::$overriddenDefaults['errorSeverity']) === true) {
1160
-                    break;
1161
-                }
1162
-
1163
-                $this->errorSeverity = (int) substr($arg, 15);
1164
-                self::$overriddenDefaults['errorSeverity'] = true;
1165
-            } else if (substr($arg, 0, 17) === 'warning-severity=') {
1166
-                if (isset(self::$overriddenDefaults['warningSeverity']) === true) {
1167
-                    break;
1168
-                }
1169
-
1170
-                $this->warningSeverity = (int) substr($arg, 17);
1171
-                self::$overriddenDefaults['warningSeverity'] = true;
1172
-            } else if (substr($arg, 0, 7) === 'ignore=') {
1173
-                if (isset(self::$overriddenDefaults['ignored']) === true) {
1174
-                    break;
1175
-                }
1176
-
1177
-                // Split the ignore string on commas, unless the comma is escaped
1178
-                // using 1 or 3 slashes (\, or \\\,).
1179
-                $patterns = preg_split(
1180
-                    '/(?<=(?<!\\\\)\\\\\\\\),|(?<!\\\\),/',
1181
-                    substr($arg, 7)
1182
-                );
1183
-
1184
-                $ignored = [];
1185
-                foreach ($patterns as $pattern) {
1186
-                    $pattern = trim($pattern);
1187
-                    if ($pattern === '') {
1188
-                        continue;
1189
-                    }
1190
-
1191
-                    $ignored[$pattern] = 'absolute';
1192
-                }
1193
-
1194
-                $this->ignored = $ignored;
1195
-                self::$overriddenDefaults['ignored'] = true;
1196
-            } else if (substr($arg, 0, 10) === 'generator='
1197
-                && PHP_CODESNIFFER_CBF === false
1198
-            ) {
1199
-                if (isset(self::$overriddenDefaults['generator']) === true) {
1200
-                    break;
1201
-                }
1202
-
1203
-                $this->generator = substr($arg, 10);
1204
-                self::$overriddenDefaults['generator'] = true;
1205
-            } else if (substr($arg, 0, 9) === 'encoding=') {
1206
-                if (isset(self::$overriddenDefaults['encoding']) === true) {
1207
-                    break;
1208
-                }
1209
-
1210
-                $this->encoding = strtolower(substr($arg, 9));
1211
-                self::$overriddenDefaults['encoding'] = true;
1212
-            } else if (substr($arg, 0, 10) === 'tab-width=') {
1213
-                if (isset(self::$overriddenDefaults['tabWidth']) === true) {
1214
-                    break;
1215
-                }
1216
-
1217
-                $this->tabWidth = (int) substr($arg, 10);
1218
-                self::$overriddenDefaults['tabWidth'] = true;
1219
-            } else {
1220
-                if ($this->dieOnUnknownArg === false) {
1221
-                    $eqPos = strpos($arg, '=');
1222
-                    try {
1223
-                        if ($eqPos === false) {
1224
-                            $this->values[$arg] = $arg;
1225
-                        } else {
1226
-                            $value = substr($arg, ($eqPos + 1));
1227
-                            $arg   = substr($arg, 0, $eqPos);
1228
-                            $this->values[$arg] = $value;
1229
-                        }
1230
-                    } catch (RuntimeException $e) {
1231
-                        // Value is not valid, so just ignore it.
1232
-                    }
1233
-                } else {
1234
-                    $this->processUnknownArgument('--'.$arg, $pos);
1235
-                }
1236
-            }//end if
1237
-            break;
1238
-        }//end switch
1239
-
1240
-    }//end processLongArgument()
1241
-
1242
-
1243
-    /**
1244
-     * Processes an unknown command line argument.
1245
-     *
1246
-     * Assumes all unknown arguments are files and folders to check.
1247
-     *
1248
-     * @param string $arg The command line argument.
1249
-     * @param int    $pos The position of the argument on the command line.
1250
-     *
1251
-     * @return void
1252
-     */
1253
-    public function processUnknownArgument($arg, $pos)
1254
-    {
1255
-        // We don't know about any additional switches; just files.
1256
-        if ($arg{0} === '-') {
1257
-            if ($this->dieOnUnknownArg === false) {
1258
-                return;
1259
-            }
1260
-
1261
-            $error  = "ERROR: option \"$arg\" not known".PHP_EOL.PHP_EOL;
1262
-            $error .= $this->printShortUsage(true);
1263
-            throw new DeepExitException($error, 3);
1264
-        }
1265
-
1266
-        $this->processFilePath($arg);
1267
-
1268
-    }//end processUnknownArgument()
1269
-
1270
-
1271
-    /**
1272
-     * Processes a file path and add it to the file list.
1273
-     *
1274
-     * @param string $path The path to the file to add.
1275
-     *
1276
-     * @return void
1277
-     */
1278
-    public function processFilePath($path)
1279
-    {
1280
-        // If we are processing STDIN, don't record any files to check.
1281
-        if ($this->stdin === true) {
1282
-            return;
1283
-        }
1284
-
1285
-        $file = Util\Common::realpath($path);
1286
-        if (file_exists($file) === false) {
1287
-            if ($this->dieOnUnknownArg === false) {
1288
-                return;
1289
-            }
1290
-
1291
-            $error  = 'ERROR: The file "'.$path.'" does not exist.'.PHP_EOL.PHP_EOL;
1292
-            $error .= $this->printShortUsage(true);
1293
-            throw new DeepExitException($error, 3);
1294
-        } else {
1295
-            // Can't modify the files array directly because it's not a real
1296
-            // class member, so need to use this little get/modify/set trick.
1297
-            $files       = $this->files;
1298
-            $files[]     = $file;
1299
-            $this->files = $files;
1300
-            self::$overriddenDefaults['files'] = true;
1301
-        }
1302
-
1303
-    }//end processFilePath()
1304
-
1305
-
1306
-    /**
1307
-     * Prints out the usage information for this script.
1308
-     *
1309
-     * @return void
1310
-     */
1311
-    public function printUsage()
1312
-    {
1313
-        echo PHP_EOL;
1314
-
1315
-        if (PHP_CODESNIFFER_CBF === true) {
1316
-            $this->printPHPCBFUsage();
1317
-        } else {
1318
-            $this->printPHPCSUsage();
1319
-        }
1320
-
1321
-        echo PHP_EOL;
1322
-
1323
-    }//end printUsage()
1324
-
1325
-
1326
-    /**
1327
-     * Prints out the short usage information for this script.
1328
-     *
1329
-     * @param bool $return If TRUE, the usage string is returned
1330
-     *                     instead of output to screen.
1331
-     *
1332
-     * @return string|void
1333
-     */
1334
-    public function printShortUsage($return=false)
1335
-    {
1336
-        if (PHP_CODESNIFFER_CBF === true) {
1337
-            $usage = 'Run "phpcbf --help" for usage information';
1338
-        } else {
1339
-            $usage = 'Run "phpcs --help" for usage information';
1340
-        }
1341
-
1342
-        $usage .= PHP_EOL.PHP_EOL;
1343
-
1344
-        if ($return === true) {
1345
-            return $usage;
1346
-        }
1347
-
1348
-        echo $usage;
1349
-
1350
-    }//end printShortUsage()
1351
-
1352
-
1353
-    /**
1354
-     * Prints out the usage information for PHPCS.
1355
-     *
1356
-     * @return void
1357
-     */
1358
-    public function printPHPCSUsage()
1359
-    {
1360
-        echo 'Usage: phpcs [-nwlsaepqvi] [-d key[=value]] [--colors] [--no-colors]'.PHP_EOL;
1361
-        echo '  [--cache[=<cacheFile>]] [--no-cache] [--tab-width=<tabWidth>]'.PHP_EOL;
1362
-        echo '  [--report=<report>] [--report-file=<reportFile>] [--report-<report>=<reportFile>]'.PHP_EOL;
1363
-        echo '  [--report-width=<reportWidth>] [--basepath=<basepath>] [--bootstrap=<bootstrap>]'.PHP_EOL;
1364
-        echo '  [--severity=<severity>] [--error-severity=<severity>] [--warning-severity=<severity>]'.PHP_EOL;
1365
-        echo '  [--runtime-set key value] [--config-set key value] [--config-delete key] [--config-show]'.PHP_EOL;
1366
-        echo '  [--standard=<standard>] [--sniffs=<sniffs>] [--exclude=<sniffs>]'.PHP_EOL;
1367
-        echo '  [--encoding=<encoding>] [--parallel=<processes>] [--generator=<generator>]'.PHP_EOL;
1368
-        echo '  [--extensions=<extensions>] [--ignore=<patterns>] [--ignore-annotations]'.PHP_EOL;
1369
-        echo '  [--stdin-path=<stdinPath>] [--file-list=<fileList>] [--filter=<filter>] <file> - ...'.PHP_EOL;
1370
-        echo PHP_EOL;
1371
-        echo ' -     Check STDIN instead of local files and directories'.PHP_EOL;
1372
-        echo ' -n    Do not print warnings (shortcut for --warning-severity=0)'.PHP_EOL;
1373
-        echo ' -w    Print both warnings and errors (this is the default)'.PHP_EOL;
1374
-        echo ' -l    Local directory only, no recursion'.PHP_EOL;
1375
-        echo ' -s    Show sniff codes in all reports'.PHP_EOL;
1376
-        echo ' -a    Run interactively'.PHP_EOL;
1377
-        echo ' -e    Explain a standard by showing the sniffs it includes'.PHP_EOL;
1378
-        echo ' -p    Show progress of the run'.PHP_EOL;
1379
-        echo ' -q    Quiet mode; disables progress and verbose output'.PHP_EOL;
1380
-        echo ' -m    Stop error messages from being recorded'.PHP_EOL;
1381
-        echo '       (saves a lot of memory, but stops many reports from being used)'.PHP_EOL;
1382
-        echo ' -v    Print processed files'.PHP_EOL;
1383
-        echo ' -vv   Print ruleset and token output'.PHP_EOL;
1384
-        echo ' -vvv  Print sniff processing information'.PHP_EOL;
1385
-        echo ' -i    Show a list of installed coding standards'.PHP_EOL;
1386
-        echo ' -d    Set the [key] php.ini value to [value] or [true] if value is omitted'.PHP_EOL;
1387
-        echo PHP_EOL;
1388
-        echo ' --help                Print this help message'.PHP_EOL;
1389
-        echo ' --version             Print version information'.PHP_EOL;
1390
-        echo ' --colors              Use colors in output'.PHP_EOL;
1391
-        echo ' --no-colors           Do not use colors in output (this is the default)'.PHP_EOL;
1392
-        echo ' --cache               Cache results between runs'.PHP_EOL;
1393
-        echo ' --no-cache            Do not cache results between runs (this is the default)'.PHP_EOL;
1394
-        echo ' --ignore-annotations  Ignore all phpcs: annotations in code comments'.PHP_EOL;
1395
-        echo PHP_EOL;
1396
-        echo ' <cacheFile>    Use a specific file for caching (uses a temporary file by default)'.PHP_EOL;
1397
-        echo ' <basepath>     A path to strip from the front of file paths inside reports'.PHP_EOL;
1398
-        echo ' <bootstrap>    A comma separated list of files to run before processing begins'.PHP_EOL;
1399
-        echo ' <encoding>     The encoding of the files being checked (default is utf-8)'.PHP_EOL;
1400
-        echo ' <extensions>   A comma separated list of file extensions to check'.PHP_EOL;
1401
-        echo '                The type of the file can be specified using: ext/type'.PHP_EOL;
1402
-        echo '                e.g., module/php,es/js'.PHP_EOL;
1403
-        echo ' <file>         One or more files and/or directories to check'.PHP_EOL;
1404
-        echo ' <fileList>     A file containing a list of files and/or directories to check (one per line)'.PHP_EOL;
1405
-        echo ' <filter>       Use the "gitmodified" filter, or specify the path to a custom filter class'.PHP_EOL;
1406
-        echo ' <generator>    Uses either the "HTML", "Markdown" or "Text" generator'.PHP_EOL;
1407
-        echo '                (forces documentation generation instead of checking)'.PHP_EOL;
1408
-        echo ' <patterns>     A comma separated list of patterns to ignore files and directories'.PHP_EOL;
1409
-        echo ' <processes>    How many files should be checked simultaneously (default is 1)'.PHP_EOL;
1410
-        echo ' <report>       Print either the "full", "xml", "checkstyle", "csv"'.PHP_EOL;
1411
-        echo '                "json", "junit", "emacs", "source", "summary", "diff"'.PHP_EOL;
1412
-        echo '                "svnblame", "gitblame", "hgblame" or "notifysend" report,'.PHP_EOL;
1413
-        echo '                or specify the path to a custom report class'.PHP_EOL;
1414
-        echo '                (the "full" report is printed by default)'.PHP_EOL;
1415
-        echo ' <reportFile>   Write the report to the specified file path'.PHP_EOL;
1416
-        echo ' <reportWidth>  How many columns wide screen reports should be printed'.PHP_EOL;
1417
-        echo '                or set to "auto" to use current screen width, where supported'.PHP_EOL;
1418
-        echo ' <severity>     The minimum severity required to display an error or warning'.PHP_EOL;
1419
-        echo ' <sniffs>       A comma separated list of sniff codes to include or exclude from checking'.PHP_EOL;
1420
-        echo '                (all sniffs must be part of the specified standard)'.PHP_EOL;
1421
-        echo ' <standard>     The name or path of the coding standard to use'.PHP_EOL;
1422
-        echo ' <stdinPath>    If processing STDIN, the file path that STDIN will be processed as'.PHP_EOL;
1423
-        echo ' <tabWidth>     The number of spaces each tab represents'.PHP_EOL;
1424
-
1425
-    }//end printPHPCSUsage()
1426
-
1427
-
1428
-    /**
1429
-     * Prints out the usage information for PHPCBF.
1430
-     *
1431
-     * @return void
1432
-     */
1433
-    public function printPHPCBFUsage()
1434
-    {
1435
-        echo 'Usage: phpcbf [-nwli] [-d key[=value]] [--ignore-annotations] [--bootstrap=<bootstrap>]'.PHP_EOL;
1436
-        echo '  [--standard=<standard>] [--sniffs=<sniffs>] [--exclude=<sniffs>] [--suffix=<suffix>]'.PHP_EOL;
1437
-        echo '  [--severity=<severity>] [--error-severity=<severity>] [--warning-severity=<severity>]'.PHP_EOL;
1438
-        echo '  [--tab-width=<tabWidth>] [--encoding=<encoding>] [--parallel=<processes>]'.PHP_EOL;
1439
-        echo '  [--basepath=<basepath>] [--extensions=<extensions>] [--ignore=<patterns>]'.PHP_EOL;
1440
-        echo '  [--stdin-path=<stdinPath>] [--file-list=<fileList>] [--filter=<filter>] <file> - ...'.PHP_EOL;
1441
-        echo PHP_EOL;
1442
-        echo ' -     Fix STDIN instead of local files and directories'.PHP_EOL;
1443
-        echo ' -n    Do not fix warnings (shortcut for --warning-severity=0)'.PHP_EOL;
1444
-        echo ' -w    Fix both warnings and errors (on by default)'.PHP_EOL;
1445
-        echo ' -l    Local directory only, no recursion'.PHP_EOL;
1446
-        echo ' -p    Show progress of the run'.PHP_EOL;
1447
-        echo ' -q    Quiet mode; disables progress and verbose output'.PHP_EOL;
1448
-        echo ' -v    Print processed files'.PHP_EOL;
1449
-        echo ' -vv   Print ruleset and token output'.PHP_EOL;
1450
-        echo ' -vvv  Print sniff processing information'.PHP_EOL;
1451
-        echo ' -i    Show a list of installed coding standards'.PHP_EOL;
1452
-        echo ' -d    Set the [key] php.ini value to [value] or [true] if value is omitted'.PHP_EOL;
1453
-        echo PHP_EOL;
1454
-        echo ' --help                Print this help message'.PHP_EOL;
1455
-        echo ' --version             Print version information'.PHP_EOL;
1456
-        echo ' --ignore-annotations  Ignore all phpcs: annotations in code comments'.PHP_EOL;
1457
-        echo PHP_EOL;
1458
-        echo ' <basepath>    A path to strip from the front of file paths inside reports'.PHP_EOL;
1459
-        echo ' <bootstrap>   A comma separated list of files to run before processing begins'.PHP_EOL;
1460
-        echo ' <encoding>    The encoding of the files being fixed (default is utf-8)'.PHP_EOL;
1461
-        echo ' <extensions>  A comma separated list of file extensions to fix'.PHP_EOL;
1462
-        echo '               The type of the file can be specified using: ext/type'.PHP_EOL;
1463
-        echo '               e.g., module/php,es/js'.PHP_EOL;
1464
-        echo ' <file>        One or more files and/or directories to fix'.PHP_EOL;
1465
-        echo ' <fileList>    A file containing a list of files and/or directories to fix (one per line)'.PHP_EOL;
1466
-        echo ' <filter>      Use the "gitmodified" filter, or specify the path to a custom filter class'.PHP_EOL;
1467
-        echo ' <patterns>    A comma separated list of patterns to ignore files and directories'.PHP_EOL;
1468
-        echo ' <processes>   How many files should be fixed simultaneously (default is 1)'.PHP_EOL;
1469
-        echo ' <severity>    The minimum severity required to fix an error or warning'.PHP_EOL;
1470
-        echo ' <sniffs>      A comma separated list of sniff codes to include or exclude from fixing'.PHP_EOL;
1471
-        echo '               (all sniffs must be part of the specified standard)'.PHP_EOL;
1472
-        echo ' <standard>    The name or path of the coding standard to use'.PHP_EOL;
1473
-        echo ' <stdinPath>   If processing STDIN, the file path that STDIN will be processed as'.PHP_EOL;
1474
-        echo ' <suffix>      Write modified files to a filename using this suffix'.PHP_EOL;
1475
-        echo '               ("diff" and "patch" are not used in this mode)'.PHP_EOL;
1476
-        echo ' <tabWidth>    The number of spaces each tab represents'.PHP_EOL;
1477
-
1478
-    }//end printPHPCBFUsage()
1479
-
1480
-
1481
-    /**
1482
-     * Get a single config value.
1483
-     *
1484
-     * @param string $key The name of the config value.
1485
-     *
1486
-     * @return string|null
1487
-     * @see    setConfigData()
1488
-     * @see    getAllConfigData()
1489
-     */
1490
-    public static function getConfigData($key)
1491
-    {
1492
-        $phpCodeSnifferConfig = self::getAllConfigData();
1493
-
1494
-        if ($phpCodeSnifferConfig === null) {
1495
-            return null;
1496
-        }
1497
-
1498
-        if (isset($phpCodeSnifferConfig[$key]) === false) {
1499
-            return null;
1500
-        }
1501
-
1502
-        return $phpCodeSnifferConfig[$key];
1503
-
1504
-    }//end getConfigData()
1505
-
1506
-
1507
-    /**
1508
-     * Get the path to an executable utility.
1509
-     *
1510
-     * @param string $name The name of the executable utility.
1511
-     *
1512
-     * @return string|null
1513
-     * @see    getConfigData()
1514
-     */
1515
-    public static function getExecutablePath($name)
1516
-    {
1517
-        $data = self::getConfigData($name.'_path');
1518
-        if ($data !== null) {
1519
-            return $data;
1520
-        }
1521
-
1522
-        if ($name === "php") {
1523
-            // For php, we know the executable path. There's no need to look it up.
1524
-            return PHP_BINARY;
1525
-        }
1526
-
1527
-        if (array_key_exists($name, self::$executablePaths) === true) {
1528
-            return self::$executablePaths[$name];
1529
-        }
1530
-
1531
-        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
1532
-            $cmd = 'where '.escapeshellarg($name).' 2> nul';
1533
-        } else {
1534
-            $cmd = 'which '.escapeshellarg($name).' 2> /dev/null';
1535
-        }
1536
-
1537
-        $result = exec($cmd, $output, $retVal);
1538
-        if ($retVal !== 0) {
1539
-            $result = null;
1540
-        }
1541
-
1542
-        self::$executablePaths[$name] = $result;
1543
-        return $result;
1544
-
1545
-    }//end getExecutablePath()
1546
-
1547
-
1548
-    /**
1549
-     * Set a single config value.
1550
-     *
1551
-     * @param string      $key   The name of the config value.
1552
-     * @param string|null $value The value to set. If null, the config
1553
-     *                           entry is deleted, reverting it to the
1554
-     *                           default value.
1555
-     * @param boolean     $temp  Set this config data temporarily for this
1556
-     *                           script run. This will not write the config
1557
-     *                           data to the config file.
1558
-     *
1559
-     * @return bool
1560
-     * @see    getConfigData()
1561
-     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the config file can not be written.
1562
-     */
1563
-    public static function setConfigData($key, $value, $temp=false)
1564
-    {
1565
-        if (isset(self::$overriddenDefaults['runtime-set']) === true
1566
-            && isset(self::$overriddenDefaults['runtime-set'][$key]) === true
1567
-        ) {
1568
-            return false;
1569
-        }
1570
-
1571
-        if ($temp === false) {
1572
-            $path = '';
1573
-            if (is_callable('\Phar::running') === true) {
1574
-                $path = \Phar::running(false);
1575
-            }
1576
-
1577
-            if ($path !== '') {
1578
-                $configFile = dirname($path).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
1579
-            } else {
1580
-                $configFile = dirname(__DIR__).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
1581
-                if (is_file($configFile) === false
1582
-                    && strpos('@data_dir@', '@data_dir') === false
1583
-                ) {
1584
-                    // If data_dir was replaced, this is a PEAR install and we can
1585
-                    // use the PEAR data dir to store the conf file.
1586
-                    $configFile = '@data_dir@/PHP_CodeSniffer/CodeSniffer.conf';
1587
-                }
1588
-            }
1589
-
1590
-            if (is_file($configFile) === true
1591
-                && is_writable($configFile) === false
1592
-            ) {
1593
-                $error = 'ERROR: Config file '.$configFile.' is not writable'.PHP_EOL.PHP_EOL;
1594
-                throw new DeepExitException($error, 3);
1595
-            }
1596
-        }//end if
1597
-
1598
-        $phpCodeSnifferConfig = self::getAllConfigData();
1599
-
1600
-        if ($value === null) {
1601
-            if (isset($phpCodeSnifferConfig[$key]) === true) {
1602
-                unset($phpCodeSnifferConfig[$key]);
1603
-            }
1604
-        } else {
1605
-            $phpCodeSnifferConfig[$key] = $value;
1606
-        }
1607
-
1608
-        if ($temp === false) {
1609
-            $output  = '<'.'?php'."\n".' $phpCodeSnifferConfig = ';
1610
-            $output .= var_export($phpCodeSnifferConfig, true);
1611
-            $output .= "\n?".'>';
1612
-
1613
-            if (file_put_contents($configFile, $output) === false) {
1614
-                $error = 'ERROR: Config file '.$configFile.' could not be written'.PHP_EOL.PHP_EOL;
1615
-                throw new DeepExitException($error, 3);
1616
-            }
1617
-
1618
-            self::$configDataFile = $configFile;
1619
-        }
1620
-
1621
-        self::$configData = $phpCodeSnifferConfig;
1622
-
1623
-        // If the installed paths are being set, make sure all known
1624
-        // standards paths are added to the autoloader.
1625
-        if ($key === 'installed_paths') {
1626
-            $installedStandards = Util\Standards::getInstalledStandardDetails();
1627
-            foreach ($installedStandards as $name => $details) {
1628
-                Autoload::addSearchPath($details['path'], $details['namespace']);
1629
-            }
1630
-        }
1631
-
1632
-        return true;
1633
-
1634
-    }//end setConfigData()
1635
-
1636
-
1637
-    /**
1638
-     * Get all config data.
1639
-     *
1640
-     * @return array<string, string>
1641
-     * @see    getConfigData()
1642
-     */
1643
-    public static function getAllConfigData()
1644
-    {
1645
-        if (self::$configData !== null) {
1646
-            return self::$configData;
1647
-        }
1648
-
1649
-        $path = '';
1650
-        if (is_callable('\Phar::running') === true) {
1651
-            $path = \Phar::running(false);
1652
-        }
1653
-
1654
-        if ($path !== '') {
1655
-            $configFile = dirname($path).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
1656
-        } else {
1657
-            $configFile = dirname(__DIR__).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
1658
-            if (is_file($configFile) === false
1659
-                && strpos('@data_dir@', '@data_dir') === false
1660
-            ) {
1661
-                $configFile = '@data_dir@/PHP_CodeSniffer/CodeSniffer.conf';
1662
-            }
1663
-        }
1664
-
1665
-        if (is_file($configFile) === false) {
1666
-            self::$configData = [];
1667
-            return [];
1668
-        }
1669
-
1670
-        if (is_readable($configFile) === false) {
1671
-            $error = 'ERROR: Config file '.$configFile.' is not readable'.PHP_EOL.PHP_EOL;
1672
-            throw new DeepExitException($error, 3);
1673
-        }
1674
-
1675
-        include $configFile;
1676
-        self::$configDataFile = $configFile;
1677
-        self::$configData     = $phpCodeSnifferConfig;
1678
-        return self::$configData;
1679
-
1680
-    }//end getAllConfigData()
1681
-
1682
-
1683
-    /**
1684
-     * Prints out the gathered config data.
1685
-     *
1686
-     * @param array $data The config data to print.
1687
-     *
1688
-     * @return void
1689
-     */
1690
-    public function printConfigData($data)
1691
-    {
1692
-        $max  = 0;
1693
-        $keys = array_keys($data);
1694
-        foreach ($keys as $key) {
1695
-            $len = strlen($key);
1696
-            if (strlen($key) > $max) {
1697
-                $max = $len;
1698
-            }
1699
-        }
1700
-
1701
-        if ($max === 0) {
1702
-            return;
1703
-        }
1704
-
1705
-        $max += 2;
1706
-        ksort($data);
1707
-        foreach ($data as $name => $value) {
1708
-            echo str_pad($name.': ', $max).$value.PHP_EOL;
1709
-        }
1710
-
1711
-    }//end printConfigData()
21
+	/**
22
+	 * The current version.
23
+	 *
24
+	 * @var string
25
+	 */
26
+	const VERSION = '3.4.2';
27
+
28
+	/**
29
+	 * Package stability; either stable, beta or alpha.
30
+	 *
31
+	 * @var string
32
+	 */
33
+	const STABILITY = 'stable';
34
+
35
+	/**
36
+	 * An array of settings that PHPCS and PHPCBF accept.
37
+	 *
38
+	 * This array is not meant to be accessed directly. Instead, use the settings
39
+	 * as if they are class member vars so the __get() and __set() magic methods
40
+	 * can be used to validate the values. For example, to set the verbosity level to
41
+	 * level 2, use $this->verbosity = 2; instead of accessing this property directly.
42
+	 *
43
+	 * The list of settings are:
44
+	 *
45
+	 * string[] files           The files and directories to check.
46
+	 * string[] standards       The standards being used for checking.
47
+	 * int      verbosity       How verbose the output should be.
48
+	 *                          0: no unnecessary output
49
+	 *                          1: basic output for files being checked
50
+	 *                          2: ruleset and file parsing output
51
+	 *                          3: sniff execution output
52
+	 * bool     interactive     Enable interactive checking mode.
53
+	 * bool     parallel        Check files in parallel.
54
+	 * bool     cache           Enable the use of the file cache.
55
+	 * bool     cacheFile       A file where the cache data should be written
56
+	 * bool     colors          Display colours in output.
57
+	 * bool     explain         Explain the coding standards.
58
+	 * bool     local           Process local files in directories only (no recursion).
59
+	 * bool     showSources     Show sniff source codes in report output.
60
+	 * bool     showProgress    Show basic progress information while running.
61
+	 * bool     quiet           Quiet mode; disables progress and verbose output.
62
+	 * bool     annotations     Process phpcs: annotations.
63
+	 * int      tabWidth        How many spaces each tab is worth.
64
+	 * string   encoding        The encoding of the files being checked.
65
+	 * string[] sniffs          The sniffs that should be used for checking.
66
+	 *                          If empty, all sniffs in the supplied standards will be used.
67
+	 * string[] exclude         The sniffs that should be excluded from checking.
68
+	 *                          If empty, all sniffs in the supplied standards will be used.
69
+	 * string[] ignored         Regular expressions used to ignore files and folders during checking.
70
+	 * string   reportFile      A file where the report output should be written.
71
+	 * string   generator       The documentation generator to use.
72
+	 * string   filter          The filter to use for the run.
73
+	 * string[] bootstrap       One of more files to include before the run begins.
74
+	 * int      reportWidth     The maximum number of columns that reports should use for output.
75
+	 *                          Set to "auto" for have this value changed to the width of the terminal.
76
+	 * int      errorSeverity   The minimum severity an error must have to be displayed.
77
+	 * int      warningSeverity The minimum severity a warning must have to be displayed.
78
+	 * bool     recordErrors    Record the content of error messages as well as error counts.
79
+	 * string   suffix          A suffix to add to fixed files.
80
+	 * string   basepath        A file system location to strip from the paths of files shown in reports.
81
+	 * bool     stdin           Read content from STDIN instead of supplied files.
82
+	 * string   stdinContent    Content passed directly to PHPCS on STDIN.
83
+	 * string   stdinPath       The path to use for content passed on STDIN.
84
+	 *
85
+	 * array<string, string>      extensions File extensions that should be checked, and what tokenizer to use.
86
+	 *                                       E.g., array('inc' => 'PHP');
87
+	 * array<string, string|null> reports    The reports to use for printing output after the run.
88
+	 *                                       The format of the array is:
89
+	 *                                           array(
90
+	 *                                            'reportName1' => 'outputFile',
91
+	 *                                            'reportName2' => null,
92
+	 *                                           );
93
+	 *                                       If the array value is NULL, the report will be written to the screen.
94
+	 *
95
+	 * string[] unknown Any arguments gathered on the command line that are unknown to us.
96
+	 *                  E.g., using `phpcs -c` will give array('c');
97
+	 *
98
+	 * @var array<string, mixed>
99
+	 */
100
+	private $settings = [
101
+		'files'           => null,
102
+		'standards'       => null,
103
+		'verbosity'       => null,
104
+		'interactive'     => null,
105
+		'parallel'        => null,
106
+		'cache'           => null,
107
+		'cacheFile'       => null,
108
+		'colors'          => null,
109
+		'explain'         => null,
110
+		'local'           => null,
111
+		'showSources'     => null,
112
+		'showProgress'    => null,
113
+		'quiet'           => null,
114
+		'annotations'     => null,
115
+		'tabWidth'        => null,
116
+		'encoding'        => null,
117
+		'extensions'      => null,
118
+		'sniffs'          => null,
119
+		'exclude'         => null,
120
+		'ignored'         => null,
121
+		'reportFile'      => null,
122
+		'generator'       => null,
123
+		'filter'          => null,
124
+		'bootstrap'       => null,
125
+		'reports'         => null,
126
+		'basepath'        => null,
127
+		'reportWidth'     => null,
128
+		'errorSeverity'   => null,
129
+		'warningSeverity' => null,
130
+		'recordErrors'    => null,
131
+		'suffix'          => null,
132
+		'stdin'           => null,
133
+		'stdinContent'    => null,
134
+		'stdinPath'       => null,
135
+		'unknown'         => null,
136
+	];
137
+
138
+	/**
139
+	 * Whether or not to kill the process when an unknown command line arg is found.
140
+	 *
141
+	 * If FALSE, arguments that are not command line options or file/directory paths
142
+	 * will be ignored and execution will continue. These values will be stored in
143
+	 * $this->unknown.
144
+	 *
145
+	 * @var boolean
146
+	 */
147
+	public $dieOnUnknownArg;
148
+
149
+	/**
150
+	 * The current command line arguments we are processing.
151
+	 *
152
+	 * @var string[]
153
+	 */
154
+	private $cliArgs = [];
155
+
156
+	/**
157
+	 * Command line values that the user has supplied directly.
158
+	 *
159
+	 * @var array<string, TRUE>
160
+	 */
161
+	private static $overriddenDefaults = [];
162
+
163
+	/**
164
+	 * Config file data that has been loaded for the run.
165
+	 *
166
+	 * @var array<string, string>
167
+	 */
168
+	private static $configData = null;
169
+
170
+	/**
171
+	 * The full path to the config data file that has been loaded.
172
+	 *
173
+	 * @var string
174
+	 */
175
+	private static $configDataFile = null;
176
+
177
+	/**
178
+	 * Automatically discovered executable utility paths.
179
+	 *
180
+	 * @var array<string, string>
181
+	 */
182
+	private static $executablePaths = [];
183
+
184
+
185
+	/**
186
+	 * Get the value of an inaccessible property.
187
+	 *
188
+	 * @param string $name The name of the property.
189
+	 *
190
+	 * @return mixed
191
+	 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the setting name is invalid.
192
+	 */
193
+	public function __get($name)
194
+	{
195
+		if (array_key_exists($name, $this->settings) === false) {
196
+			throw new RuntimeException("ERROR: unable to get value of property \"$name\"");
197
+		}
198
+
199
+		return $this->settings[$name];
200
+
201
+	}//end __get()
202
+
203
+
204
+	/**
205
+	 * Set the value of an inaccessible property.
206
+	 *
207
+	 * @param string $name  The name of the property.
208
+	 * @param mixed  $value The value of the property.
209
+	 *
210
+	 * @return void
211
+	 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the setting name is invalid.
212
+	 */
213
+	public function __set($name, $value)
214
+	{
215
+		if (array_key_exists($name, $this->settings) === false) {
216
+			throw new RuntimeException("Can't __set() $name; setting doesn't exist");
217
+		}
218
+
219
+		switch ($name) {
220
+		case 'reportWidth' :
221
+			// Support auto terminal width.
222
+			if ($value === 'auto' && preg_match('|\d+ (\d+)|', shell_exec('stty size 2>&1'), $matches) === 1) {
223
+				$value = (int) $matches[1];
224
+			} else {
225
+				$value = (int) $value;
226
+			}
227
+			break;
228
+		case 'standards' :
229
+			$cleaned = [];
230
+
231
+			// Check if the standard name is valid, or if the case is invalid.
232
+			$installedStandards = Util\Standards::getInstalledStandards();
233
+			foreach ($value as $standard) {
234
+				foreach ($installedStandards as $validStandard) {
235
+					if (strtolower($standard) === strtolower($validStandard)) {
236
+						$standard = $validStandard;
237
+						break;
238
+					}
239
+				}
240
+
241
+				$cleaned[] = $standard;
242
+			}
243
+
244
+			$value = $cleaned;
245
+			break;
246
+		default :
247
+			// No validation required.
248
+			break;
249
+		}//end switch
250
+
251
+		$this->settings[$name] = $value;
252
+
253
+	}//end __set()
254
+
255
+
256
+	/**
257
+	 * Check if the value of an inaccessible property is set.
258
+	 *
259
+	 * @param string $name The name of the property.
260
+	 *
261
+	 * @return bool
262
+	 */
263
+	public function __isset($name)
264
+	{
265
+		return isset($this->settings[$name]);
266
+
267
+	}//end __isset()
268
+
269
+
270
+	/**
271
+	 * Unset the value of an inaccessible property.
272
+	 *
273
+	 * @param string $name The name of the property.
274
+	 *
275
+	 * @return void
276
+	 */
277
+	public function __unset($name)
278
+	{
279
+		$this->settings[$name] = null;
280
+
281
+	}//end __unset()
282
+
283
+
284
+	/**
285
+	 * Get the array of all config settings.
286
+	 *
287
+	 * @return array<string, mixed>
288
+	 */
289
+	public function getSettings()
290
+	{
291
+		return $this->settings;
292
+
293
+	}//end getSettings()
294
+
295
+
296
+	/**
297
+	 * Set the array of all config settings.
298
+	 *
299
+	 * @param array<string, mixed> $settings The array of config settings.
300
+	 *
301
+	 * @return void
302
+	 */
303
+	public function setSettings($settings)
304
+	{
305
+		return $this->settings = $settings;
306
+
307
+	}//end setSettings()
308
+
309
+
310
+	/**
311
+	 * Creates a Config object and populates it with command line values.
312
+	 *
313
+	 * @param array $cliArgs         An array of values gathered from CLI args.
314
+	 * @param bool  $dieOnUnknownArg Whether or not to kill the process when an
315
+	 *                               unknown command line arg is found.
316
+	 *
317
+	 * @return void
318
+	 */
319
+	public function __construct(array $cliArgs=[], $dieOnUnknownArg=true)
320
+	{
321
+		if (defined('PHP_CODESNIFFER_IN_TESTS') === true) {
322
+			// Let everything through during testing so that we can
323
+			// make use of PHPUnit command line arguments as well.
324
+			$this->dieOnUnknownArg = false;
325
+		} else {
326
+			$this->dieOnUnknownArg = $dieOnUnknownArg;
327
+		}
328
+
329
+		if (empty($cliArgs) === true) {
330
+			$cliArgs = $_SERVER['argv'];
331
+			array_shift($cliArgs);
332
+		}
333
+
334
+		$this->restoreDefaults();
335
+		$this->setCommandLineValues($cliArgs);
336
+
337
+		if (isset(self::$overriddenDefaults['standards']) === false) {
338
+			// They did not supply a standard to use.
339
+			// Look for a default ruleset in the current directory or higher.
340
+			$currentDir = getcwd();
341
+
342
+			$defaultFiles = [
343
+				'.phpcs.xml',
344
+				'phpcs.xml',
345
+				'.phpcs.xml.dist',
346
+				'phpcs.xml.dist',
347
+			];
348
+
349
+			do {
350
+				foreach ($defaultFiles as $defaultFilename) {
351
+					$default = $currentDir.DIRECTORY_SEPARATOR.$defaultFilename;
352
+					if (is_file($default) === true) {
353
+						$this->standards = [$default];
354
+						break(2);
355
+					}
356
+				}
357
+
358
+				$lastDir    = $currentDir;
359
+				$currentDir = dirname($currentDir);
360
+			} while ($currentDir !== '.' && $currentDir !== $lastDir);
361
+		}//end if
362
+
363
+		if (defined('STDIN') === false
364
+			|| strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'
365
+		) {
366
+			return;
367
+		}
368
+
369
+		$handle = fopen('php://stdin', 'r');
370
+
371
+		// Check for content on STDIN.
372
+		if ($this->stdin === true
373
+			|| (Util\Common::isStdinATTY() === false
374
+			&& feof($handle) === false)
375
+		) {
376
+			$readStreams = [$handle];
377
+			$writeSteams = null;
378
+
379
+			$fileContents = '';
380
+			while (is_resource($handle) === true && feof($handle) === false) {
381
+				// Set a timeout of 200ms.
382
+				if (stream_select($readStreams, $writeSteams, $writeSteams, 0, 200000) === 0) {
383
+					break;
384
+				}
385
+
386
+				$fileContents .= fgets($handle);
387
+			}
388
+
389
+			if (trim($fileContents) !== '') {
390
+				$this->stdin        = true;
391
+				$this->stdinContent = $fileContents;
392
+				self::$overriddenDefaults['stdin']        = true;
393
+				self::$overriddenDefaults['stdinContent'] = true;
394
+			}
395
+		}//end if
396
+
397
+		fclose($handle);
398
+
399
+	}//end __construct()
400
+
401
+
402
+	/**
403
+	 * Set the command line values.
404
+	 *
405
+	 * @param array $args An array of command line arguments to set.
406
+	 *
407
+	 * @return void
408
+	 */
409
+	public function setCommandLineValues($args)
410
+	{
411
+		$this->cliArgs = $args;
412
+		$numArgs       = count($args);
413
+
414
+		for ($i = 0; $i < $numArgs; $i++) {
415
+			$arg = $this->cliArgs[$i];
416
+			if ($arg === '') {
417
+				continue;
418
+			}
419
+
420
+			if ($arg{0} === '-') {
421
+				if ($arg === '-') {
422
+					// Asking to read from STDIN.
423
+					$this->stdin = true;
424
+					self::$overriddenDefaults['stdin'] = true;
425
+					continue;
426
+				}
427
+
428
+				if ($arg === '--') {
429
+					// Empty argument, ignore it.
430
+					continue;
431
+				}
432
+
433
+				if ($arg{1} === '-') {
434
+					$this->processLongArgument(substr($arg, 2), $i);
435
+				} else {
436
+					$switches = str_split($arg);
437
+					foreach ($switches as $switch) {
438
+						if ($switch === '-') {
439
+							continue;
440
+						}
441
+
442
+						$this->processShortArgument($switch, $i);
443
+					}
444
+				}
445
+			} else {
446
+				$this->processUnknownArgument($arg, $i);
447
+			}//end if
448
+		}//end for
449
+
450
+	}//end setCommandLineValues()
451
+
452
+
453
+	/**
454
+	 * Restore default values for all possible command line arguments.
455
+	 *
456
+	 * @return array
457
+	 */
458
+	public function restoreDefaults()
459
+	{
460
+		$this->files           = [];
461
+		$this->standards       = ['PEAR'];
462
+		$this->verbosity       = 0;
463
+		$this->interactive     = false;
464
+		$this->cache           = false;
465
+		$this->cacheFile       = null;
466
+		$this->colors          = false;
467
+		$this->explain         = false;
468
+		$this->local           = false;
469
+		$this->showSources     = false;
470
+		$this->showProgress    = false;
471
+		$this->quiet           = false;
472
+		$this->annotations     = true;
473
+		$this->parallel        = 1;
474
+		$this->tabWidth        = 0;
475
+		$this->encoding        = 'utf-8';
476
+		$this->extensions      = [
477
+			'php' => 'PHP',
478
+			'inc' => 'PHP',
479
+			'js'  => 'JS',
480
+			'css' => 'CSS',
481
+		];
482
+		$this->sniffs          = [];
483
+		$this->exclude         = [];
484
+		$this->ignored         = [];
485
+		$this->reportFile      = null;
486
+		$this->generator       = null;
487
+		$this->filter          = null;
488
+		$this->bootstrap       = [];
489
+		$this->basepath        = null;
490
+		$this->reports         = ['full' => null];
491
+		$this->reportWidth     = 'auto';
492
+		$this->errorSeverity   = 5;
493
+		$this->warningSeverity = 5;
494
+		$this->recordErrors    = true;
495
+		$this->suffix          = '';
496
+		$this->stdin           = false;
497
+		$this->stdinContent    = null;
498
+		$this->stdinPath       = null;
499
+		$this->unknown         = [];
500
+
501
+		$standard = self::getConfigData('default_standard');
502
+		if ($standard !== null) {
503
+			$this->standards = explode(',', $standard);
504
+		}
505
+
506
+		$reportFormat = self::getConfigData('report_format');
507
+		if ($reportFormat !== null) {
508
+			$this->reports = [$reportFormat => null];
509
+		}
510
+
511
+		$tabWidth = self::getConfigData('tab_width');
512
+		if ($tabWidth !== null) {
513
+			$this->tabWidth = (int) $tabWidth;
514
+		}
515
+
516
+		$encoding = self::getConfigData('encoding');
517
+		if ($encoding !== null) {
518
+			$this->encoding = strtolower($encoding);
519
+		}
520
+
521
+		$severity = self::getConfigData('severity');
522
+		if ($severity !== null) {
523
+			$this->errorSeverity   = (int) $severity;
524
+			$this->warningSeverity = (int) $severity;
525
+		}
526
+
527
+		$severity = self::getConfigData('error_severity');
528
+		if ($severity !== null) {
529
+			$this->errorSeverity = (int) $severity;
530
+		}
531
+
532
+		$severity = self::getConfigData('warning_severity');
533
+		if ($severity !== null) {
534
+			$this->warningSeverity = (int) $severity;
535
+		}
536
+
537
+		$showWarnings = self::getConfigData('show_warnings');
538
+		if ($showWarnings !== null) {
539
+			$showWarnings = (bool) $showWarnings;
540
+			if ($showWarnings === false) {
541
+				$this->warningSeverity = 0;
542
+			}
543
+		}
544
+
545
+		$reportWidth = self::getConfigData('report_width');
546
+		if ($reportWidth !== null) {
547
+			$this->reportWidth = $reportWidth;
548
+		}
549
+
550
+		$showProgress = self::getConfigData('show_progress');
551
+		if ($showProgress !== null) {
552
+			$this->showProgress = (bool) $showProgress;
553
+		}
554
+
555
+		$quiet = self::getConfigData('quiet');
556
+		if ($quiet !== null) {
557
+			$this->quiet = (bool) $quiet;
558
+		}
559
+
560
+		$colors = self::getConfigData('colors');
561
+		if ($colors !== null) {
562
+			$this->colors = (bool) $colors;
563
+		}
564
+
565
+		if (defined('PHP_CODESNIFFER_IN_TESTS') === false) {
566
+			$cache = self::getConfigData('cache');
567
+			if ($cache !== null) {
568
+				$this->cache = (bool) $cache;
569
+			}
570
+
571
+			$parallel = self::getConfigData('parallel');
572
+			if ($parallel !== null) {
573
+				$this->parallel = max((int) $parallel, 1);
574
+			}
575
+		}
576
+
577
+	}//end restoreDefaults()
578
+
579
+
580
+	/**
581
+	 * Processes a short (-e) command line argument.
582
+	 *
583
+	 * @param string $arg The command line argument.
584
+	 * @param int    $pos The position of the argument on the command line.
585
+	 *
586
+	 * @return void
587
+	 */
588
+	public function processShortArgument($arg, $pos)
589
+	{
590
+		switch ($arg) {
591
+		case 'h':
592
+		case '?':
593
+			ob_start();
594
+			$this->printUsage();
595
+			$output = ob_get_contents();
596
+			ob_end_clean();
597
+			throw new DeepExitException($output, 0);
598
+		case 'i' :
599
+			ob_start();
600
+			Util\Standards::printInstalledStandards();
601
+			$output = ob_get_contents();
602
+			ob_end_clean();
603
+			throw new DeepExitException($output, 0);
604
+		case 'v' :
605
+			if ($this->quiet === true) {
606
+				// Ignore when quiet mode is enabled.
607
+				break;
608
+			}
609
+
610
+			$this->verbosity++;
611
+			self::$overriddenDefaults['verbosity'] = true;
612
+			break;
613
+		case 'l' :
614
+			$this->local = true;
615
+			self::$overriddenDefaults['local'] = true;
616
+			break;
617
+		case 's' :
618
+			$this->showSources = true;
619
+			self::$overriddenDefaults['showSources'] = true;
620
+			break;
621
+		case 'a' :
622
+			$this->interactive = true;
623
+			self::$overriddenDefaults['interactive'] = true;
624
+			break;
625
+		case 'e':
626
+			$this->explain = true;
627
+			self::$overriddenDefaults['explain'] = true;
628
+			break;
629
+		case 'p' :
630
+			if ($this->quiet === true) {
631
+				// Ignore when quiet mode is enabled.
632
+				break;
633
+			}
634
+
635
+			$this->showProgress = true;
636
+			self::$overriddenDefaults['showProgress'] = true;
637
+			break;
638
+		case 'q' :
639
+			// Quiet mode disables a few other settings as well.
640
+			$this->quiet        = true;
641
+			$this->showProgress = false;
642
+			$this->verbosity    = 0;
643
+
644
+			self::$overriddenDefaults['quiet'] = true;
645
+			break;
646
+		case 'm' :
647
+			$this->recordErrors = false;
648
+			self::$overriddenDefaults['recordErrors'] = true;
649
+			break;
650
+		case 'd' :
651
+			$ini = explode('=', $this->cliArgs[($pos + 1)]);
652
+			$this->cliArgs[($pos + 1)] = '';
653
+			if (isset($ini[1]) === true) {
654
+				ini_set($ini[0], $ini[1]);
655
+			} else {
656
+				ini_set($ini[0], true);
657
+			}
658
+			break;
659
+		case 'n' :
660
+			if (isset(self::$overriddenDefaults['warningSeverity']) === false) {
661
+				$this->warningSeverity = 0;
662
+				self::$overriddenDefaults['warningSeverity'] = true;
663
+			}
664
+			break;
665
+		case 'w' :
666
+			if (isset(self::$overriddenDefaults['warningSeverity']) === false) {
667
+				$this->warningSeverity = $this->errorSeverity;
668
+				self::$overriddenDefaults['warningSeverity'] = true;
669
+			}
670
+			break;
671
+		default:
672
+			if ($this->dieOnUnknownArg === false) {
673
+				$unknown       = $this->unknown;
674
+				$unknown[]     = $arg;
675
+				$this->unknown = $unknown;
676
+			} else {
677
+				$this->processUnknownArgument('-'.$arg, $pos);
678
+			}
679
+		}//end switch
680
+
681
+	}//end processShortArgument()
682
+
683
+
684
+	/**
685
+	 * Processes a long (--example) command line argument.
686
+	 *
687
+	 * @param string $arg The command line argument.
688
+	 * @param int    $pos The position of the argument on the command line.
689
+	 *
690
+	 * @return void
691
+	 */
692
+	public function processLongArgument($arg, $pos)
693
+	{
694
+		switch ($arg) {
695
+		case 'help':
696
+			ob_start();
697
+			$this->printUsage();
698
+			$output = ob_get_contents();
699
+			ob_end_clean();
700
+			throw new DeepExitException($output, 0);
701
+		case 'version':
702
+			$output  = 'PHP_CodeSniffer version '.self::VERSION.' ('.self::STABILITY.') ';
703
+			$output .= 'by Squiz (http://www.squiz.net)'.PHP_EOL;
704
+			throw new DeepExitException($output, 0);
705
+		case 'colors':
706
+			if (isset(self::$overriddenDefaults['colors']) === true) {
707
+				break;
708
+			}
709
+
710
+			$this->colors = true;
711
+			self::$overriddenDefaults['colors'] = true;
712
+			break;
713
+		case 'no-colors':
714
+			if (isset(self::$overriddenDefaults['colors']) === true) {
715
+				break;
716
+			}
717
+
718
+			$this->colors = false;
719
+			self::$overriddenDefaults['colors'] = true;
720
+			break;
721
+		case 'cache':
722
+			if (isset(self::$overriddenDefaults['cache']) === true) {
723
+				break;
724
+			}
725
+
726
+			if (defined('PHP_CODESNIFFER_IN_TESTS') === false) {
727
+				$this->cache = true;
728
+				self::$overriddenDefaults['cache'] = true;
729
+			}
730
+			break;
731
+		case 'no-cache':
732
+			if (isset(self::$overriddenDefaults['cache']) === true) {
733
+				break;
734
+			}
735
+
736
+			$this->cache = false;
737
+			self::$overriddenDefaults['cache'] = true;
738
+			break;
739
+		case 'ignore-annotations':
740
+			if (isset(self::$overriddenDefaults['annotations']) === true) {
741
+				break;
742
+			}
743
+
744
+			$this->annotations = false;
745
+			self::$overriddenDefaults['annotations'] = true;
746
+			break;
747
+		case 'config-set':
748
+			if (isset($this->cliArgs[($pos + 1)]) === false
749
+				|| isset($this->cliArgs[($pos + 2)]) === false
750
+			) {
751
+				$error  = 'ERROR: Setting a config option requires a name and value'.PHP_EOL.PHP_EOL;
752
+				$error .= $this->printShortUsage(true);
753
+				throw new DeepExitException($error, 3);
754
+			}
755
+
756
+			$key     = $this->cliArgs[($pos + 1)];
757
+			$value   = $this->cliArgs[($pos + 2)];
758
+			$current = self::getConfigData($key);
759
+
760
+			try {
761
+				$this->setConfigData($key, $value);
762
+			} catch (\Exception $e) {
763
+				throw new DeepExitException($e->getMessage().PHP_EOL, 3);
764
+			}
765
+
766
+			$output = 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
767
+
768
+			if ($current === null) {
769
+				$output .= "Config value \"$key\" added successfully".PHP_EOL;
770
+			} else {
771
+				$output .= "Config value \"$key\" updated successfully; old value was \"$current\"".PHP_EOL;
772
+			}
773
+			throw new DeepExitException($output, 0);
774
+		case 'config-delete':
775
+			if (isset($this->cliArgs[($pos + 1)]) === false) {
776
+				$error  = 'ERROR: Deleting a config option requires the name of the option'.PHP_EOL.PHP_EOL;
777
+				$error .= $this->printShortUsage(true);
778
+				throw new DeepExitException($error, 3);
779
+			}
780
+
781
+			$output = 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
782
+
783
+			$key     = $this->cliArgs[($pos + 1)];
784
+			$current = self::getConfigData($key);
785
+			if ($current === null) {
786
+				$output .= "Config value \"$key\" has not been set".PHP_EOL;
787
+			} else {
788
+				try {
789
+					$this->setConfigData($key, null);
790
+				} catch (\Exception $e) {
791
+					throw new DeepExitException($e->getMessage().PHP_EOL, 3);
792
+				}
793
+
794
+				$output .= "Config value \"$key\" removed successfully; old value was \"$current\"".PHP_EOL;
795
+			}
796
+			throw new DeepExitException($output, 0);
797
+		case 'config-show':
798
+			ob_start();
799
+			$data = self::getAllConfigData();
800
+			echo 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
801
+			$this->printConfigData($data);
802
+			$output = ob_get_contents();
803
+			ob_end_clean();
804
+			throw new DeepExitException($output, 0);
805
+		case 'runtime-set':
806
+			if (isset($this->cliArgs[($pos + 1)]) === false
807
+				|| isset($this->cliArgs[($pos + 2)]) === false
808
+			) {
809
+				$error  = 'ERROR: Setting a runtime config option requires a name and value'.PHP_EOL.PHP_EOL;
810
+				$error .= $this->printShortUsage(true);
811
+				throw new DeepExitException($error, 3);
812
+			}
813
+
814
+			$key   = $this->cliArgs[($pos + 1)];
815
+			$value = $this->cliArgs[($pos + 2)];
816
+			$this->cliArgs[($pos + 1)] = '';
817
+			$this->cliArgs[($pos + 2)] = '';
818
+			self::setConfigData($key, $value, true);
819
+			if (isset(self::$overriddenDefaults['runtime-set']) === false) {
820
+				self::$overriddenDefaults['runtime-set'] = [];
821
+			}
822
+
823
+			self::$overriddenDefaults['runtime-set'][$key] = true;
824
+			break;
825
+		default:
826
+			if (substr($arg, 0, 7) === 'sniffs=') {
827
+				if (isset(self::$overriddenDefaults['sniffs']) === true) {
828
+					break;
829
+				}
830
+
831
+				$sniffs = explode(',', substr($arg, 7));
832
+				foreach ($sniffs as $sniff) {
833
+					if (substr_count($sniff, '.') !== 2) {
834
+						$error  = 'ERROR: The specified sniff code "'.$sniff.'" is invalid'.PHP_EOL.PHP_EOL;
835
+						$error .= $this->printShortUsage(true);
836
+						throw new DeepExitException($error, 3);
837
+					}
838
+				}
839
+
840
+				$this->sniffs = $sniffs;
841
+				self::$overriddenDefaults['sniffs'] = true;
842
+			} else if (substr($arg, 0, 8) === 'exclude=') {
843
+				if (isset(self::$overriddenDefaults['exclude']) === true) {
844
+					break;
845
+				}
846
+
847
+				$sniffs = explode(',', substr($arg, 8));
848
+				foreach ($sniffs as $sniff) {
849
+					if (substr_count($sniff, '.') !== 2) {
850
+						$error  = 'ERROR: The specified sniff code "'.$sniff.'" is invalid'.PHP_EOL.PHP_EOL;
851
+						$error .= $this->printShortUsage(true);
852
+						throw new DeepExitException($error, 3);
853
+					}
854
+				}
855
+
856
+				$this->exclude = $sniffs;
857
+				self::$overriddenDefaults['exclude'] = true;
858
+			} else if (defined('PHP_CODESNIFFER_IN_TESTS') === false
859
+				&& substr($arg, 0, 6) === 'cache='
860
+			) {
861
+				if ((isset(self::$overriddenDefaults['cache']) === true
862
+					&& $this->cache === false)
863
+					|| isset(self::$overriddenDefaults['cacheFile']) === true
864
+				) {
865
+					break;
866
+				}
867
+
868
+				// Turn caching on.
869
+				$this->cache = true;
870
+				self::$overriddenDefaults['cache'] = true;
871
+
872
+				$this->cacheFile = Util\Common::realpath(substr($arg, 6));
873
+
874
+				// It may not exist and return false instead.
875
+				if ($this->cacheFile === false) {
876
+					$this->cacheFile = substr($arg, 6);
877
+
878
+					$dir = dirname($this->cacheFile);
879
+					if (is_dir($dir) === false) {
880
+						$error  = 'ERROR: The specified cache file path "'.$this->cacheFile.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
881
+						$error .= $this->printShortUsage(true);
882
+						throw new DeepExitException($error, 3);
883
+					}
884
+
885
+					if ($dir === '.') {
886
+						// Passed cache file is a file in the current directory.
887
+						$this->cacheFile = getcwd().'/'.basename($this->cacheFile);
888
+					} else {
889
+						if ($dir{0} === '/') {
890
+							// An absolute path.
891
+							$dir = Util\Common::realpath($dir);
892
+						} else {
893
+							$dir = Util\Common::realpath(getcwd().'/'.$dir);
894
+						}
895
+
896
+						if ($dir !== false) {
897
+							// Cache file path is relative.
898
+							$this->cacheFile = $dir.'/'.basename($this->cacheFile);
899
+						}
900
+					}
901
+				}//end if
902
+
903
+				self::$overriddenDefaults['cacheFile'] = true;
904
+
905
+				if (is_dir($this->cacheFile) === true) {
906
+					$error  = 'ERROR: The specified cache file path "'.$this->cacheFile.'" is a directory'.PHP_EOL.PHP_EOL;
907
+					$error .= $this->printShortUsage(true);
908
+					throw new DeepExitException($error, 3);
909
+				}
910
+			} else if (substr($arg, 0, 10) === 'bootstrap=') {
911
+				$files     = explode(',', substr($arg, 10));
912
+				$bootstrap = [];
913
+				foreach ($files as $file) {
914
+					$path = Util\Common::realpath($file);
915
+					if ($path === false) {
916
+						$error  = 'ERROR: The specified bootstrap file "'.$file.'" does not exist'.PHP_EOL.PHP_EOL;
917
+						$error .= $this->printShortUsage(true);
918
+						throw new DeepExitException($error, 3);
919
+					}
920
+
921
+					$bootstrap[] = $path;
922
+				}
923
+
924
+				$this->bootstrap = array_merge($this->bootstrap, $bootstrap);
925
+				self::$overriddenDefaults['bootstrap'] = true;
926
+			} else if (substr($arg, 0, 10) === 'file-list=') {
927
+				$fileList = substr($arg, 10);
928
+				$path     = Util\Common::realpath($fileList);
929
+				if ($path === false) {
930
+					$error  = 'ERROR: The specified file list "'.$fileList.'" does not exist'.PHP_EOL.PHP_EOL;
931
+					$error .= $this->printShortUsage(true);
932
+					throw new DeepExitException($error, 3);
933
+				}
934
+
935
+				$files = file($path);
936
+				foreach ($files as $inputFile) {
937
+					$inputFile = trim($inputFile);
938
+
939
+					// Skip empty lines.
940
+					if ($inputFile === '') {
941
+						continue;
942
+					}
943
+
944
+					$this->processFilePath($inputFile);
945
+				}
946
+			} else if (substr($arg, 0, 11) === 'stdin-path=') {
947
+				if (isset(self::$overriddenDefaults['stdinPath']) === true) {
948
+					break;
949
+				}
950
+
951
+				$this->stdinPath = Util\Common::realpath(substr($arg, 11));
952
+
953
+				// It may not exist and return false instead, so use whatever they gave us.
954
+				if ($this->stdinPath === false) {
955
+					$this->stdinPath = trim(substr($arg, 11));
956
+				}
957
+
958
+				self::$overriddenDefaults['stdinPath'] = true;
959
+			} else if (PHP_CODESNIFFER_CBF === false && substr($arg, 0, 12) === 'report-file=') {
960
+				if (isset(self::$overriddenDefaults['reportFile']) === true) {
961
+					break;
962
+				}
963
+
964
+				$this->reportFile = Util\Common::realpath(substr($arg, 12));
965
+
966
+				// It may not exist and return false instead.
967
+				if ($this->reportFile === false) {
968
+					$this->reportFile = substr($arg, 12);
969
+
970
+					$dir = dirname($this->reportFile);
971
+					if (is_dir($dir) === false) {
972
+						$error  = 'ERROR: The specified report file path "'.$this->reportFile.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
973
+						$error .= $this->printShortUsage(true);
974
+						throw new DeepExitException($error, 3);
975
+					}
976
+
977
+					if ($dir === '.') {
978
+						// Passed report file is a file in the current directory.
979
+						$this->reportFile = getcwd().'/'.basename($this->reportFile);
980
+					} else {
981
+						if ($dir{0} === '/') {
982
+							// An absolute path.
983
+							$dir = Util\Common::realpath($dir);
984
+						} else {
985
+							$dir = Util\Common::realpath(getcwd().'/'.$dir);
986
+						}
987
+
988
+						if ($dir !== false) {
989
+							// Report file path is relative.
990
+							$this->reportFile = $dir.'/'.basename($this->reportFile);
991
+						}
992
+					}
993
+				}//end if
994
+
995
+				self::$overriddenDefaults['reportFile'] = true;
996
+
997
+				if (is_dir($this->reportFile) === true) {
998
+					$error  = 'ERROR: The specified report file path "'.$this->reportFile.'" is a directory'.PHP_EOL.PHP_EOL;
999
+					$error .= $this->printShortUsage(true);
1000
+					throw new DeepExitException($error, 3);
1001
+				}
1002
+			} else if (substr($arg, 0, 13) === 'report-width=') {
1003
+				if (isset(self::$overriddenDefaults['reportWidth']) === true) {
1004
+					break;
1005
+				}
1006
+
1007
+				$this->reportWidth = substr($arg, 13);
1008
+				self::$overriddenDefaults['reportWidth'] = true;
1009
+			} else if (substr($arg, 0, 9) === 'basepath=') {
1010
+				if (isset(self::$overriddenDefaults['basepath']) === true) {
1011
+					break;
1012
+				}
1013
+
1014
+				self::$overriddenDefaults['basepath'] = true;
1015
+
1016
+				if (substr($arg, 9) === '') {
1017
+					$this->basepath = null;
1018
+					break;
1019
+				}
1020
+
1021
+				$this->basepath = Util\Common::realpath(substr($arg, 9));
1022
+
1023
+				// It may not exist and return false instead.
1024
+				if ($this->basepath === false) {
1025
+					$this->basepath = substr($arg, 9);
1026
+				}
1027
+
1028
+				if (is_dir($this->basepath) === false) {
1029
+					$error  = 'ERROR: The specified basepath "'.$this->basepath.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
1030
+					$error .= $this->printShortUsage(true);
1031
+					throw new DeepExitException($error, 3);
1032
+				}
1033
+			} else if ((substr($arg, 0, 7) === 'report=' || substr($arg, 0, 7) === 'report-')) {
1034
+				$reports = [];
1035
+
1036
+				if ($arg[6] === '-') {
1037
+					// This is a report with file output.
1038
+					$split = strpos($arg, '=');
1039
+					if ($split === false) {
1040
+						$report = substr($arg, 7);
1041
+						$output = null;
1042
+					} else {
1043
+						$report = substr($arg, 7, ($split - 7));
1044
+						$output = substr($arg, ($split + 1));
1045
+						if ($output === false) {
1046
+							$output = null;
1047
+						} else {
1048
+							$dir = dirname($output);
1049
+							if (is_dir($dir) === false) {
1050
+								$error  = 'ERROR: The specified '.$report.' report file path "'.$output.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
1051
+								$error .= $this->printShortUsage(true);
1052
+								throw new DeepExitException($error, 3);
1053
+							}
1054
+
1055
+							if ($dir === '.') {
1056
+								// Passed report file is a filename in the current directory.
1057
+								$output = getcwd().'/'.basename($output);
1058
+							} else {
1059
+								if ($dir{0} === '/') {
1060
+									// An absolute path.
1061
+									$dir = Util\Common::realpath($dir);
1062
+								} else {
1063
+									$dir = Util\Common::realpath(getcwd().'/'.$dir);
1064
+								}
1065
+
1066
+								if ($dir !== false) {
1067
+									// Report file path is relative.
1068
+									$output = $dir.'/'.basename($output);
1069
+								}
1070
+							}
1071
+						}//end if
1072
+					}//end if
1073
+
1074
+					$reports[$report] = $output;
1075
+				} else {
1076
+					// This is a single report.
1077
+					if (isset(self::$overriddenDefaults['reports']) === true) {
1078
+						break;
1079
+					}
1080
+
1081
+					$reportNames = explode(',', substr($arg, 7));
1082
+					foreach ($reportNames as $report) {
1083
+						$reports[$report] = null;
1084
+					}
1085
+				}//end if
1086
+
1087
+				// Remove the default value so the CLI value overrides it.
1088
+				if (isset(self::$overriddenDefaults['reports']) === false) {
1089
+					$this->reports = $reports;
1090
+				} else {
1091
+					$this->reports = array_merge($this->reports, $reports);
1092
+				}
1093
+
1094
+				self::$overriddenDefaults['reports'] = true;
1095
+			} else if (substr($arg, 0, 7) === 'filter=') {
1096
+				if (isset(self::$overriddenDefaults['filter']) === true) {
1097
+					break;
1098
+				}
1099
+
1100
+				$this->filter = substr($arg, 7);
1101
+				self::$overriddenDefaults['filter'] = true;
1102
+			} else if (substr($arg, 0, 9) === 'standard=') {
1103
+				$standards = trim(substr($arg, 9));
1104
+				if ($standards !== '') {
1105
+					$this->standards = explode(',', $standards);
1106
+				}
1107
+
1108
+				self::$overriddenDefaults['standards'] = true;
1109
+			} else if (substr($arg, 0, 11) === 'extensions=') {
1110
+				if (isset(self::$overriddenDefaults['extensions']) === true) {
1111
+					break;
1112
+				}
1113
+
1114
+				$extensions    = explode(',', substr($arg, 11));
1115
+				$newExtensions = [];
1116
+				foreach ($extensions as $ext) {
1117
+					$slash = strpos($ext, '/');
1118
+					if ($slash !== false) {
1119
+						// They specified the tokenizer too.
1120
+						list($ext, $tokenizer) = explode('/', $ext);
1121
+						$newExtensions[$ext]   = strtoupper($tokenizer);
1122
+						continue;
1123
+					}
1124
+
1125
+					if (isset($this->extensions[$ext]) === true) {
1126
+						$newExtensions[$ext] = $this->extensions[$ext];
1127
+					} else {
1128
+						$newExtensions[$ext] = 'PHP';
1129
+					}
1130
+				}
1131
+
1132
+				$this->extensions = $newExtensions;
1133
+				self::$overriddenDefaults['extensions'] = true;
1134
+			} else if (substr($arg, 0, 7) === 'suffix=') {
1135
+				if (isset(self::$overriddenDefaults['suffix']) === true) {
1136
+					break;
1137
+				}
1138
+
1139
+				$this->suffix = substr($arg, 7);
1140
+				self::$overriddenDefaults['suffix'] = true;
1141
+			} else if (substr($arg, 0, 9) === 'parallel=') {
1142
+				if (isset(self::$overriddenDefaults['parallel']) === true) {
1143
+					break;
1144
+				}
1145
+
1146
+				$this->parallel = max((int) substr($arg, 9), 1);
1147
+				self::$overriddenDefaults['parallel'] = true;
1148
+			} else if (substr($arg, 0, 9) === 'severity=') {
1149
+				$this->errorSeverity   = (int) substr($arg, 9);
1150
+				$this->warningSeverity = $this->errorSeverity;
1151
+				if (isset(self::$overriddenDefaults['errorSeverity']) === false) {
1152
+					self::$overriddenDefaults['errorSeverity'] = true;
1153
+				}
1154
+
1155
+				if (isset(self::$overriddenDefaults['warningSeverity']) === false) {
1156
+					self::$overriddenDefaults['warningSeverity'] = true;
1157
+				}
1158
+			} else if (substr($arg, 0, 15) === 'error-severity=') {
1159
+				if (isset(self::$overriddenDefaults['errorSeverity']) === true) {
1160
+					break;
1161
+				}
1162
+
1163
+				$this->errorSeverity = (int) substr($arg, 15);
1164
+				self::$overriddenDefaults['errorSeverity'] = true;
1165
+			} else if (substr($arg, 0, 17) === 'warning-severity=') {
1166
+				if (isset(self::$overriddenDefaults['warningSeverity']) === true) {
1167
+					break;
1168
+				}
1169
+
1170
+				$this->warningSeverity = (int) substr($arg, 17);
1171
+				self::$overriddenDefaults['warningSeverity'] = true;
1172
+			} else if (substr($arg, 0, 7) === 'ignore=') {
1173
+				if (isset(self::$overriddenDefaults['ignored']) === true) {
1174
+					break;
1175
+				}
1176
+
1177
+				// Split the ignore string on commas, unless the comma is escaped
1178
+				// using 1 or 3 slashes (\, or \\\,).
1179
+				$patterns = preg_split(
1180
+					'/(?<=(?<!\\\\)\\\\\\\\),|(?<!\\\\),/',
1181
+					substr($arg, 7)
1182
+				);
1183
+
1184
+				$ignored = [];
1185
+				foreach ($patterns as $pattern) {
1186
+					$pattern = trim($pattern);
1187
+					if ($pattern === '') {
1188
+						continue;
1189
+					}
1190
+
1191
+					$ignored[$pattern] = 'absolute';
1192
+				}
1193
+
1194
+				$this->ignored = $ignored;
1195
+				self::$overriddenDefaults['ignored'] = true;
1196
+			} else if (substr($arg, 0, 10) === 'generator='
1197
+				&& PHP_CODESNIFFER_CBF === false
1198
+			) {
1199
+				if (isset(self::$overriddenDefaults['generator']) === true) {
1200
+					break;
1201
+				}
1202
+
1203
+				$this->generator = substr($arg, 10);
1204
+				self::$overriddenDefaults['generator'] = true;
1205
+			} else if (substr($arg, 0, 9) === 'encoding=') {
1206
+				if (isset(self::$overriddenDefaults['encoding']) === true) {
1207
+					break;
1208
+				}
1209
+
1210
+				$this->encoding = strtolower(substr($arg, 9));
1211
+				self::$overriddenDefaults['encoding'] = true;
1212
+			} else if (substr($arg, 0, 10) === 'tab-width=') {
1213
+				if (isset(self::$overriddenDefaults['tabWidth']) === true) {
1214
+					break;
1215
+				}
1216
+
1217
+				$this->tabWidth = (int) substr($arg, 10);
1218
+				self::$overriddenDefaults['tabWidth'] = true;
1219
+			} else {
1220
+				if ($this->dieOnUnknownArg === false) {
1221
+					$eqPos = strpos($arg, '=');
1222
+					try {
1223
+						if ($eqPos === false) {
1224
+							$this->values[$arg] = $arg;
1225
+						} else {
1226
+							$value = substr($arg, ($eqPos + 1));
1227
+							$arg   = substr($arg, 0, $eqPos);
1228
+							$this->values[$arg] = $value;
1229
+						}
1230
+					} catch (RuntimeException $e) {
1231
+						// Value is not valid, so just ignore it.
1232
+					}
1233
+				} else {
1234
+					$this->processUnknownArgument('--'.$arg, $pos);
1235
+				}
1236
+			}//end if
1237
+			break;
1238
+		}//end switch
1239
+
1240
+	}//end processLongArgument()
1241
+
1242
+
1243
+	/**
1244
+	 * Processes an unknown command line argument.
1245
+	 *
1246
+	 * Assumes all unknown arguments are files and folders to check.
1247
+	 *
1248
+	 * @param string $arg The command line argument.
1249
+	 * @param int    $pos The position of the argument on the command line.
1250
+	 *
1251
+	 * @return void
1252
+	 */
1253
+	public function processUnknownArgument($arg, $pos)
1254
+	{
1255
+		// We don't know about any additional switches; just files.
1256
+		if ($arg{0} === '-') {
1257
+			if ($this->dieOnUnknownArg === false) {
1258
+				return;
1259
+			}
1260
+
1261
+			$error  = "ERROR: option \"$arg\" not known".PHP_EOL.PHP_EOL;
1262
+			$error .= $this->printShortUsage(true);
1263
+			throw new DeepExitException($error, 3);
1264
+		}
1265
+
1266
+		$this->processFilePath($arg);
1267
+
1268
+	}//end processUnknownArgument()
1269
+
1270
+
1271
+	/**
1272
+	 * Processes a file path and add it to the file list.
1273
+	 *
1274
+	 * @param string $path The path to the file to add.
1275
+	 *
1276
+	 * @return void
1277
+	 */
1278
+	public function processFilePath($path)
1279
+	{
1280
+		// If we are processing STDIN, don't record any files to check.
1281
+		if ($this->stdin === true) {
1282
+			return;
1283
+		}
1284
+
1285
+		$file = Util\Common::realpath($path);
1286
+		if (file_exists($file) === false) {
1287
+			if ($this->dieOnUnknownArg === false) {
1288
+				return;
1289
+			}
1290
+
1291
+			$error  = 'ERROR: The file "'.$path.'" does not exist.'.PHP_EOL.PHP_EOL;
1292
+			$error .= $this->printShortUsage(true);
1293
+			throw new DeepExitException($error, 3);
1294
+		} else {
1295
+			// Can't modify the files array directly because it's not a real
1296
+			// class member, so need to use this little get/modify/set trick.
1297
+			$files       = $this->files;
1298
+			$files[]     = $file;
1299
+			$this->files = $files;
1300
+			self::$overriddenDefaults['files'] = true;
1301
+		}
1302
+
1303
+	}//end processFilePath()
1304
+
1305
+
1306
+	/**
1307
+	 * Prints out the usage information for this script.
1308
+	 *
1309
+	 * @return void
1310
+	 */
1311
+	public function printUsage()
1312
+	{
1313
+		echo PHP_EOL;
1314
+
1315
+		if (PHP_CODESNIFFER_CBF === true) {
1316
+			$this->printPHPCBFUsage();
1317
+		} else {
1318
+			$this->printPHPCSUsage();
1319
+		}
1320
+
1321
+		echo PHP_EOL;
1322
+
1323
+	}//end printUsage()
1324
+
1325
+
1326
+	/**
1327
+	 * Prints out the short usage information for this script.
1328
+	 *
1329
+	 * @param bool $return If TRUE, the usage string is returned
1330
+	 *                     instead of output to screen.
1331
+	 *
1332
+	 * @return string|void
1333
+	 */
1334
+	public function printShortUsage($return=false)
1335
+	{
1336
+		if (PHP_CODESNIFFER_CBF === true) {
1337
+			$usage = 'Run "phpcbf --help" for usage information';
1338
+		} else {
1339
+			$usage = 'Run "phpcs --help" for usage information';
1340
+		}
1341
+
1342
+		$usage .= PHP_EOL.PHP_EOL;
1343
+
1344
+		if ($return === true) {
1345
+			return $usage;
1346
+		}
1347
+
1348
+		echo $usage;
1349
+
1350
+	}//end printShortUsage()
1351
+
1352
+
1353
+	/**
1354
+	 * Prints out the usage information for PHPCS.
1355
+	 *
1356
+	 * @return void
1357
+	 */
1358
+	public function printPHPCSUsage()
1359
+	{
1360
+		echo 'Usage: phpcs [-nwlsaepqvi] [-d key[=value]] [--colors] [--no-colors]'.PHP_EOL;
1361
+		echo '  [--cache[=<cacheFile>]] [--no-cache] [--tab-width=<tabWidth>]'.PHP_EOL;
1362
+		echo '  [--report=<report>] [--report-file=<reportFile>] [--report-<report>=<reportFile>]'.PHP_EOL;
1363
+		echo '  [--report-width=<reportWidth>] [--basepath=<basepath>] [--bootstrap=<bootstrap>]'.PHP_EOL;
1364
+		echo '  [--severity=<severity>] [--error-severity=<severity>] [--warning-severity=<severity>]'.PHP_EOL;
1365
+		echo '  [--runtime-set key value] [--config-set key value] [--config-delete key] [--config-show]'.PHP_EOL;
1366
+		echo '  [--standard=<standard>] [--sniffs=<sniffs>] [--exclude=<sniffs>]'.PHP_EOL;
1367
+		echo '  [--encoding=<encoding>] [--parallel=<processes>] [--generator=<generator>]'.PHP_EOL;
1368
+		echo '  [--extensions=<extensions>] [--ignore=<patterns>] [--ignore-annotations]'.PHP_EOL;
1369
+		echo '  [--stdin-path=<stdinPath>] [--file-list=<fileList>] [--filter=<filter>] <file> - ...'.PHP_EOL;
1370
+		echo PHP_EOL;
1371
+		echo ' -     Check STDIN instead of local files and directories'.PHP_EOL;
1372
+		echo ' -n    Do not print warnings (shortcut for --warning-severity=0)'.PHP_EOL;
1373
+		echo ' -w    Print both warnings and errors (this is the default)'.PHP_EOL;
1374
+		echo ' -l    Local directory only, no recursion'.PHP_EOL;
1375
+		echo ' -s    Show sniff codes in all reports'.PHP_EOL;
1376
+		echo ' -a    Run interactively'.PHP_EOL;
1377
+		echo ' -e    Explain a standard by showing the sniffs it includes'.PHP_EOL;
1378
+		echo ' -p    Show progress of the run'.PHP_EOL;
1379
+		echo ' -q    Quiet mode; disables progress and verbose output'.PHP_EOL;
1380
+		echo ' -m    Stop error messages from being recorded'.PHP_EOL;
1381
+		echo '       (saves a lot of memory, but stops many reports from being used)'.PHP_EOL;
1382
+		echo ' -v    Print processed files'.PHP_EOL;
1383
+		echo ' -vv   Print ruleset and token output'.PHP_EOL;
1384
+		echo ' -vvv  Print sniff processing information'.PHP_EOL;
1385
+		echo ' -i    Show a list of installed coding standards'.PHP_EOL;
1386
+		echo ' -d    Set the [key] php.ini value to [value] or [true] if value is omitted'.PHP_EOL;
1387
+		echo PHP_EOL;
1388
+		echo ' --help                Print this help message'.PHP_EOL;
1389
+		echo ' --version             Print version information'.PHP_EOL;
1390
+		echo ' --colors              Use colors in output'.PHP_EOL;
1391
+		echo ' --no-colors           Do not use colors in output (this is the default)'.PHP_EOL;
1392
+		echo ' --cache               Cache results between runs'.PHP_EOL;
1393
+		echo ' --no-cache            Do not cache results between runs (this is the default)'.PHP_EOL;
1394
+		echo ' --ignore-annotations  Ignore all phpcs: annotations in code comments'.PHP_EOL;
1395
+		echo PHP_EOL;
1396
+		echo ' <cacheFile>    Use a specific file for caching (uses a temporary file by default)'.PHP_EOL;
1397
+		echo ' <basepath>     A path to strip from the front of file paths inside reports'.PHP_EOL;
1398
+		echo ' <bootstrap>    A comma separated list of files to run before processing begins'.PHP_EOL;
1399
+		echo ' <encoding>     The encoding of the files being checked (default is utf-8)'.PHP_EOL;
1400
+		echo ' <extensions>   A comma separated list of file extensions to check'.PHP_EOL;
1401
+		echo '                The type of the file can be specified using: ext/type'.PHP_EOL;
1402
+		echo '                e.g., module/php,es/js'.PHP_EOL;
1403
+		echo ' <file>         One or more files and/or directories to check'.PHP_EOL;
1404
+		echo ' <fileList>     A file containing a list of files and/or directories to check (one per line)'.PHP_EOL;
1405
+		echo ' <filter>       Use the "gitmodified" filter, or specify the path to a custom filter class'.PHP_EOL;
1406
+		echo ' <generator>    Uses either the "HTML", "Markdown" or "Text" generator'.PHP_EOL;
1407
+		echo '                (forces documentation generation instead of checking)'.PHP_EOL;
1408
+		echo ' <patterns>     A comma separated list of patterns to ignore files and directories'.PHP_EOL;
1409
+		echo ' <processes>    How many files should be checked simultaneously (default is 1)'.PHP_EOL;
1410
+		echo ' <report>       Print either the "full", "xml", "checkstyle", "csv"'.PHP_EOL;
1411
+		echo '                "json", "junit", "emacs", "source", "summary", "diff"'.PHP_EOL;
1412
+		echo '                "svnblame", "gitblame", "hgblame" or "notifysend" report,'.PHP_EOL;
1413
+		echo '                or specify the path to a custom report class'.PHP_EOL;
1414
+		echo '                (the "full" report is printed by default)'.PHP_EOL;
1415
+		echo ' <reportFile>   Write the report to the specified file path'.PHP_EOL;
1416
+		echo ' <reportWidth>  How many columns wide screen reports should be printed'.PHP_EOL;
1417
+		echo '                or set to "auto" to use current screen width, where supported'.PHP_EOL;
1418
+		echo ' <severity>     The minimum severity required to display an error or warning'.PHP_EOL;
1419
+		echo ' <sniffs>       A comma separated list of sniff codes to include or exclude from checking'.PHP_EOL;
1420
+		echo '                (all sniffs must be part of the specified standard)'.PHP_EOL;
1421
+		echo ' <standard>     The name or path of the coding standard to use'.PHP_EOL;
1422
+		echo ' <stdinPath>    If processing STDIN, the file path that STDIN will be processed as'.PHP_EOL;
1423
+		echo ' <tabWidth>     The number of spaces each tab represents'.PHP_EOL;
1424
+
1425
+	}//end printPHPCSUsage()
1426
+
1427
+
1428
+	/**
1429
+	 * Prints out the usage information for PHPCBF.
1430
+	 *
1431
+	 * @return void
1432
+	 */
1433
+	public function printPHPCBFUsage()
1434
+	{
1435
+		echo 'Usage: phpcbf [-nwli] [-d key[=value]] [--ignore-annotations] [--bootstrap=<bootstrap>]'.PHP_EOL;
1436
+		echo '  [--standard=<standard>] [--sniffs=<sniffs>] [--exclude=<sniffs>] [--suffix=<suffix>]'.PHP_EOL;
1437
+		echo '  [--severity=<severity>] [--error-severity=<severity>] [--warning-severity=<severity>]'.PHP_EOL;
1438
+		echo '  [--tab-width=<tabWidth>] [--encoding=<encoding>] [--parallel=<processes>]'.PHP_EOL;
1439
+		echo '  [--basepath=<basepath>] [--extensions=<extensions>] [--ignore=<patterns>]'.PHP_EOL;
1440
+		echo '  [--stdin-path=<stdinPath>] [--file-list=<fileList>] [--filter=<filter>] <file> - ...'.PHP_EOL;
1441
+		echo PHP_EOL;
1442
+		echo ' -     Fix STDIN instead of local files and directories'.PHP_EOL;
1443
+		echo ' -n    Do not fix warnings (shortcut for --warning-severity=0)'.PHP_EOL;
1444
+		echo ' -w    Fix both warnings and errors (on by default)'.PHP_EOL;
1445
+		echo ' -l    Local directory only, no recursion'.PHP_EOL;
1446
+		echo ' -p    Show progress of the run'.PHP_EOL;
1447
+		echo ' -q    Quiet mode; disables progress and verbose output'.PHP_EOL;
1448
+		echo ' -v    Print processed files'.PHP_EOL;
1449
+		echo ' -vv   Print ruleset and token output'.PHP_EOL;
1450
+		echo ' -vvv  Print sniff processing information'.PHP_EOL;
1451
+		echo ' -i    Show a list of installed coding standards'.PHP_EOL;
1452
+		echo ' -d    Set the [key] php.ini value to [value] or [true] if value is omitted'.PHP_EOL;
1453
+		echo PHP_EOL;
1454
+		echo ' --help                Print this help message'.PHP_EOL;
1455
+		echo ' --version             Print version information'.PHP_EOL;
1456
+		echo ' --ignore-annotations  Ignore all phpcs: annotations in code comments'.PHP_EOL;
1457
+		echo PHP_EOL;
1458
+		echo ' <basepath>    A path to strip from the front of file paths inside reports'.PHP_EOL;
1459
+		echo ' <bootstrap>   A comma separated list of files to run before processing begins'.PHP_EOL;
1460
+		echo ' <encoding>    The encoding of the files being fixed (default is utf-8)'.PHP_EOL;
1461
+		echo ' <extensions>  A comma separated list of file extensions to fix'.PHP_EOL;
1462
+		echo '               The type of the file can be specified using: ext/type'.PHP_EOL;
1463
+		echo '               e.g., module/php,es/js'.PHP_EOL;
1464
+		echo ' <file>        One or more files and/or directories to fix'.PHP_EOL;
1465
+		echo ' <fileList>    A file containing a list of files and/or directories to fix (one per line)'.PHP_EOL;
1466
+		echo ' <filter>      Use the "gitmodified" filter, or specify the path to a custom filter class'.PHP_EOL;
1467
+		echo ' <patterns>    A comma separated list of patterns to ignore files and directories'.PHP_EOL;
1468
+		echo ' <processes>   How many files should be fixed simultaneously (default is 1)'.PHP_EOL;
1469
+		echo ' <severity>    The minimum severity required to fix an error or warning'.PHP_EOL;
1470
+		echo ' <sniffs>      A comma separated list of sniff codes to include or exclude from fixing'.PHP_EOL;
1471
+		echo '               (all sniffs must be part of the specified standard)'.PHP_EOL;
1472
+		echo ' <standard>    The name or path of the coding standard to use'.PHP_EOL;
1473
+		echo ' <stdinPath>   If processing STDIN, the file path that STDIN will be processed as'.PHP_EOL;
1474
+		echo ' <suffix>      Write modified files to a filename using this suffix'.PHP_EOL;
1475
+		echo '               ("diff" and "patch" are not used in this mode)'.PHP_EOL;
1476
+		echo ' <tabWidth>    The number of spaces each tab represents'.PHP_EOL;
1477
+
1478
+	}//end printPHPCBFUsage()
1479
+
1480
+
1481
+	/**
1482
+	 * Get a single config value.
1483
+	 *
1484
+	 * @param string $key The name of the config value.
1485
+	 *
1486
+	 * @return string|null
1487
+	 * @see    setConfigData()
1488
+	 * @see    getAllConfigData()
1489
+	 */
1490
+	public static function getConfigData($key)
1491
+	{
1492
+		$phpCodeSnifferConfig = self::getAllConfigData();
1493
+
1494
+		if ($phpCodeSnifferConfig === null) {
1495
+			return null;
1496
+		}
1497
+
1498
+		if (isset($phpCodeSnifferConfig[$key]) === false) {
1499
+			return null;
1500
+		}
1501
+
1502
+		return $phpCodeSnifferConfig[$key];
1503
+
1504
+	}//end getConfigData()
1505
+
1506
+
1507
+	/**
1508
+	 * Get the path to an executable utility.
1509
+	 *
1510
+	 * @param string $name The name of the executable utility.
1511
+	 *
1512
+	 * @return string|null
1513
+	 * @see    getConfigData()
1514
+	 */
1515
+	public static function getExecutablePath($name)
1516
+	{
1517
+		$data = self::getConfigData($name.'_path');
1518
+		if ($data !== null) {
1519
+			return $data;
1520
+		}
1521
+
1522
+		if ($name === "php") {
1523
+			// For php, we know the executable path. There's no need to look it up.
1524
+			return PHP_BINARY;
1525
+		}
1526
+
1527
+		if (array_key_exists($name, self::$executablePaths) === true) {
1528
+			return self::$executablePaths[$name];
1529
+		}
1530
+
1531
+		if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
1532
+			$cmd = 'where '.escapeshellarg($name).' 2> nul';
1533
+		} else {
1534
+			$cmd = 'which '.escapeshellarg($name).' 2> /dev/null';
1535
+		}
1536
+
1537
+		$result = exec($cmd, $output, $retVal);
1538
+		if ($retVal !== 0) {
1539
+			$result = null;
1540
+		}
1541
+
1542
+		self::$executablePaths[$name] = $result;
1543
+		return $result;
1544
+
1545
+	}//end getExecutablePath()
1546
+
1547
+
1548
+	/**
1549
+	 * Set a single config value.
1550
+	 *
1551
+	 * @param string      $key   The name of the config value.
1552
+	 * @param string|null $value The value to set. If null, the config
1553
+	 *                           entry is deleted, reverting it to the
1554
+	 *                           default value.
1555
+	 * @param boolean     $temp  Set this config data temporarily for this
1556
+	 *                           script run. This will not write the config
1557
+	 *                           data to the config file.
1558
+	 *
1559
+	 * @return bool
1560
+	 * @see    getConfigData()
1561
+	 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the config file can not be written.
1562
+	 */
1563
+	public static function setConfigData($key, $value, $temp=false)
1564
+	{
1565
+		if (isset(self::$overriddenDefaults['runtime-set']) === true
1566
+			&& isset(self::$overriddenDefaults['runtime-set'][$key]) === true
1567
+		) {
1568
+			return false;
1569
+		}
1570
+
1571
+		if ($temp === false) {
1572
+			$path = '';
1573
+			if (is_callable('\Phar::running') === true) {
1574
+				$path = \Phar::running(false);
1575
+			}
1576
+
1577
+			if ($path !== '') {
1578
+				$configFile = dirname($path).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
1579
+			} else {
1580
+				$configFile = dirname(__DIR__).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
1581
+				if (is_file($configFile) === false
1582
+					&& strpos('@data_dir@', '@data_dir') === false
1583
+				) {
1584
+					// If data_dir was replaced, this is a PEAR install and we can
1585
+					// use the PEAR data dir to store the conf file.
1586
+					$configFile = '@data_dir@/PHP_CodeSniffer/CodeSniffer.conf';
1587
+				}
1588
+			}
1589
+
1590
+			if (is_file($configFile) === true
1591
+				&& is_writable($configFile) === false
1592
+			) {
1593
+				$error = 'ERROR: Config file '.$configFile.' is not writable'.PHP_EOL.PHP_EOL;
1594
+				throw new DeepExitException($error, 3);
1595
+			}
1596
+		}//end if
1597
+
1598
+		$phpCodeSnifferConfig = self::getAllConfigData();
1599
+
1600
+		if ($value === null) {
1601
+			if (isset($phpCodeSnifferConfig[$key]) === true) {
1602
+				unset($phpCodeSnifferConfig[$key]);
1603
+			}
1604
+		} else {
1605
+			$phpCodeSnifferConfig[$key] = $value;
1606
+		}
1607
+
1608
+		if ($temp === false) {
1609
+			$output  = '<'.'?php'."\n".' $phpCodeSnifferConfig = ';
1610
+			$output .= var_export($phpCodeSnifferConfig, true);
1611
+			$output .= "\n?".'>';
1612
+
1613
+			if (file_put_contents($configFile, $output) === false) {
1614
+				$error = 'ERROR: Config file '.$configFile.' could not be written'.PHP_EOL.PHP_EOL;
1615
+				throw new DeepExitException($error, 3);
1616
+			}
1617
+
1618
+			self::$configDataFile = $configFile;
1619
+		}
1620
+
1621
+		self::$configData = $phpCodeSnifferConfig;
1622
+
1623
+		// If the installed paths are being set, make sure all known
1624
+		// standards paths are added to the autoloader.
1625
+		if ($key === 'installed_paths') {
1626
+			$installedStandards = Util\Standards::getInstalledStandardDetails();
1627
+			foreach ($installedStandards as $name => $details) {
1628
+				Autoload::addSearchPath($details['path'], $details['namespace']);
1629
+			}
1630
+		}
1631
+
1632
+		return true;
1633
+
1634
+	}//end setConfigData()
1635
+
1636
+
1637
+	/**
1638
+	 * Get all config data.
1639
+	 *
1640
+	 * @return array<string, string>
1641
+	 * @see    getConfigData()
1642
+	 */
1643
+	public static function getAllConfigData()
1644
+	{
1645
+		if (self::$configData !== null) {
1646
+			return self::$configData;
1647
+		}
1648
+
1649
+		$path = '';
1650
+		if (is_callable('\Phar::running') === true) {
1651
+			$path = \Phar::running(false);
1652
+		}
1653
+
1654
+		if ($path !== '') {
1655
+			$configFile = dirname($path).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
1656
+		} else {
1657
+			$configFile = dirname(__DIR__).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
1658
+			if (is_file($configFile) === false
1659
+				&& strpos('@data_dir@', '@data_dir') === false
1660
+			) {
1661
+				$configFile = '@data_dir@/PHP_CodeSniffer/CodeSniffer.conf';
1662
+			}
1663
+		}
1664
+
1665
+		if (is_file($configFile) === false) {
1666
+			self::$configData = [];
1667
+			return [];
1668
+		}
1669
+
1670
+		if (is_readable($configFile) === false) {
1671
+			$error = 'ERROR: Config file '.$configFile.' is not readable'.PHP_EOL.PHP_EOL;
1672
+			throw new DeepExitException($error, 3);
1673
+		}
1674
+
1675
+		include $configFile;
1676
+		self::$configDataFile = $configFile;
1677
+		self::$configData     = $phpCodeSnifferConfig;
1678
+		return self::$configData;
1679
+
1680
+	}//end getAllConfigData()
1681
+
1682
+
1683
+	/**
1684
+	 * Prints out the gathered config data.
1685
+	 *
1686
+	 * @param array $data The config data to print.
1687
+	 *
1688
+	 * @return void
1689
+	 */
1690
+	public function printConfigData($data)
1691
+	{
1692
+		$max  = 0;
1693
+		$keys = array_keys($data);
1694
+		foreach ($keys as $key) {
1695
+			$len = strlen($key);
1696
+			if (strlen($key) > $max) {
1697
+				$max = $len;
1698
+			}
1699
+		}
1700
+
1701
+		if ($max === 0) {
1702
+			return;
1703
+		}
1704
+
1705
+		$max += 2;
1706
+		ksort($data);
1707
+		foreach ($data as $name => $value) {
1708
+			echo str_pad($name.': ', $max).$value.PHP_EOL;
1709
+		}
1710
+
1711
+	}//end printConfigData()
1712 1712
 
1713 1713
 
1714 1714
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Sniffs/Sniff.php 1 patch
Indentation   +53 added lines, -53 removed lines patch added patch discarded remove patch
@@ -20,61 +20,61 @@
 block discarded – undo
20 20
 {
21 21
 
22 22
 
23
-    /**
24
-     * Registers the tokens that this sniff wants to listen for.
25
-     *
26
-     * An example return value for a sniff that wants to listen for whitespace
27
-     * and any comments would be:
28
-     *
29
-     * <code>
30
-     *    return array(
31
-     *            T_WHITESPACE,
32
-     *            T_DOC_COMMENT,
33
-     *            T_COMMENT,
34
-     *           );
35
-     * </code>
36
-     *
37
-     * @return mixed[]
38
-     * @see    Tokens.php
39
-     */
40
-    public function register();
23
+	/**
24
+	 * Registers the tokens that this sniff wants to listen for.
25
+	 *
26
+	 * An example return value for a sniff that wants to listen for whitespace
27
+	 * and any comments would be:
28
+	 *
29
+	 * <code>
30
+	 *    return array(
31
+	 *            T_WHITESPACE,
32
+	 *            T_DOC_COMMENT,
33
+	 *            T_COMMENT,
34
+	 *           );
35
+	 * </code>
36
+	 *
37
+	 * @return mixed[]
38
+	 * @see    Tokens.php
39
+	 */
40
+	public function register();
41 41
 
42 42
 
43
-    /**
44
-     * Called when one of the token types that this sniff is listening for
45
-     * is found.
46
-     *
47
-     * The stackPtr variable indicates where in the stack the token was found.
48
-     * A sniff can acquire information this token, along with all the other
49
-     * tokens within the stack by first acquiring the token stack:
50
-     *
51
-     * <code>
52
-     *    $tokens = $phpcsFile->getTokens();
53
-     *    echo 'Encountered a '.$tokens[$stackPtr]['type'].' token';
54
-     *    echo 'token information: ';
55
-     *    print_r($tokens[$stackPtr]);
56
-     * </code>
57
-     *
58
-     * If the sniff discovers an anomaly in the code, they can raise an error
59
-     * by calling addError() on the \PHP_CodeSniffer\Files\File object, specifying an error
60
-     * message and the position of the offending token:
61
-     *
62
-     * <code>
63
-     *    $phpcsFile->addError('Encountered an error', $stackPtr);
64
-     * </code>
65
-     *
66
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
67
-     *                                               token was found.
68
-     * @param int                         $stackPtr  The position in the PHP_CodeSniffer
69
-     *                                               file's token stack where the token
70
-     *                                               was found.
71
-     *
72
-     * @return void|int Optionally returns a stack pointer. The sniff will not be
73
-     *                  called again on the current file until the returned stack
74
-     *                  pointer is reached. Return (count($tokens) + 1) to skip
75
-     *                  the rest of the file.
76
-     */
77
-    public function process(File $phpcsFile, $stackPtr);
43
+	/**
44
+	 * Called when one of the token types that this sniff is listening for
45
+	 * is found.
46
+	 *
47
+	 * The stackPtr variable indicates where in the stack the token was found.
48
+	 * A sniff can acquire information this token, along with all the other
49
+	 * tokens within the stack by first acquiring the token stack:
50
+	 *
51
+	 * <code>
52
+	 *    $tokens = $phpcsFile->getTokens();
53
+	 *    echo 'Encountered a '.$tokens[$stackPtr]['type'].' token';
54
+	 *    echo 'token information: ';
55
+	 *    print_r($tokens[$stackPtr]);
56
+	 * </code>
57
+	 *
58
+	 * If the sniff discovers an anomaly in the code, they can raise an error
59
+	 * by calling addError() on the \PHP_CodeSniffer\Files\File object, specifying an error
60
+	 * message and the position of the offending token:
61
+	 *
62
+	 * <code>
63
+	 *    $phpcsFile->addError('Encountered an error', $stackPtr);
64
+	 * </code>
65
+	 *
66
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
67
+	 *                                               token was found.
68
+	 * @param int                         $stackPtr  The position in the PHP_CodeSniffer
69
+	 *                                               file's token stack where the token
70
+	 *                                               was found.
71
+	 *
72
+	 * @return void|int Optionally returns a stack pointer. The sniff will not be
73
+	 *                  called again on the current file until the returned stack
74
+	 *                  pointer is reached. Return (count($tokens) + 1) to skip
75
+	 *                  the rest of the file.
76
+	 */
77
+	public function process(File $phpcsFile, $stackPtr);
78 78
 
79 79
 
80 80
 }//end interface
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Sniffs/AbstractScopeSniff.php 1 patch
Indentation   +154 added lines, -154 removed lines patch added patch discarded remove patch
@@ -32,160 +32,160 @@
 block discarded – undo
32 32
 abstract class AbstractScopeSniff implements Sniff
33 33
 {
34 34
 
35
-    /**
36
-     * The token types that this test wishes to listen to within the scope.
37
-     *
38
-     * @var array
39
-     */
40
-    private $tokens = [];
41
-
42
-    /**
43
-     * The type of scope opener tokens that this test wishes to listen to.
44
-     *
45
-     * @var string
46
-     */
47
-    private $scopeTokens = [];
48
-
49
-    /**
50
-     * True if this test should fire on tokens outside of the scope.
51
-     *
52
-     * @var boolean
53
-     */
54
-    private $listenOutside = false;
55
-
56
-
57
-    /**
58
-     * Constructs a new AbstractScopeTest.
59
-     *
60
-     * @param array   $scopeTokens   The type of scope the test wishes to listen to.
61
-     * @param array   $tokens        The tokens that the test wishes to listen to
62
-     *                               within the scope.
63
-     * @param boolean $listenOutside If true this test will also alert the
64
-     *                               extending class when a token is found outside
65
-     *                               the scope, by calling the
66
-     *                               processTokenOutsideScope method.
67
-     *
68
-     * @see    PHP_CodeSniffer.getValidScopeTokeners()
69
-     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified tokens array is empty.
70
-     */
71
-    public function __construct(
72
-        array $scopeTokens,
73
-        array $tokens,
74
-        $listenOutside=false
75
-    ) {
76
-        if (empty($scopeTokens) === true) {
77
-            $error = 'The scope tokens list cannot be empty';
78
-            throw new RuntimeException($error);
79
-        }
80
-
81
-        if (empty($tokens) === true) {
82
-            $error = 'The tokens list cannot be empty';
83
-            throw new RuntimeException($error);
84
-        }
85
-
86
-        $invalidScopeTokens = array_intersect($scopeTokens, $tokens);
87
-        if (empty($invalidScopeTokens) === false) {
88
-            $invalid = implode(', ', $invalidScopeTokens);
89
-            $error   = "Scope tokens [$invalid] can't be in the tokens array";
90
-            throw new RuntimeException($error);
91
-        }
92
-
93
-        $this->listenOutside = $listenOutside;
94
-        $this->scopeTokens   = array_flip($scopeTokens);
95
-        $this->tokens        = $tokens;
96
-
97
-    }//end __construct()
98
-
99
-
100
-    /**
101
-     * The method that is called to register the tokens this test wishes to
102
-     * listen to.
103
-     *
104
-     * DO NOT OVERRIDE THIS METHOD. Use the constructor of this class to register
105
-     * for the desired tokens and scope.
106
-     *
107
-     * @return int[]
108
-     * @see    __constructor()
109
-     */
110
-    final public function register()
111
-    {
112
-        return $this->tokens;
113
-
114
-    }//end register()
115
-
116
-
117
-    /**
118
-     * Processes the tokens that this test is listening for.
119
-     *
120
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
121
-     * @param int                         $stackPtr  The position in the stack where this
122
-     *                                               token was found.
123
-     *
124
-     * @return void|int Optionally returns a stack pointer. The sniff will not be
125
-     *                  called again on the current file until the returned stack
126
-     *                  pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
127
-     *                  the rest of the file.
128
-     * @see    processTokenWithinScope()
129
-     */
130
-    final public function process(File $phpcsFile, $stackPtr)
131
-    {
132
-        $tokens = $phpcsFile->getTokens();
133
-
134
-        $foundScope = false;
135
-        $skipPtrs   = [];
136
-        foreach ($tokens[$stackPtr]['conditions'] as $scope => $code) {
137
-            if (isset($this->scopeTokens[$code]) === true) {
138
-                $skipPtrs[] = $this->processTokenWithinScope($phpcsFile, $stackPtr, $scope);
139
-                $foundScope = true;
140
-            }
141
-        }
142
-
143
-        if ($this->listenOutside === true && $foundScope === false) {
144
-            $skipPtrs[] = $this->processTokenOutsideScope($phpcsFile, $stackPtr);
145
-        }
146
-
147
-        if (empty($skipPtrs) === false) {
148
-            return min($skipPtrs);
149
-        }
150
-
151
-        return;
152
-
153
-    }//end process()
154
-
155
-
156
-    /**
157
-     * Processes a token that is found within the scope that this test is
158
-     * listening to.
159
-     *
160
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
161
-     * @param int                         $stackPtr  The position in the stack where this
162
-     *                                               token was found.
163
-     * @param int                         $currScope The position in the tokens array that
164
-     *                                               opened the scope that this test is
165
-     *                                               listening for.
166
-     *
167
-     * @return void|int Optionally returns a stack pointer. The sniff will not be
168
-     *                  called again on the current file until the returned stack
169
-     *                  pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
170
-     *                  the rest of the file.
171
-     */
172
-    abstract protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope);
173
-
174
-
175
-    /**
176
-     * Processes a token that is found outside the scope that this test is
177
-     * listening to.
178
-     *
179
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
180
-     * @param int                         $stackPtr  The position in the stack where this
181
-     *                                               token was found.
182
-     *
183
-     * @return void|int Optionally returns a stack pointer. The sniff will not be
184
-     *                  called again on the current file until the returned stack
185
-     *                  pointer is reached. Return (count($tokens) + 1) to skip
186
-     *                  the rest of the file.
187
-     */
188
-    abstract protected function processTokenOutsideScope(File $phpcsFile, $stackPtr);
35
+	/**
36
+	 * The token types that this test wishes to listen to within the scope.
37
+	 *
38
+	 * @var array
39
+	 */
40
+	private $tokens = [];
41
+
42
+	/**
43
+	 * The type of scope opener tokens that this test wishes to listen to.
44
+	 *
45
+	 * @var string
46
+	 */
47
+	private $scopeTokens = [];
48
+
49
+	/**
50
+	 * True if this test should fire on tokens outside of the scope.
51
+	 *
52
+	 * @var boolean
53
+	 */
54
+	private $listenOutside = false;
55
+
56
+
57
+	/**
58
+	 * Constructs a new AbstractScopeTest.
59
+	 *
60
+	 * @param array   $scopeTokens   The type of scope the test wishes to listen to.
61
+	 * @param array   $tokens        The tokens that the test wishes to listen to
62
+	 *                               within the scope.
63
+	 * @param boolean $listenOutside If true this test will also alert the
64
+	 *                               extending class when a token is found outside
65
+	 *                               the scope, by calling the
66
+	 *                               processTokenOutsideScope method.
67
+	 *
68
+	 * @see    PHP_CodeSniffer.getValidScopeTokeners()
69
+	 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified tokens array is empty.
70
+	 */
71
+	public function __construct(
72
+		array $scopeTokens,
73
+		array $tokens,
74
+		$listenOutside=false
75
+	) {
76
+		if (empty($scopeTokens) === true) {
77
+			$error = 'The scope tokens list cannot be empty';
78
+			throw new RuntimeException($error);
79
+		}
80
+
81
+		if (empty($tokens) === true) {
82
+			$error = 'The tokens list cannot be empty';
83
+			throw new RuntimeException($error);
84
+		}
85
+
86
+		$invalidScopeTokens = array_intersect($scopeTokens, $tokens);
87
+		if (empty($invalidScopeTokens) === false) {
88
+			$invalid = implode(', ', $invalidScopeTokens);
89
+			$error   = "Scope tokens [$invalid] can't be in the tokens array";
90
+			throw new RuntimeException($error);
91
+		}
92
+
93
+		$this->listenOutside = $listenOutside;
94
+		$this->scopeTokens   = array_flip($scopeTokens);
95
+		$this->tokens        = $tokens;
96
+
97
+	}//end __construct()
98
+
99
+
100
+	/**
101
+	 * The method that is called to register the tokens this test wishes to
102
+	 * listen to.
103
+	 *
104
+	 * DO NOT OVERRIDE THIS METHOD. Use the constructor of this class to register
105
+	 * for the desired tokens and scope.
106
+	 *
107
+	 * @return int[]
108
+	 * @see    __constructor()
109
+	 */
110
+	final public function register()
111
+	{
112
+		return $this->tokens;
113
+
114
+	}//end register()
115
+
116
+
117
+	/**
118
+	 * Processes the tokens that this test is listening for.
119
+	 *
120
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
121
+	 * @param int                         $stackPtr  The position in the stack where this
122
+	 *                                               token was found.
123
+	 *
124
+	 * @return void|int Optionally returns a stack pointer. The sniff will not be
125
+	 *                  called again on the current file until the returned stack
126
+	 *                  pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
127
+	 *                  the rest of the file.
128
+	 * @see    processTokenWithinScope()
129
+	 */
130
+	final public function process(File $phpcsFile, $stackPtr)
131
+	{
132
+		$tokens = $phpcsFile->getTokens();
133
+
134
+		$foundScope = false;
135
+		$skipPtrs   = [];
136
+		foreach ($tokens[$stackPtr]['conditions'] as $scope => $code) {
137
+			if (isset($this->scopeTokens[$code]) === true) {
138
+				$skipPtrs[] = $this->processTokenWithinScope($phpcsFile, $stackPtr, $scope);
139
+				$foundScope = true;
140
+			}
141
+		}
142
+
143
+		if ($this->listenOutside === true && $foundScope === false) {
144
+			$skipPtrs[] = $this->processTokenOutsideScope($phpcsFile, $stackPtr);
145
+		}
146
+
147
+		if (empty($skipPtrs) === false) {
148
+			return min($skipPtrs);
149
+		}
150
+
151
+		return;
152
+
153
+	}//end process()
154
+
155
+
156
+	/**
157
+	 * Processes a token that is found within the scope that this test is
158
+	 * listening to.
159
+	 *
160
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
161
+	 * @param int                         $stackPtr  The position in the stack where this
162
+	 *                                               token was found.
163
+	 * @param int                         $currScope The position in the tokens array that
164
+	 *                                               opened the scope that this test is
165
+	 *                                               listening for.
166
+	 *
167
+	 * @return void|int Optionally returns a stack pointer. The sniff will not be
168
+	 *                  called again on the current file until the returned stack
169
+	 *                  pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
170
+	 *                  the rest of the file.
171
+	 */
172
+	abstract protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope);
173
+
174
+
175
+	/**
176
+	 * Processes a token that is found outside the scope that this test is
177
+	 * listening to.
178
+	 *
179
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
180
+	 * @param int                         $stackPtr  The position in the stack where this
181
+	 *                                               token was found.
182
+	 *
183
+	 * @return void|int Optionally returns a stack pointer. The sniff will not be
184
+	 *                  called again on the current file until the returned stack
185
+	 *                  pointer is reached. Return (count($tokens) + 1) to skip
186
+	 *                  the rest of the file.
187
+	 */
188
+	abstract protected function processTokenOutsideScope(File $phpcsFile, $stackPtr);
189 189
 
190 190
 
191 191
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Sniffs/AbstractPatternSniff.php 1 patch
Indentation   +914 added lines, -914 removed lines patch added patch discarded remove patch
@@ -17,920 +17,920 @@
 block discarded – undo
17 17
 abstract class AbstractPatternSniff implements Sniff
18 18
 {
19 19
 
20
-    /**
21
-     * If true, comments will be ignored if they are found in the code.
22
-     *
23
-     * @var boolean
24
-     */
25
-    public $ignoreComments = false;
26
-
27
-    /**
28
-     * The current file being checked.
29
-     *
30
-     * @var string
31
-     */
32
-    protected $currFile = '';
33
-
34
-    /**
35
-     * The parsed patterns array.
36
-     *
37
-     * @var array
38
-     */
39
-    private $parsedPatterns = [];
40
-
41
-    /**
42
-     * Tokens that this sniff wishes to process outside of the patterns.
43
-     *
44
-     * @var int[]
45
-     * @see registerSupplementary()
46
-     * @see processSupplementary()
47
-     */
48
-    private $supplementaryTokens = [];
49
-
50
-    /**
51
-     * Positions in the stack where errors have occurred.
52
-     *
53
-     * @var array<int, bool>
54
-     */
55
-    private $errorPos = [];
56
-
57
-
58
-    /**
59
-     * Constructs a AbstractPatternSniff.
60
-     *
61
-     * @param boolean $ignoreComments If true, comments will be ignored.
62
-     */
63
-    public function __construct($ignoreComments=null)
64
-    {
65
-        // This is here for backwards compatibility.
66
-        if ($ignoreComments !== null) {
67
-            $this->ignoreComments = $ignoreComments;
68
-        }
69
-
70
-        $this->supplementaryTokens = $this->registerSupplementary();
71
-
72
-    }//end __construct()
73
-
74
-
75
-    /**
76
-     * Registers the tokens to listen to.
77
-     *
78
-     * Classes extending <i>AbstractPatternTest</i> should implement the
79
-     * <i>getPatterns()</i> method to register the patterns they wish to test.
80
-     *
81
-     * @return int[]
82
-     * @see    process()
83
-     */
84
-    final public function register()
85
-    {
86
-        $listenTypes = [];
87
-        $patterns    = $this->getPatterns();
88
-
89
-        foreach ($patterns as $pattern) {
90
-            $parsedPattern = $this->parse($pattern);
91
-
92
-            // Find a token position in the pattern that we can use
93
-            // for a listener token.
94
-            $pos           = $this->getListenerTokenPos($parsedPattern);
95
-            $tokenType     = $parsedPattern[$pos]['token'];
96
-            $listenTypes[] = $tokenType;
97
-
98
-            $patternArray = [
99
-                'listen_pos'   => $pos,
100
-                'pattern'      => $parsedPattern,
101
-                'pattern_code' => $pattern,
102
-            ];
103
-
104
-            if (isset($this->parsedPatterns[$tokenType]) === false) {
105
-                $this->parsedPatterns[$tokenType] = [];
106
-            }
107
-
108
-            $this->parsedPatterns[$tokenType][] = $patternArray;
109
-        }//end foreach
110
-
111
-        return array_unique(array_merge($listenTypes, $this->supplementaryTokens));
112
-
113
-    }//end register()
114
-
115
-
116
-    /**
117
-     * Returns the token types that the specified pattern is checking for.
118
-     *
119
-     * Returned array is in the format:
120
-     * <code>
121
-     *   array(
122
-     *      T_WHITESPACE => 0, // 0 is the position where the T_WHITESPACE token
123
-     *                         // should occur in the pattern.
124
-     *   );
125
-     * </code>
126
-     *
127
-     * @param array $pattern The parsed pattern to find the acquire the token
128
-     *                       types from.
129
-     *
130
-     * @return array<int, int>
131
-     */
132
-    private function getPatternTokenTypes($pattern)
133
-    {
134
-        $tokenTypes = [];
135
-        foreach ($pattern as $pos => $patternInfo) {
136
-            if ($patternInfo['type'] === 'token') {
137
-                if (isset($tokenTypes[$patternInfo['token']]) === false) {
138
-                    $tokenTypes[$patternInfo['token']] = $pos;
139
-                }
140
-            }
141
-        }
142
-
143
-        return $tokenTypes;
144
-
145
-    }//end getPatternTokenTypes()
146
-
147
-
148
-    /**
149
-     * Returns the position in the pattern that this test should register as
150
-     * a listener for the pattern.
151
-     *
152
-     * @param array $pattern The pattern to acquire the listener for.
153
-     *
154
-     * @return int The position in the pattern that this test should register
155
-     *             as the listener.
156
-     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If we could not determine a token to listen for.
157
-     */
158
-    private function getListenerTokenPos($pattern)
159
-    {
160
-        $tokenTypes = $this->getPatternTokenTypes($pattern);
161
-        $tokenCodes = array_keys($tokenTypes);
162
-        $token      = Tokens::getHighestWeightedToken($tokenCodes);
163
-
164
-        // If we could not get a token.
165
-        if ($token === false) {
166
-            $error = 'Could not determine a token to listen for';
167
-            throw new RuntimeException($error);
168
-        }
169
-
170
-        return $tokenTypes[$token];
171
-
172
-    }//end getListenerTokenPos()
173
-
174
-
175
-    /**
176
-     * Processes the test.
177
-     *
178
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
179
-     *                                               token occurred.
180
-     * @param int                         $stackPtr  The position in the tokens stack
181
-     *                                               where the listening token type
182
-     *                                               was found.
183
-     *
184
-     * @return void
185
-     * @see    register()
186
-     */
187
-    final public function process(File $phpcsFile, $stackPtr)
188
-    {
189
-        $file = $phpcsFile->getFilename();
190
-        if ($this->currFile !== $file) {
191
-            // We have changed files, so clean up.
192
-            $this->errorPos = [];
193
-            $this->currFile = $file;
194
-        }
195
-
196
-        $tokens = $phpcsFile->getTokens();
197
-
198
-        if (in_array($tokens[$stackPtr]['code'], $this->supplementaryTokens, true) === true) {
199
-            $this->processSupplementary($phpcsFile, $stackPtr);
200
-        }
201
-
202
-        $type = $tokens[$stackPtr]['code'];
203
-
204
-        // If the type is not set, then it must have been a token registered
205
-        // with registerSupplementary().
206
-        if (isset($this->parsedPatterns[$type]) === false) {
207
-            return;
208
-        }
209
-
210
-        $allErrors = [];
211
-
212
-        // Loop over each pattern that is listening to the current token type
213
-        // that we are processing.
214
-        foreach ($this->parsedPatterns[$type] as $patternInfo) {
215
-            // If processPattern returns false, then the pattern that we are
216
-            // checking the code with must not be designed to check that code.
217
-            $errors = $this->processPattern($patternInfo, $phpcsFile, $stackPtr);
218
-            if ($errors === false) {
219
-                // The pattern didn't match.
220
-                continue;
221
-            } else if (empty($errors) === true) {
222
-                // The pattern matched, but there were no errors.
223
-                break;
224
-            }
225
-
226
-            foreach ($errors as $stackPtr => $error) {
227
-                if (isset($this->errorPos[$stackPtr]) === false) {
228
-                    $this->errorPos[$stackPtr] = true;
229
-                    $allErrors[$stackPtr]      = $error;
230
-                }
231
-            }
232
-        }
233
-
234
-        foreach ($allErrors as $stackPtr => $error) {
235
-            $phpcsFile->addError($error, $stackPtr, 'Found');
236
-        }
237
-
238
-    }//end process()
239
-
240
-
241
-    /**
242
-     * Processes the pattern and verifies the code at $stackPtr.
243
-     *
244
-     * @param array                       $patternInfo Information about the pattern used
245
-     *                                                 for checking, which includes are
246
-     *                                                 parsed token representation of the
247
-     *                                                 pattern.
248
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile   The PHP_CodeSniffer file where the
249
-     *                                                 token occurred.
250
-     * @param int                         $stackPtr    The position in the tokens stack where
251
-     *                                                 the listening token type was found.
252
-     *
253
-     * @return array
254
-     */
255
-    protected function processPattern($patternInfo, File $phpcsFile, $stackPtr)
256
-    {
257
-        $tokens      = $phpcsFile->getTokens();
258
-        $pattern     = $patternInfo['pattern'];
259
-        $patternCode = $patternInfo['pattern_code'];
260
-        $errors      = [];
261
-        $found       = '';
262
-
263
-        $ignoreTokens = [T_WHITESPACE => T_WHITESPACE];
264
-        if ($this->ignoreComments === true) {
265
-            $ignoreTokens += Tokens::$commentTokens;
266
-        }
267
-
268
-        $origStackPtr = $stackPtr;
269
-        $hasError     = false;
270
-
271
-        if ($patternInfo['listen_pos'] > 0) {
272
-            $stackPtr--;
273
-
274
-            for ($i = ($patternInfo['listen_pos'] - 1); $i >= 0; $i--) {
275
-                if ($pattern[$i]['type'] === 'token') {
276
-                    if ($pattern[$i]['token'] === T_WHITESPACE) {
277
-                        if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
278
-                            $found = $tokens[$stackPtr]['content'].$found;
279
-                        }
280
-
281
-                        // Only check the size of the whitespace if this is not
282
-                        // the first token. We don't care about the size of
283
-                        // leading whitespace, just that there is some.
284
-                        if ($i !== 0) {
285
-                            if ($tokens[$stackPtr]['content'] !== $pattern[$i]['value']) {
286
-                                $hasError = true;
287
-                            }
288
-                        }
289
-                    } else {
290
-                        // Check to see if this important token is the same as the
291
-                        // previous important token in the pattern. If it is not,
292
-                        // then the pattern cannot be for this piece of code.
293
-                        $prev = $phpcsFile->findPrevious(
294
-                            $ignoreTokens,
295
-                            $stackPtr,
296
-                            null,
297
-                            true
298
-                        );
299
-
300
-                        if ($prev === false
301
-                            || $tokens[$prev]['code'] !== $pattern[$i]['token']
302
-                        ) {
303
-                            return false;
304
-                        }
305
-
306
-                        // If we skipped past some whitespace tokens, then add them
307
-                        // to the found string.
308
-                        $tokenContent = $phpcsFile->getTokensAsString(
309
-                            ($prev + 1),
310
-                            ($stackPtr - $prev - 1)
311
-                        );
312
-
313
-                        $found = $tokens[$prev]['content'].$tokenContent.$found;
314
-
315
-                        if (isset($pattern[($i - 1)]) === true
316
-                            && $pattern[($i - 1)]['type'] === 'skip'
317
-                        ) {
318
-                            $stackPtr = $prev;
319
-                        } else {
320
-                            $stackPtr = ($prev - 1);
321
-                        }
322
-                    }//end if
323
-                } else if ($pattern[$i]['type'] === 'skip') {
324
-                    // Skip to next piece of relevant code.
325
-                    if ($pattern[$i]['to'] === 'parenthesis_closer') {
326
-                        $to = 'parenthesis_opener';
327
-                    } else {
328
-                        $to = 'scope_opener';
329
-                    }
330
-
331
-                    // Find the previous opener.
332
-                    $next = $phpcsFile->findPrevious(
333
-                        $ignoreTokens,
334
-                        $stackPtr,
335
-                        null,
336
-                        true
337
-                    );
338
-
339
-                    if ($next === false || isset($tokens[$next][$to]) === false) {
340
-                        // If there was not opener, then we must be
341
-                        // using the wrong pattern.
342
-                        return false;
343
-                    }
344
-
345
-                    if ($to === 'parenthesis_opener') {
346
-                        $found = '{'.$found;
347
-                    } else {
348
-                        $found = '('.$found;
349
-                    }
350
-
351
-                    $found = '...'.$found;
352
-
353
-                    // Skip to the opening token.
354
-                    $stackPtr = ($tokens[$next][$to] - 1);
355
-                } else if ($pattern[$i]['type'] === 'string') {
356
-                    $found = 'abc';
357
-                } else if ($pattern[$i]['type'] === 'newline') {
358
-                    if ($this->ignoreComments === true
359
-                        && isset(Tokens::$commentTokens[$tokens[$stackPtr]['code']]) === true
360
-                    ) {
361
-                        $startComment = $phpcsFile->findPrevious(
362
-                            Tokens::$commentTokens,
363
-                            ($stackPtr - 1),
364
-                            null,
365
-                            true
366
-                        );
367
-
368
-                        if ($tokens[$startComment]['line'] !== $tokens[($startComment + 1)]['line']) {
369
-                            $startComment++;
370
-                        }
371
-
372
-                        $tokenContent = $phpcsFile->getTokensAsString(
373
-                            $startComment,
374
-                            ($stackPtr - $startComment + 1)
375
-                        );
376
-
377
-                        $found    = $tokenContent.$found;
378
-                        $stackPtr = ($startComment - 1);
379
-                    }
380
-
381
-                    if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
382
-                        if ($tokens[$stackPtr]['content'] !== $phpcsFile->eolChar) {
383
-                            $found = $tokens[$stackPtr]['content'].$found;
384
-
385
-                            // This may just be an indent that comes after a newline
386
-                            // so check the token before to make sure. If it is a newline, we
387
-                            // can ignore the error here.
388
-                            if (($tokens[($stackPtr - 1)]['content'] !== $phpcsFile->eolChar)
389
-                                && ($this->ignoreComments === true
390
-                                && isset(Tokens::$commentTokens[$tokens[($stackPtr - 1)]['code']]) === false)
391
-                            ) {
392
-                                $hasError = true;
393
-                            } else {
394
-                                $stackPtr--;
395
-                            }
396
-                        } else {
397
-                            $found = 'EOL'.$found;
398
-                        }
399
-                    } else {
400
-                        $found    = $tokens[$stackPtr]['content'].$found;
401
-                        $hasError = true;
402
-                    }//end if
403
-
404
-                    if ($hasError === false && $pattern[($i - 1)]['type'] !== 'newline') {
405
-                        // Make sure they only have 1 newline.
406
-                        $prev = $phpcsFile->findPrevious($ignoreTokens, ($stackPtr - 1), null, true);
407
-                        if ($prev !== false && $tokens[$prev]['line'] !== $tokens[$stackPtr]['line']) {
408
-                            $hasError = true;
409
-                        }
410
-                    }
411
-                }//end if
412
-            }//end for
413
-        }//end if
414
-
415
-        $stackPtr          = $origStackPtr;
416
-        $lastAddedStackPtr = null;
417
-        $patternLen        = count($pattern);
418
-
419
-        for ($i = $patternInfo['listen_pos']; $i < $patternLen; $i++) {
420
-            if (isset($tokens[$stackPtr]) === false) {
421
-                break;
422
-            }
423
-
424
-            if ($pattern[$i]['type'] === 'token') {
425
-                if ($pattern[$i]['token'] === T_WHITESPACE) {
426
-                    if ($this->ignoreComments === true) {
427
-                        // If we are ignoring comments, check to see if this current
428
-                        // token is a comment. If so skip it.
429
-                        if (isset(Tokens::$commentTokens[$tokens[$stackPtr]['code']]) === true) {
430
-                            continue;
431
-                        }
432
-
433
-                        // If the next token is a comment, the we need to skip the
434
-                        // current token as we should allow a space before a
435
-                        // comment for readability.
436
-                        if (isset($tokens[($stackPtr + 1)]) === true
437
-                            && isset(Tokens::$commentTokens[$tokens[($stackPtr + 1)]['code']]) === true
438
-                        ) {
439
-                            continue;
440
-                        }
441
-                    }
442
-
443
-                    $tokenContent = '';
444
-                    if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
445
-                        if (isset($pattern[($i + 1)]) === false) {
446
-                            // This is the last token in the pattern, so just compare
447
-                            // the next token of content.
448
-                            $tokenContent = $tokens[$stackPtr]['content'];
449
-                        } else {
450
-                            // Get all the whitespace to the next token.
451
-                            $next = $phpcsFile->findNext(
452
-                                Tokens::$emptyTokens,
453
-                                $stackPtr,
454
-                                null,
455
-                                true
456
-                            );
457
-
458
-                            $tokenContent = $phpcsFile->getTokensAsString(
459
-                                $stackPtr,
460
-                                ($next - $stackPtr)
461
-                            );
462
-
463
-                            $lastAddedStackPtr = $stackPtr;
464
-                            $stackPtr          = $next;
465
-                        }//end if
466
-
467
-                        if ($stackPtr !== $lastAddedStackPtr) {
468
-                            $found .= $tokenContent;
469
-                        }
470
-                    } else {
471
-                        if ($stackPtr !== $lastAddedStackPtr) {
472
-                            $found            .= $tokens[$stackPtr]['content'];
473
-                            $lastAddedStackPtr = $stackPtr;
474
-                        }
475
-                    }//end if
476
-
477
-                    if (isset($pattern[($i + 1)]) === true
478
-                        && $pattern[($i + 1)]['type'] === 'skip'
479
-                    ) {
480
-                        // The next token is a skip token, so we just need to make
481
-                        // sure the whitespace we found has *at least* the
482
-                        // whitespace required.
483
-                        if (strpos($tokenContent, $pattern[$i]['value']) !== 0) {
484
-                            $hasError = true;
485
-                        }
486
-                    } else {
487
-                        if ($tokenContent !== $pattern[$i]['value']) {
488
-                            $hasError = true;
489
-                        }
490
-                    }
491
-                } else {
492
-                    // Check to see if this important token is the same as the
493
-                    // next important token in the pattern. If it is not, then
494
-                    // the pattern cannot be for this piece of code.
495
-                    $next = $phpcsFile->findNext(
496
-                        $ignoreTokens,
497
-                        $stackPtr,
498
-                        null,
499
-                        true
500
-                    );
501
-
502
-                    if ($next === false
503
-                        || $tokens[$next]['code'] !== $pattern[$i]['token']
504
-                    ) {
505
-                        // The next important token did not match the pattern.
506
-                        return false;
507
-                    }
508
-
509
-                    if ($lastAddedStackPtr !== null) {
510
-                        if (($tokens[$next]['code'] === T_OPEN_CURLY_BRACKET
511
-                            || $tokens[$next]['code'] === T_CLOSE_CURLY_BRACKET)
512
-                            && isset($tokens[$next]['scope_condition']) === true
513
-                            && $tokens[$next]['scope_condition'] > $lastAddedStackPtr
514
-                        ) {
515
-                            // This is a brace, but the owner of it is after the current
516
-                            // token, which means it does not belong to any token in
517
-                            // our pattern. This means the pattern is not for us.
518
-                            return false;
519
-                        }
520
-
521
-                        if (($tokens[$next]['code'] === T_OPEN_PARENTHESIS
522
-                            || $tokens[$next]['code'] === T_CLOSE_PARENTHESIS)
523
-                            && isset($tokens[$next]['parenthesis_owner']) === true
524
-                            && $tokens[$next]['parenthesis_owner'] > $lastAddedStackPtr
525
-                        ) {
526
-                            // This is a bracket, but the owner of it is after the current
527
-                            // token, which means it does not belong to any token in
528
-                            // our pattern. This means the pattern is not for us.
529
-                            return false;
530
-                        }
531
-                    }//end if
532
-
533
-                    // If we skipped past some whitespace tokens, then add them
534
-                    // to the found string.
535
-                    if (($next - $stackPtr) > 0) {
536
-                        $hasComment = false;
537
-                        for ($j = $stackPtr; $j < $next; $j++) {
538
-                            $found .= $tokens[$j]['content'];
539
-                            if (isset(Tokens::$commentTokens[$tokens[$j]['code']]) === true) {
540
-                                $hasComment = true;
541
-                            }
542
-                        }
543
-
544
-                        // If we are not ignoring comments, this additional
545
-                        // whitespace or comment is not allowed. If we are
546
-                        // ignoring comments, there needs to be at least one
547
-                        // comment for this to be allowed.
548
-                        if ($this->ignoreComments === false
549
-                            || ($this->ignoreComments === true
550
-                            && $hasComment === false)
551
-                        ) {
552
-                            $hasError = true;
553
-                        }
554
-
555
-                        // Even when ignoring comments, we are not allowed to include
556
-                        // newlines without the pattern specifying them, so
557
-                        // everything should be on the same line.
558
-                        if ($tokens[$next]['line'] !== $tokens[$stackPtr]['line']) {
559
-                            $hasError = true;
560
-                        }
561
-                    }//end if
562
-
563
-                    if ($next !== $lastAddedStackPtr) {
564
-                        $found            .= $tokens[$next]['content'];
565
-                        $lastAddedStackPtr = $next;
566
-                    }
567
-
568
-                    if (isset($pattern[($i + 1)]) === true
569
-                        && $pattern[($i + 1)]['type'] === 'skip'
570
-                    ) {
571
-                        $stackPtr = $next;
572
-                    } else {
573
-                        $stackPtr = ($next + 1);
574
-                    }
575
-                }//end if
576
-            } else if ($pattern[$i]['type'] === 'skip') {
577
-                if ($pattern[$i]['to'] === 'unknown') {
578
-                    $next = $phpcsFile->findNext(
579
-                        $pattern[($i + 1)]['token'],
580
-                        $stackPtr
581
-                    );
582
-
583
-                    if ($next === false) {
584
-                        // Couldn't find the next token, so we must
585
-                        // be using the wrong pattern.
586
-                        return false;
587
-                    }
588
-
589
-                    $found   .= '...';
590
-                    $stackPtr = $next;
591
-                } else {
592
-                    // Find the previous opener.
593
-                    $next = $phpcsFile->findPrevious(
594
-                        Tokens::$blockOpeners,
595
-                        $stackPtr
596
-                    );
597
-
598
-                    if ($next === false
599
-                        || isset($tokens[$next][$pattern[$i]['to']]) === false
600
-                    ) {
601
-                        // If there was not opener, then we must
602
-                        // be using the wrong pattern.
603
-                        return false;
604
-                    }
605
-
606
-                    $found .= '...';
607
-                    if ($pattern[$i]['to'] === 'parenthesis_closer') {
608
-                        $found .= ')';
609
-                    } else {
610
-                        $found .= '}';
611
-                    }
612
-
613
-                    // Skip to the closing token.
614
-                    $stackPtr = ($tokens[$next][$pattern[$i]['to']] + 1);
615
-                }//end if
616
-            } else if ($pattern[$i]['type'] === 'string') {
617
-                if ($tokens[$stackPtr]['code'] !== T_STRING) {
618
-                    $hasError = true;
619
-                }
620
-
621
-                if ($stackPtr !== $lastAddedStackPtr) {
622
-                    $found            .= 'abc';
623
-                    $lastAddedStackPtr = $stackPtr;
624
-                }
625
-
626
-                $stackPtr++;
627
-            } else if ($pattern[$i]['type'] === 'newline') {
628
-                // Find the next token that contains a newline character.
629
-                $newline = 0;
630
-                for ($j = $stackPtr; $j < $phpcsFile->numTokens; $j++) {
631
-                    if (strpos($tokens[$j]['content'], $phpcsFile->eolChar) !== false) {
632
-                        $newline = $j;
633
-                        break;
634
-                    }
635
-                }
636
-
637
-                if ($newline === 0) {
638
-                    // We didn't find a newline character in the rest of the file.
639
-                    $next     = ($phpcsFile->numTokens - 1);
640
-                    $hasError = true;
641
-                } else {
642
-                    if ($this->ignoreComments === false) {
643
-                        // The newline character cannot be part of a comment.
644
-                        if (isset(Tokens::$commentTokens[$tokens[$newline]['code']]) === true) {
645
-                            $hasError = true;
646
-                        }
647
-                    }
648
-
649
-                    if ($newline === $stackPtr) {
650
-                        $next = ($stackPtr + 1);
651
-                    } else {
652
-                        // Check that there were no significant tokens that we
653
-                        // skipped over to find our newline character.
654
-                        $next = $phpcsFile->findNext(
655
-                            $ignoreTokens,
656
-                            $stackPtr,
657
-                            null,
658
-                            true
659
-                        );
660
-
661
-                        if ($next < $newline) {
662
-                            // We skipped a non-ignored token.
663
-                            $hasError = true;
664
-                        } else {
665
-                            $next = ($newline + 1);
666
-                        }
667
-                    }
668
-                }//end if
669
-
670
-                if ($stackPtr !== $lastAddedStackPtr) {
671
-                    $found .= $phpcsFile->getTokensAsString(
672
-                        $stackPtr,
673
-                        ($next - $stackPtr)
674
-                    );
675
-
676
-                    $lastAddedStackPtr = ($next - 1);
677
-                }
678
-
679
-                $stackPtr = $next;
680
-            }//end if
681
-        }//end for
682
-
683
-        if ($hasError === true) {
684
-            $error = $this->prepareError($found, $patternCode);
685
-            $errors[$origStackPtr] = $error;
686
-        }
687
-
688
-        return $errors;
689
-
690
-    }//end processPattern()
691
-
692
-
693
-    /**
694
-     * Prepares an error for the specified patternCode.
695
-     *
696
-     * @param string $found       The actual found string in the code.
697
-     * @param string $patternCode The expected pattern code.
698
-     *
699
-     * @return string The error message.
700
-     */
701
-    protected function prepareError($found, $patternCode)
702
-    {
703
-        $found    = str_replace("\r\n", '\n', $found);
704
-        $found    = str_replace("\n", '\n', $found);
705
-        $found    = str_replace("\r", '\n', $found);
706
-        $found    = str_replace("\t", '\t', $found);
707
-        $found    = str_replace('EOL', '\n', $found);
708
-        $expected = str_replace('EOL', '\n', $patternCode);
709
-
710
-        $error = "Expected \"$expected\"; found \"$found\"";
711
-
712
-        return $error;
713
-
714
-    }//end prepareError()
715
-
716
-
717
-    /**
718
-     * Returns the patterns that should be checked.
719
-     *
720
-     * @return string[]
721
-     */
722
-    abstract protected function getPatterns();
723
-
724
-
725
-    /**
726
-     * Registers any supplementary tokens that this test might wish to process.
727
-     *
728
-     * A sniff may wish to register supplementary tests when it wishes to group
729
-     * an arbitrary validation that cannot be performed using a pattern, with
730
-     * other pattern tests.
731
-     *
732
-     * @return int[]
733
-     * @see    processSupplementary()
734
-     */
735
-    protected function registerSupplementary()
736
-    {
737
-        return [];
738
-
739
-    }//end registerSupplementary()
740
-
741
-
742
-     /**
743
-      * Processes any tokens registered with registerSupplementary().
744
-      *
745
-      * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where to
746
-      *                                               process the skip.
747
-      * @param int                         $stackPtr  The position in the tokens stack to
748
-      *                                               process.
749
-      *
750
-      * @return void
751
-      * @see    registerSupplementary()
752
-      */
753
-    protected function processSupplementary(File $phpcsFile, $stackPtr)
754
-    {
755
-
756
-    }//end processSupplementary()
757
-
758
-
759
-    /**
760
-     * Parses a pattern string into an array of pattern steps.
761
-     *
762
-     * @param string $pattern The pattern to parse.
763
-     *
764
-     * @return array The parsed pattern array.
765
-     * @see    createSkipPattern()
766
-     * @see    createTokenPattern()
767
-     */
768
-    private function parse($pattern)
769
-    {
770
-        $patterns   = [];
771
-        $length     = strlen($pattern);
772
-        $lastToken  = 0;
773
-        $firstToken = 0;
774
-
775
-        for ($i = 0; $i < $length; $i++) {
776
-            $specialPattern = false;
777
-            $isLastChar     = ($i === ($length - 1));
778
-            $oldFirstToken  = $firstToken;
779
-
780
-            if (substr($pattern, $i, 3) === '...') {
781
-                // It's a skip pattern. The skip pattern requires the
782
-                // content of the token in the "from" position and the token
783
-                // to skip to.
784
-                $specialPattern = $this->createSkipPattern($pattern, ($i - 1));
785
-                $lastToken      = ($i - $firstToken);
786
-                $firstToken     = ($i + 3);
787
-                $i += 2;
788
-
789
-                if ($specialPattern['to'] !== 'unknown') {
790
-                    $firstToken++;
791
-                }
792
-            } else if (substr($pattern, $i, 3) === 'abc') {
793
-                $specialPattern = ['type' => 'string'];
794
-                $lastToken      = ($i - $firstToken);
795
-                $firstToken     = ($i + 3);
796
-                $i += 2;
797
-            } else if (substr($pattern, $i, 3) === 'EOL') {
798
-                $specialPattern = ['type' => 'newline'];
799
-                $lastToken      = ($i - $firstToken);
800
-                $firstToken     = ($i + 3);
801
-                $i += 2;
802
-            }//end if
803
-
804
-            if ($specialPattern !== false || $isLastChar === true) {
805
-                // If we are at the end of the string, don't worry about a limit.
806
-                if ($isLastChar === true) {
807
-                    // Get the string from the end of the last skip pattern, if any,
808
-                    // to the end of the pattern string.
809
-                    $str = substr($pattern, $oldFirstToken);
810
-                } else {
811
-                    // Get the string from the end of the last special pattern,
812
-                    // if any, to the start of this special pattern.
813
-                    if ($lastToken === 0) {
814
-                        // Note that if the last special token was zero characters ago,
815
-                        // there will be nothing to process so we can skip this bit.
816
-                        // This happens if you have something like: EOL... in your pattern.
817
-                        $str = '';
818
-                    } else {
819
-                        $str = substr($pattern, $oldFirstToken, $lastToken);
820
-                    }
821
-                }
822
-
823
-                if ($str !== '') {
824
-                    $tokenPatterns = $this->createTokenPattern($str);
825
-                    foreach ($tokenPatterns as $tokenPattern) {
826
-                        $patterns[] = $tokenPattern;
827
-                    }
828
-                }
829
-
830
-                // Make sure we don't skip the last token.
831
-                if ($isLastChar === false && $i === ($length - 1)) {
832
-                    $i--;
833
-                }
834
-            }//end if
835
-
836
-            // Add the skip pattern *after* we have processed
837
-            // all the tokens from the end of the last skip pattern
838
-            // to the start of this skip pattern.
839
-            if ($specialPattern !== false) {
840
-                $patterns[] = $specialPattern;
841
-            }
842
-        }//end for
843
-
844
-        return $patterns;
845
-
846
-    }//end parse()
847
-
848
-
849
-    /**
850
-     * Creates a skip pattern.
851
-     *
852
-     * @param string $pattern The pattern being parsed.
853
-     * @param string $from    The token content that the skip pattern starts from.
854
-     *
855
-     * @return array The pattern step.
856
-     * @see    createTokenPattern()
857
-     * @see    parse()
858
-     */
859
-    private function createSkipPattern($pattern, $from)
860
-    {
861
-        $skip = ['type' => 'skip'];
862
-
863
-        $nestedParenthesis = 0;
864
-        $nestedBraces      = 0;
865
-        for ($start = $from; $start >= 0; $start--) {
866
-            switch ($pattern[$start]) {
867
-            case '(':
868
-                if ($nestedParenthesis === 0) {
869
-                    $skip['to'] = 'parenthesis_closer';
870
-                }
871
-
872
-                $nestedParenthesis--;
873
-                break;
874
-            case '{':
875
-                if ($nestedBraces === 0) {
876
-                    $skip['to'] = 'scope_closer';
877
-                }
878
-
879
-                $nestedBraces--;
880
-                break;
881
-            case '}':
882
-                $nestedBraces++;
883
-                break;
884
-            case ')':
885
-                $nestedParenthesis++;
886
-                break;
887
-            }//end switch
888
-
889
-            if (isset($skip['to']) === true) {
890
-                break;
891
-            }
892
-        }//end for
893
-
894
-        if (isset($skip['to']) === false) {
895
-            $skip['to'] = 'unknown';
896
-        }
897
-
898
-        return $skip;
899
-
900
-    }//end createSkipPattern()
901
-
902
-
903
-    /**
904
-     * Creates a token pattern.
905
-     *
906
-     * @param string $str The tokens string that the pattern should match.
907
-     *
908
-     * @return array The pattern step.
909
-     * @see    createSkipPattern()
910
-     * @see    parse()
911
-     */
912
-    private function createTokenPattern($str)
913
-    {
914
-        // Don't add a space after the closing php tag as it will add a new
915
-        // whitespace token.
916
-        $tokenizer = new PHP('<?php '.$str.'?>', null);
917
-
918
-        // Remove the <?php tag from the front and the end php tag from the back.
919
-        $tokens = $tokenizer->getTokens();
920
-        $tokens = array_slice($tokens, 1, (count($tokens) - 2));
921
-
922
-        $patterns = [];
923
-        foreach ($tokens as $patternInfo) {
924
-            $patterns[] = [
925
-                'type'  => 'token',
926
-                'token' => $patternInfo['code'],
927
-                'value' => $patternInfo['content'],
928
-            ];
929
-        }
930
-
931
-        return $patterns;
932
-
933
-    }//end createTokenPattern()
20
+	/**
21
+	 * If true, comments will be ignored if they are found in the code.
22
+	 *
23
+	 * @var boolean
24
+	 */
25
+	public $ignoreComments = false;
26
+
27
+	/**
28
+	 * The current file being checked.
29
+	 *
30
+	 * @var string
31
+	 */
32
+	protected $currFile = '';
33
+
34
+	/**
35
+	 * The parsed patterns array.
36
+	 *
37
+	 * @var array
38
+	 */
39
+	private $parsedPatterns = [];
40
+
41
+	/**
42
+	 * Tokens that this sniff wishes to process outside of the patterns.
43
+	 *
44
+	 * @var int[]
45
+	 * @see registerSupplementary()
46
+	 * @see processSupplementary()
47
+	 */
48
+	private $supplementaryTokens = [];
49
+
50
+	/**
51
+	 * Positions in the stack where errors have occurred.
52
+	 *
53
+	 * @var array<int, bool>
54
+	 */
55
+	private $errorPos = [];
56
+
57
+
58
+	/**
59
+	 * Constructs a AbstractPatternSniff.
60
+	 *
61
+	 * @param boolean $ignoreComments If true, comments will be ignored.
62
+	 */
63
+	public function __construct($ignoreComments=null)
64
+	{
65
+		// This is here for backwards compatibility.
66
+		if ($ignoreComments !== null) {
67
+			$this->ignoreComments = $ignoreComments;
68
+		}
69
+
70
+		$this->supplementaryTokens = $this->registerSupplementary();
71
+
72
+	}//end __construct()
73
+
74
+
75
+	/**
76
+	 * Registers the tokens to listen to.
77
+	 *
78
+	 * Classes extending <i>AbstractPatternTest</i> should implement the
79
+	 * <i>getPatterns()</i> method to register the patterns they wish to test.
80
+	 *
81
+	 * @return int[]
82
+	 * @see    process()
83
+	 */
84
+	final public function register()
85
+	{
86
+		$listenTypes = [];
87
+		$patterns    = $this->getPatterns();
88
+
89
+		foreach ($patterns as $pattern) {
90
+			$parsedPattern = $this->parse($pattern);
91
+
92
+			// Find a token position in the pattern that we can use
93
+			// for a listener token.
94
+			$pos           = $this->getListenerTokenPos($parsedPattern);
95
+			$tokenType     = $parsedPattern[$pos]['token'];
96
+			$listenTypes[] = $tokenType;
97
+
98
+			$patternArray = [
99
+				'listen_pos'   => $pos,
100
+				'pattern'      => $parsedPattern,
101
+				'pattern_code' => $pattern,
102
+			];
103
+
104
+			if (isset($this->parsedPatterns[$tokenType]) === false) {
105
+				$this->parsedPatterns[$tokenType] = [];
106
+			}
107
+
108
+			$this->parsedPatterns[$tokenType][] = $patternArray;
109
+		}//end foreach
110
+
111
+		return array_unique(array_merge($listenTypes, $this->supplementaryTokens));
112
+
113
+	}//end register()
114
+
115
+
116
+	/**
117
+	 * Returns the token types that the specified pattern is checking for.
118
+	 *
119
+	 * Returned array is in the format:
120
+	 * <code>
121
+	 *   array(
122
+	 *      T_WHITESPACE => 0, // 0 is the position where the T_WHITESPACE token
123
+	 *                         // should occur in the pattern.
124
+	 *   );
125
+	 * </code>
126
+	 *
127
+	 * @param array $pattern The parsed pattern to find the acquire the token
128
+	 *                       types from.
129
+	 *
130
+	 * @return array<int, int>
131
+	 */
132
+	private function getPatternTokenTypes($pattern)
133
+	{
134
+		$tokenTypes = [];
135
+		foreach ($pattern as $pos => $patternInfo) {
136
+			if ($patternInfo['type'] === 'token') {
137
+				if (isset($tokenTypes[$patternInfo['token']]) === false) {
138
+					$tokenTypes[$patternInfo['token']] = $pos;
139
+				}
140
+			}
141
+		}
142
+
143
+		return $tokenTypes;
144
+
145
+	}//end getPatternTokenTypes()
146
+
147
+
148
+	/**
149
+	 * Returns the position in the pattern that this test should register as
150
+	 * a listener for the pattern.
151
+	 *
152
+	 * @param array $pattern The pattern to acquire the listener for.
153
+	 *
154
+	 * @return int The position in the pattern that this test should register
155
+	 *             as the listener.
156
+	 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If we could not determine a token to listen for.
157
+	 */
158
+	private function getListenerTokenPos($pattern)
159
+	{
160
+		$tokenTypes = $this->getPatternTokenTypes($pattern);
161
+		$tokenCodes = array_keys($tokenTypes);
162
+		$token      = Tokens::getHighestWeightedToken($tokenCodes);
163
+
164
+		// If we could not get a token.
165
+		if ($token === false) {
166
+			$error = 'Could not determine a token to listen for';
167
+			throw new RuntimeException($error);
168
+		}
169
+
170
+		return $tokenTypes[$token];
171
+
172
+	}//end getListenerTokenPos()
173
+
174
+
175
+	/**
176
+	 * Processes the test.
177
+	 *
178
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
179
+	 *                                               token occurred.
180
+	 * @param int                         $stackPtr  The position in the tokens stack
181
+	 *                                               where the listening token type
182
+	 *                                               was found.
183
+	 *
184
+	 * @return void
185
+	 * @see    register()
186
+	 */
187
+	final public function process(File $phpcsFile, $stackPtr)
188
+	{
189
+		$file = $phpcsFile->getFilename();
190
+		if ($this->currFile !== $file) {
191
+			// We have changed files, so clean up.
192
+			$this->errorPos = [];
193
+			$this->currFile = $file;
194
+		}
195
+
196
+		$tokens = $phpcsFile->getTokens();
197
+
198
+		if (in_array($tokens[$stackPtr]['code'], $this->supplementaryTokens, true) === true) {
199
+			$this->processSupplementary($phpcsFile, $stackPtr);
200
+		}
201
+
202
+		$type = $tokens[$stackPtr]['code'];
203
+
204
+		// If the type is not set, then it must have been a token registered
205
+		// with registerSupplementary().
206
+		if (isset($this->parsedPatterns[$type]) === false) {
207
+			return;
208
+		}
209
+
210
+		$allErrors = [];
211
+
212
+		// Loop over each pattern that is listening to the current token type
213
+		// that we are processing.
214
+		foreach ($this->parsedPatterns[$type] as $patternInfo) {
215
+			// If processPattern returns false, then the pattern that we are
216
+			// checking the code with must not be designed to check that code.
217
+			$errors = $this->processPattern($patternInfo, $phpcsFile, $stackPtr);
218
+			if ($errors === false) {
219
+				// The pattern didn't match.
220
+				continue;
221
+			} else if (empty($errors) === true) {
222
+				// The pattern matched, but there were no errors.
223
+				break;
224
+			}
225
+
226
+			foreach ($errors as $stackPtr => $error) {
227
+				if (isset($this->errorPos[$stackPtr]) === false) {
228
+					$this->errorPos[$stackPtr] = true;
229
+					$allErrors[$stackPtr]      = $error;
230
+				}
231
+			}
232
+		}
233
+
234
+		foreach ($allErrors as $stackPtr => $error) {
235
+			$phpcsFile->addError($error, $stackPtr, 'Found');
236
+		}
237
+
238
+	}//end process()
239
+
240
+
241
+	/**
242
+	 * Processes the pattern and verifies the code at $stackPtr.
243
+	 *
244
+	 * @param array                       $patternInfo Information about the pattern used
245
+	 *                                                 for checking, which includes are
246
+	 *                                                 parsed token representation of the
247
+	 *                                                 pattern.
248
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile   The PHP_CodeSniffer file where the
249
+	 *                                                 token occurred.
250
+	 * @param int                         $stackPtr    The position in the tokens stack where
251
+	 *                                                 the listening token type was found.
252
+	 *
253
+	 * @return array
254
+	 */
255
+	protected function processPattern($patternInfo, File $phpcsFile, $stackPtr)
256
+	{
257
+		$tokens      = $phpcsFile->getTokens();
258
+		$pattern     = $patternInfo['pattern'];
259
+		$patternCode = $patternInfo['pattern_code'];
260
+		$errors      = [];
261
+		$found       = '';
262
+
263
+		$ignoreTokens = [T_WHITESPACE => T_WHITESPACE];
264
+		if ($this->ignoreComments === true) {
265
+			$ignoreTokens += Tokens::$commentTokens;
266
+		}
267
+
268
+		$origStackPtr = $stackPtr;
269
+		$hasError     = false;
270
+
271
+		if ($patternInfo['listen_pos'] > 0) {
272
+			$stackPtr--;
273
+
274
+			for ($i = ($patternInfo['listen_pos'] - 1); $i >= 0; $i--) {
275
+				if ($pattern[$i]['type'] === 'token') {
276
+					if ($pattern[$i]['token'] === T_WHITESPACE) {
277
+						if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
278
+							$found = $tokens[$stackPtr]['content'].$found;
279
+						}
280
+
281
+						// Only check the size of the whitespace if this is not
282
+						// the first token. We don't care about the size of
283
+						// leading whitespace, just that there is some.
284
+						if ($i !== 0) {
285
+							if ($tokens[$stackPtr]['content'] !== $pattern[$i]['value']) {
286
+								$hasError = true;
287
+							}
288
+						}
289
+					} else {
290
+						// Check to see if this important token is the same as the
291
+						// previous important token in the pattern. If it is not,
292
+						// then the pattern cannot be for this piece of code.
293
+						$prev = $phpcsFile->findPrevious(
294
+							$ignoreTokens,
295
+							$stackPtr,
296
+							null,
297
+							true
298
+						);
299
+
300
+						if ($prev === false
301
+							|| $tokens[$prev]['code'] !== $pattern[$i]['token']
302
+						) {
303
+							return false;
304
+						}
305
+
306
+						// If we skipped past some whitespace tokens, then add them
307
+						// to the found string.
308
+						$tokenContent = $phpcsFile->getTokensAsString(
309
+							($prev + 1),
310
+							($stackPtr - $prev - 1)
311
+						);
312
+
313
+						$found = $tokens[$prev]['content'].$tokenContent.$found;
314
+
315
+						if (isset($pattern[($i - 1)]) === true
316
+							&& $pattern[($i - 1)]['type'] === 'skip'
317
+						) {
318
+							$stackPtr = $prev;
319
+						} else {
320
+							$stackPtr = ($prev - 1);
321
+						}
322
+					}//end if
323
+				} else if ($pattern[$i]['type'] === 'skip') {
324
+					// Skip to next piece of relevant code.
325
+					if ($pattern[$i]['to'] === 'parenthesis_closer') {
326
+						$to = 'parenthesis_opener';
327
+					} else {
328
+						$to = 'scope_opener';
329
+					}
330
+
331
+					// Find the previous opener.
332
+					$next = $phpcsFile->findPrevious(
333
+						$ignoreTokens,
334
+						$stackPtr,
335
+						null,
336
+						true
337
+					);
338
+
339
+					if ($next === false || isset($tokens[$next][$to]) === false) {
340
+						// If there was not opener, then we must be
341
+						// using the wrong pattern.
342
+						return false;
343
+					}
344
+
345
+					if ($to === 'parenthesis_opener') {
346
+						$found = '{'.$found;
347
+					} else {
348
+						$found = '('.$found;
349
+					}
350
+
351
+					$found = '...'.$found;
352
+
353
+					// Skip to the opening token.
354
+					$stackPtr = ($tokens[$next][$to] - 1);
355
+				} else if ($pattern[$i]['type'] === 'string') {
356
+					$found = 'abc';
357
+				} else if ($pattern[$i]['type'] === 'newline') {
358
+					if ($this->ignoreComments === true
359
+						&& isset(Tokens::$commentTokens[$tokens[$stackPtr]['code']]) === true
360
+					) {
361
+						$startComment = $phpcsFile->findPrevious(
362
+							Tokens::$commentTokens,
363
+							($stackPtr - 1),
364
+							null,
365
+							true
366
+						);
367
+
368
+						if ($tokens[$startComment]['line'] !== $tokens[($startComment + 1)]['line']) {
369
+							$startComment++;
370
+						}
371
+
372
+						$tokenContent = $phpcsFile->getTokensAsString(
373
+							$startComment,
374
+							($stackPtr - $startComment + 1)
375
+						);
376
+
377
+						$found    = $tokenContent.$found;
378
+						$stackPtr = ($startComment - 1);
379
+					}
380
+
381
+					if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
382
+						if ($tokens[$stackPtr]['content'] !== $phpcsFile->eolChar) {
383
+							$found = $tokens[$stackPtr]['content'].$found;
384
+
385
+							// This may just be an indent that comes after a newline
386
+							// so check the token before to make sure. If it is a newline, we
387
+							// can ignore the error here.
388
+							if (($tokens[($stackPtr - 1)]['content'] !== $phpcsFile->eolChar)
389
+								&& ($this->ignoreComments === true
390
+								&& isset(Tokens::$commentTokens[$tokens[($stackPtr - 1)]['code']]) === false)
391
+							) {
392
+								$hasError = true;
393
+							} else {
394
+								$stackPtr--;
395
+							}
396
+						} else {
397
+							$found = 'EOL'.$found;
398
+						}
399
+					} else {
400
+						$found    = $tokens[$stackPtr]['content'].$found;
401
+						$hasError = true;
402
+					}//end if
403
+
404
+					if ($hasError === false && $pattern[($i - 1)]['type'] !== 'newline') {
405
+						// Make sure they only have 1 newline.
406
+						$prev = $phpcsFile->findPrevious($ignoreTokens, ($stackPtr - 1), null, true);
407
+						if ($prev !== false && $tokens[$prev]['line'] !== $tokens[$stackPtr]['line']) {
408
+							$hasError = true;
409
+						}
410
+					}
411
+				}//end if
412
+			}//end for
413
+		}//end if
414
+
415
+		$stackPtr          = $origStackPtr;
416
+		$lastAddedStackPtr = null;
417
+		$patternLen        = count($pattern);
418
+
419
+		for ($i = $patternInfo['listen_pos']; $i < $patternLen; $i++) {
420
+			if (isset($tokens[$stackPtr]) === false) {
421
+				break;
422
+			}
423
+
424
+			if ($pattern[$i]['type'] === 'token') {
425
+				if ($pattern[$i]['token'] === T_WHITESPACE) {
426
+					if ($this->ignoreComments === true) {
427
+						// If we are ignoring comments, check to see if this current
428
+						// token is a comment. If so skip it.
429
+						if (isset(Tokens::$commentTokens[$tokens[$stackPtr]['code']]) === true) {
430
+							continue;
431
+						}
432
+
433
+						// If the next token is a comment, the we need to skip the
434
+						// current token as we should allow a space before a
435
+						// comment for readability.
436
+						if (isset($tokens[($stackPtr + 1)]) === true
437
+							&& isset(Tokens::$commentTokens[$tokens[($stackPtr + 1)]['code']]) === true
438
+						) {
439
+							continue;
440
+						}
441
+					}
442
+
443
+					$tokenContent = '';
444
+					if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
445
+						if (isset($pattern[($i + 1)]) === false) {
446
+							// This is the last token in the pattern, so just compare
447
+							// the next token of content.
448
+							$tokenContent = $tokens[$stackPtr]['content'];
449
+						} else {
450
+							// Get all the whitespace to the next token.
451
+							$next = $phpcsFile->findNext(
452
+								Tokens::$emptyTokens,
453
+								$stackPtr,
454
+								null,
455
+								true
456
+							);
457
+
458
+							$tokenContent = $phpcsFile->getTokensAsString(
459
+								$stackPtr,
460
+								($next - $stackPtr)
461
+							);
462
+
463
+							$lastAddedStackPtr = $stackPtr;
464
+							$stackPtr          = $next;
465
+						}//end if
466
+
467
+						if ($stackPtr !== $lastAddedStackPtr) {
468
+							$found .= $tokenContent;
469
+						}
470
+					} else {
471
+						if ($stackPtr !== $lastAddedStackPtr) {
472
+							$found            .= $tokens[$stackPtr]['content'];
473
+							$lastAddedStackPtr = $stackPtr;
474
+						}
475
+					}//end if
476
+
477
+					if (isset($pattern[($i + 1)]) === true
478
+						&& $pattern[($i + 1)]['type'] === 'skip'
479
+					) {
480
+						// The next token is a skip token, so we just need to make
481
+						// sure the whitespace we found has *at least* the
482
+						// whitespace required.
483
+						if (strpos($tokenContent, $pattern[$i]['value']) !== 0) {
484
+							$hasError = true;
485
+						}
486
+					} else {
487
+						if ($tokenContent !== $pattern[$i]['value']) {
488
+							$hasError = true;
489
+						}
490
+					}
491
+				} else {
492
+					// Check to see if this important token is the same as the
493
+					// next important token in the pattern. If it is not, then
494
+					// the pattern cannot be for this piece of code.
495
+					$next = $phpcsFile->findNext(
496
+						$ignoreTokens,
497
+						$stackPtr,
498
+						null,
499
+						true
500
+					);
501
+
502
+					if ($next === false
503
+						|| $tokens[$next]['code'] !== $pattern[$i]['token']
504
+					) {
505
+						// The next important token did not match the pattern.
506
+						return false;
507
+					}
508
+
509
+					if ($lastAddedStackPtr !== null) {
510
+						if (($tokens[$next]['code'] === T_OPEN_CURLY_BRACKET
511
+							|| $tokens[$next]['code'] === T_CLOSE_CURLY_BRACKET)
512
+							&& isset($tokens[$next]['scope_condition']) === true
513
+							&& $tokens[$next]['scope_condition'] > $lastAddedStackPtr
514
+						) {
515
+							// This is a brace, but the owner of it is after the current
516
+							// token, which means it does not belong to any token in
517
+							// our pattern. This means the pattern is not for us.
518
+							return false;
519
+						}
520
+
521
+						if (($tokens[$next]['code'] === T_OPEN_PARENTHESIS
522
+							|| $tokens[$next]['code'] === T_CLOSE_PARENTHESIS)
523
+							&& isset($tokens[$next]['parenthesis_owner']) === true
524
+							&& $tokens[$next]['parenthesis_owner'] > $lastAddedStackPtr
525
+						) {
526
+							// This is a bracket, but the owner of it is after the current
527
+							// token, which means it does not belong to any token in
528
+							// our pattern. This means the pattern is not for us.
529
+							return false;
530
+						}
531
+					}//end if
532
+
533
+					// If we skipped past some whitespace tokens, then add them
534
+					// to the found string.
535
+					if (($next - $stackPtr) > 0) {
536
+						$hasComment = false;
537
+						for ($j = $stackPtr; $j < $next; $j++) {
538
+							$found .= $tokens[$j]['content'];
539
+							if (isset(Tokens::$commentTokens[$tokens[$j]['code']]) === true) {
540
+								$hasComment = true;
541
+							}
542
+						}
543
+
544
+						// If we are not ignoring comments, this additional
545
+						// whitespace or comment is not allowed. If we are
546
+						// ignoring comments, there needs to be at least one
547
+						// comment for this to be allowed.
548
+						if ($this->ignoreComments === false
549
+							|| ($this->ignoreComments === true
550
+							&& $hasComment === false)
551
+						) {
552
+							$hasError = true;
553
+						}
554
+
555
+						// Even when ignoring comments, we are not allowed to include
556
+						// newlines without the pattern specifying them, so
557
+						// everything should be on the same line.
558
+						if ($tokens[$next]['line'] !== $tokens[$stackPtr]['line']) {
559
+							$hasError = true;
560
+						}
561
+					}//end if
562
+
563
+					if ($next !== $lastAddedStackPtr) {
564
+						$found            .= $tokens[$next]['content'];
565
+						$lastAddedStackPtr = $next;
566
+					}
567
+
568
+					if (isset($pattern[($i + 1)]) === true
569
+						&& $pattern[($i + 1)]['type'] === 'skip'
570
+					) {
571
+						$stackPtr = $next;
572
+					} else {
573
+						$stackPtr = ($next + 1);
574
+					}
575
+				}//end if
576
+			} else if ($pattern[$i]['type'] === 'skip') {
577
+				if ($pattern[$i]['to'] === 'unknown') {
578
+					$next = $phpcsFile->findNext(
579
+						$pattern[($i + 1)]['token'],
580
+						$stackPtr
581
+					);
582
+
583
+					if ($next === false) {
584
+						// Couldn't find the next token, so we must
585
+						// be using the wrong pattern.
586
+						return false;
587
+					}
588
+
589
+					$found   .= '...';
590
+					$stackPtr = $next;
591
+				} else {
592
+					// Find the previous opener.
593
+					$next = $phpcsFile->findPrevious(
594
+						Tokens::$blockOpeners,
595
+						$stackPtr
596
+					);
597
+
598
+					if ($next === false
599
+						|| isset($tokens[$next][$pattern[$i]['to']]) === false
600
+					) {
601
+						// If there was not opener, then we must
602
+						// be using the wrong pattern.
603
+						return false;
604
+					}
605
+
606
+					$found .= '...';
607
+					if ($pattern[$i]['to'] === 'parenthesis_closer') {
608
+						$found .= ')';
609
+					} else {
610
+						$found .= '}';
611
+					}
612
+
613
+					// Skip to the closing token.
614
+					$stackPtr = ($tokens[$next][$pattern[$i]['to']] + 1);
615
+				}//end if
616
+			} else if ($pattern[$i]['type'] === 'string') {
617
+				if ($tokens[$stackPtr]['code'] !== T_STRING) {
618
+					$hasError = true;
619
+				}
620
+
621
+				if ($stackPtr !== $lastAddedStackPtr) {
622
+					$found            .= 'abc';
623
+					$lastAddedStackPtr = $stackPtr;
624
+				}
625
+
626
+				$stackPtr++;
627
+			} else if ($pattern[$i]['type'] === 'newline') {
628
+				// Find the next token that contains a newline character.
629
+				$newline = 0;
630
+				for ($j = $stackPtr; $j < $phpcsFile->numTokens; $j++) {
631
+					if (strpos($tokens[$j]['content'], $phpcsFile->eolChar) !== false) {
632
+						$newline = $j;
633
+						break;
634
+					}
635
+				}
636
+
637
+				if ($newline === 0) {
638
+					// We didn't find a newline character in the rest of the file.
639
+					$next     = ($phpcsFile->numTokens - 1);
640
+					$hasError = true;
641
+				} else {
642
+					if ($this->ignoreComments === false) {
643
+						// The newline character cannot be part of a comment.
644
+						if (isset(Tokens::$commentTokens[$tokens[$newline]['code']]) === true) {
645
+							$hasError = true;
646
+						}
647
+					}
648
+
649
+					if ($newline === $stackPtr) {
650
+						$next = ($stackPtr + 1);
651
+					} else {
652
+						// Check that there were no significant tokens that we
653
+						// skipped over to find our newline character.
654
+						$next = $phpcsFile->findNext(
655
+							$ignoreTokens,
656
+							$stackPtr,
657
+							null,
658
+							true
659
+						);
660
+
661
+						if ($next < $newline) {
662
+							// We skipped a non-ignored token.
663
+							$hasError = true;
664
+						} else {
665
+							$next = ($newline + 1);
666
+						}
667
+					}
668
+				}//end if
669
+
670
+				if ($stackPtr !== $lastAddedStackPtr) {
671
+					$found .= $phpcsFile->getTokensAsString(
672
+						$stackPtr,
673
+						($next - $stackPtr)
674
+					);
675
+
676
+					$lastAddedStackPtr = ($next - 1);
677
+				}
678
+
679
+				$stackPtr = $next;
680
+			}//end if
681
+		}//end for
682
+
683
+		if ($hasError === true) {
684
+			$error = $this->prepareError($found, $patternCode);
685
+			$errors[$origStackPtr] = $error;
686
+		}
687
+
688
+		return $errors;
689
+
690
+	}//end processPattern()
691
+
692
+
693
+	/**
694
+	 * Prepares an error for the specified patternCode.
695
+	 *
696
+	 * @param string $found       The actual found string in the code.
697
+	 * @param string $patternCode The expected pattern code.
698
+	 *
699
+	 * @return string The error message.
700
+	 */
701
+	protected function prepareError($found, $patternCode)
702
+	{
703
+		$found    = str_replace("\r\n", '\n', $found);
704
+		$found    = str_replace("\n", '\n', $found);
705
+		$found    = str_replace("\r", '\n', $found);
706
+		$found    = str_replace("\t", '\t', $found);
707
+		$found    = str_replace('EOL', '\n', $found);
708
+		$expected = str_replace('EOL', '\n', $patternCode);
709
+
710
+		$error = "Expected \"$expected\"; found \"$found\"";
711
+
712
+		return $error;
713
+
714
+	}//end prepareError()
715
+
716
+
717
+	/**
718
+	 * Returns the patterns that should be checked.
719
+	 *
720
+	 * @return string[]
721
+	 */
722
+	abstract protected function getPatterns();
723
+
724
+
725
+	/**
726
+	 * Registers any supplementary tokens that this test might wish to process.
727
+	 *
728
+	 * A sniff may wish to register supplementary tests when it wishes to group
729
+	 * an arbitrary validation that cannot be performed using a pattern, with
730
+	 * other pattern tests.
731
+	 *
732
+	 * @return int[]
733
+	 * @see    processSupplementary()
734
+	 */
735
+	protected function registerSupplementary()
736
+	{
737
+		return [];
738
+
739
+	}//end registerSupplementary()
740
+
741
+
742
+	 /**
743
+	  * Processes any tokens registered with registerSupplementary().
744
+	  *
745
+	  * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where to
746
+	  *                                               process the skip.
747
+	  * @param int                         $stackPtr  The position in the tokens stack to
748
+	  *                                               process.
749
+	  *
750
+	  * @return void
751
+	  * @see    registerSupplementary()
752
+	  */
753
+	protected function processSupplementary(File $phpcsFile, $stackPtr)
754
+	{
755
+
756
+	}//end processSupplementary()
757
+
758
+
759
+	/**
760
+	 * Parses a pattern string into an array of pattern steps.
761
+	 *
762
+	 * @param string $pattern The pattern to parse.
763
+	 *
764
+	 * @return array The parsed pattern array.
765
+	 * @see    createSkipPattern()
766
+	 * @see    createTokenPattern()
767
+	 */
768
+	private function parse($pattern)
769
+	{
770
+		$patterns   = [];
771
+		$length     = strlen($pattern);
772
+		$lastToken  = 0;
773
+		$firstToken = 0;
774
+
775
+		for ($i = 0; $i < $length; $i++) {
776
+			$specialPattern = false;
777
+			$isLastChar     = ($i === ($length - 1));
778
+			$oldFirstToken  = $firstToken;
779
+
780
+			if (substr($pattern, $i, 3) === '...') {
781
+				// It's a skip pattern. The skip pattern requires the
782
+				// content of the token in the "from" position and the token
783
+				// to skip to.
784
+				$specialPattern = $this->createSkipPattern($pattern, ($i - 1));
785
+				$lastToken      = ($i - $firstToken);
786
+				$firstToken     = ($i + 3);
787
+				$i += 2;
788
+
789
+				if ($specialPattern['to'] !== 'unknown') {
790
+					$firstToken++;
791
+				}
792
+			} else if (substr($pattern, $i, 3) === 'abc') {
793
+				$specialPattern = ['type' => 'string'];
794
+				$lastToken      = ($i - $firstToken);
795
+				$firstToken     = ($i + 3);
796
+				$i += 2;
797
+			} else if (substr($pattern, $i, 3) === 'EOL') {
798
+				$specialPattern = ['type' => 'newline'];
799
+				$lastToken      = ($i - $firstToken);
800
+				$firstToken     = ($i + 3);
801
+				$i += 2;
802
+			}//end if
803
+
804
+			if ($specialPattern !== false || $isLastChar === true) {
805
+				// If we are at the end of the string, don't worry about a limit.
806
+				if ($isLastChar === true) {
807
+					// Get the string from the end of the last skip pattern, if any,
808
+					// to the end of the pattern string.
809
+					$str = substr($pattern, $oldFirstToken);
810
+				} else {
811
+					// Get the string from the end of the last special pattern,
812
+					// if any, to the start of this special pattern.
813
+					if ($lastToken === 0) {
814
+						// Note that if the last special token was zero characters ago,
815
+						// there will be nothing to process so we can skip this bit.
816
+						// This happens if you have something like: EOL... in your pattern.
817
+						$str = '';
818
+					} else {
819
+						$str = substr($pattern, $oldFirstToken, $lastToken);
820
+					}
821
+				}
822
+
823
+				if ($str !== '') {
824
+					$tokenPatterns = $this->createTokenPattern($str);
825
+					foreach ($tokenPatterns as $tokenPattern) {
826
+						$patterns[] = $tokenPattern;
827
+					}
828
+				}
829
+
830
+				// Make sure we don't skip the last token.
831
+				if ($isLastChar === false && $i === ($length - 1)) {
832
+					$i--;
833
+				}
834
+			}//end if
835
+
836
+			// Add the skip pattern *after* we have processed
837
+			// all the tokens from the end of the last skip pattern
838
+			// to the start of this skip pattern.
839
+			if ($specialPattern !== false) {
840
+				$patterns[] = $specialPattern;
841
+			}
842
+		}//end for
843
+
844
+		return $patterns;
845
+
846
+	}//end parse()
847
+
848
+
849
+	/**
850
+	 * Creates a skip pattern.
851
+	 *
852
+	 * @param string $pattern The pattern being parsed.
853
+	 * @param string $from    The token content that the skip pattern starts from.
854
+	 *
855
+	 * @return array The pattern step.
856
+	 * @see    createTokenPattern()
857
+	 * @see    parse()
858
+	 */
859
+	private function createSkipPattern($pattern, $from)
860
+	{
861
+		$skip = ['type' => 'skip'];
862
+
863
+		$nestedParenthesis = 0;
864
+		$nestedBraces      = 0;
865
+		for ($start = $from; $start >= 0; $start--) {
866
+			switch ($pattern[$start]) {
867
+			case '(':
868
+				if ($nestedParenthesis === 0) {
869
+					$skip['to'] = 'parenthesis_closer';
870
+				}
871
+
872
+				$nestedParenthesis--;
873
+				break;
874
+			case '{':
875
+				if ($nestedBraces === 0) {
876
+					$skip['to'] = 'scope_closer';
877
+				}
878
+
879
+				$nestedBraces--;
880
+				break;
881
+			case '}':
882
+				$nestedBraces++;
883
+				break;
884
+			case ')':
885
+				$nestedParenthesis++;
886
+				break;
887
+			}//end switch
888
+
889
+			if (isset($skip['to']) === true) {
890
+				break;
891
+			}
892
+		}//end for
893
+
894
+		if (isset($skip['to']) === false) {
895
+			$skip['to'] = 'unknown';
896
+		}
897
+
898
+		return $skip;
899
+
900
+	}//end createSkipPattern()
901
+
902
+
903
+	/**
904
+	 * Creates a token pattern.
905
+	 *
906
+	 * @param string $str The tokens string that the pattern should match.
907
+	 *
908
+	 * @return array The pattern step.
909
+	 * @see    createSkipPattern()
910
+	 * @see    parse()
911
+	 */
912
+	private function createTokenPattern($str)
913
+	{
914
+		// Don't add a space after the closing php tag as it will add a new
915
+		// whitespace token.
916
+		$tokenizer = new PHP('<?php '.$str.'?>', null);
917
+
918
+		// Remove the <?php tag from the front and the end php tag from the back.
919
+		$tokens = $tokenizer->getTokens();
920
+		$tokens = array_slice($tokens, 1, (count($tokens) - 2));
921
+
922
+		$patterns = [];
923
+		foreach ($tokens as $patternInfo) {
924
+			$patterns[] = [
925
+				'type'  => 'token',
926
+				'token' => $patternInfo['code'],
927
+				'value' => $patternInfo['content'],
928
+			];
929
+		}
930
+
931
+		return $patterns;
932
+
933
+	}//end createTokenPattern()
934 934
 
935 935
 
936 936
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Sniffs/AbstractArraySniff.php 1 patch
Indentation   +213 added lines, -213 removed lines patch added patch discarded remove patch
@@ -16,219 +16,219 @@
 block discarded – undo
16 16
 {
17 17
 
18 18
 
19
-    /**
20
-     * Returns an array of tokens this test wants to listen for.
21
-     *
22
-     * @return array
23
-     */
24
-    final public function register()
25
-    {
26
-        return [
27
-            T_ARRAY,
28
-            T_OPEN_SHORT_ARRAY,
29
-        ];
30
-
31
-    }//end register()
32
-
33
-
34
-    /**
35
-     * Processes this sniff, when one of its tokens is encountered.
36
-     *
37
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked.
38
-     * @param int                         $stackPtr  The position of the current token in
39
-     *                                               the stack passed in $tokens.
40
-     *
41
-     * @return void
42
-     */
43
-    public function process(File $phpcsFile, $stackPtr)
44
-    {
45
-        $tokens = $phpcsFile->getTokens();
46
-
47
-        if ($tokens[$stackPtr]['code'] === T_ARRAY) {
48
-            $phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'no');
49
-
50
-            $arrayStart = $tokens[$stackPtr]['parenthesis_opener'];
51
-            if (isset($tokens[$arrayStart]['parenthesis_closer']) === false) {
52
-                // Incomplete array.
53
-                return;
54
-            }
55
-
56
-            $arrayEnd = $tokens[$arrayStart]['parenthesis_closer'];
57
-        } else {
58
-            $phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'yes');
59
-            $arrayStart = $stackPtr;
60
-            $arrayEnd   = $tokens[$stackPtr]['bracket_closer'];
61
-        }
62
-
63
-        $lastContent = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($arrayEnd - 1), null, true);
64
-        if ($tokens[$lastContent]['code'] === T_COMMA) {
65
-            // Last array item ends with a comma.
66
-            $phpcsFile->recordMetric($stackPtr, 'Array end comma', 'yes');
67
-            $lastArrayToken = $lastContent;
68
-        } else {
69
-            $phpcsFile->recordMetric($stackPtr, 'Array end comma', 'no');
70
-            $lastArrayToken = $arrayEnd;
71
-        }
72
-
73
-        if ($tokens[$stackPtr]['code'] === T_ARRAY) {
74
-            $lastToken = $tokens[$stackPtr]['parenthesis_opener'];
75
-        } else {
76
-            $lastToken = $stackPtr;
77
-        }
78
-
79
-        $keyUsed = false;
80
-        $indices = [];
81
-
82
-        for ($checkToken = ($stackPtr + 1); $checkToken <= $lastArrayToken; $checkToken++) {
83
-            // Skip bracketed statements, like function calls.
84
-            if ($tokens[$checkToken]['code'] === T_OPEN_PARENTHESIS
85
-                && (isset($tokens[$checkToken]['parenthesis_owner']) === false
86
-                || $tokens[$checkToken]['parenthesis_owner'] !== $stackPtr)
87
-            ) {
88
-                $checkToken = $tokens[$checkToken]['parenthesis_closer'];
89
-                continue;
90
-            }
91
-
92
-            if ($tokens[$checkToken]['code'] === T_ARRAY
93
-                || $tokens[$checkToken]['code'] === T_OPEN_SHORT_ARRAY
94
-                || $tokens[$checkToken]['code'] === T_CLOSURE
95
-            ) {
96
-                // Let subsequent calls of this test handle nested arrays.
97
-                if ($tokens[$lastToken]['code'] !== T_DOUBLE_ARROW) {
98
-                    $indices[] = ['value_start' => $checkToken];
99
-                    $lastToken = $checkToken;
100
-                }
101
-
102
-                if ($tokens[$checkToken]['code'] === T_ARRAY) {
103
-                    $checkToken = $tokens[$tokens[$checkToken]['parenthesis_opener']]['parenthesis_closer'];
104
-                } else if ($tokens[$checkToken]['code'] === T_OPEN_SHORT_ARRAY) {
105
-                    $checkToken = $tokens[$checkToken]['bracket_closer'];
106
-                } else {
107
-                    // T_CLOSURE.
108
-                    $checkToken = $tokens[$checkToken]['scope_closer'];
109
-                }
110
-
111
-                $checkToken = $phpcsFile->findNext(T_WHITESPACE, ($checkToken + 1), null, true);
112
-                $lastToken  = $checkToken;
113
-                if ($tokens[$checkToken]['code'] !== T_COMMA) {
114
-                    $checkToken--;
115
-                }
116
-
117
-                continue;
118
-            }//end if
119
-
120
-            if ($tokens[$checkToken]['code'] !== T_DOUBLE_ARROW
121
-                && $tokens[$checkToken]['code'] !== T_COMMA
122
-                && $checkToken !== $arrayEnd
123
-            ) {
124
-                continue;
125
-            }
126
-
127
-            if ($tokens[$checkToken]['code'] === T_COMMA
128
-                || $checkToken === $arrayEnd
129
-            ) {
130
-                $stackPtrCount = 0;
131
-                if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
132
-                    $stackPtrCount = count($tokens[$stackPtr]['nested_parenthesis']);
133
-                }
134
-
135
-                $commaCount = 0;
136
-                if (isset($tokens[$checkToken]['nested_parenthesis']) === true) {
137
-                    $commaCount = count($tokens[$checkToken]['nested_parenthesis']);
138
-                    if ($tokens[$stackPtr]['code'] === T_ARRAY) {
139
-                        // Remove parenthesis that are used to define the array.
140
-                        $commaCount--;
141
-                    }
142
-                }
143
-
144
-                if ($commaCount > $stackPtrCount) {
145
-                    // This comma is inside more parenthesis than the ARRAY keyword,
146
-                    // so it is actually a comma used to do things like
147
-                    // separate arguments in a function call.
148
-                    continue;
149
-                }
150
-
151
-                if ($keyUsed === false) {
152
-                    $valueContent = $phpcsFile->findNext(
153
-                        Tokens::$emptyTokens,
154
-                        ($lastToken + 1),
155
-                        $checkToken,
156
-                        true
157
-                    );
158
-
159
-                    $indices[] = ['value_start' => $valueContent];
160
-                }
161
-
162
-                $lastToken = $checkToken;
163
-                $keyUsed   = false;
164
-                continue;
165
-            }//end if
166
-
167
-            if ($tokens[$checkToken]['code'] === T_DOUBLE_ARROW) {
168
-                $keyUsed = true;
169
-
170
-                // Find the start of index that uses this double arrow.
171
-                $indexEnd   = $phpcsFile->findPrevious(T_WHITESPACE, ($checkToken - 1), $arrayStart, true);
172
-                $indexStart = $phpcsFile->findStartOfStatement($indexEnd);
173
-
174
-                // Find the value of this index.
175
-                $nextContent = $phpcsFile->findNext(
176
-                    Tokens::$emptyTokens,
177
-                    ($checkToken + 1),
178
-                    $arrayEnd,
179
-                    true
180
-                );
181
-
182
-                $indices[] = [
183
-                    'index_start' => $indexStart,
184
-                    'index_end'   => $indexEnd,
185
-                    'arrow'       => $checkToken,
186
-                    'value_start' => $nextContent,
187
-                ];
188
-
189
-                $lastToken = $checkToken;
190
-            }//end if
191
-        }//end for
192
-
193
-        if ($tokens[$arrayStart]['line'] === $tokens[$arrayEnd]['line']) {
194
-            $this->processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
195
-        } else {
196
-            $this->processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
197
-        }
198
-
199
-    }//end process()
200
-
201
-
202
-    /**
203
-     * Processes a single-line array definition.
204
-     *
205
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile  The current file being checked.
206
-     * @param int                         $stackPtr   The position of the current token
207
-     *                                                in the stack passed in $tokens.
208
-     * @param int                         $arrayStart The token that starts the array definition.
209
-     * @param int                         $arrayEnd   The token that ends the array definition.
210
-     * @param array                       $indices    An array of token positions for the array keys,
211
-     *                                                double arrows, and values.
212
-     *
213
-     * @return void
214
-     */
215
-    abstract protected function processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
216
-
217
-
218
-    /**
219
-     * Processes a multi-line array definition.
220
-     *
221
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile  The current file being checked.
222
-     * @param int                         $stackPtr   The position of the current token
223
-     *                                                in the stack passed in $tokens.
224
-     * @param int                         $arrayStart The token that starts the array definition.
225
-     * @param int                         $arrayEnd   The token that ends the array definition.
226
-     * @param array                       $indices    An array of token positions for the array keys,
227
-     *                                                double arrows, and values.
228
-     *
229
-     * @return void
230
-     */
231
-    abstract protected function processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
19
+	/**
20
+	 * Returns an array of tokens this test wants to listen for.
21
+	 *
22
+	 * @return array
23
+	 */
24
+	final public function register()
25
+	{
26
+		return [
27
+			T_ARRAY,
28
+			T_OPEN_SHORT_ARRAY,
29
+		];
30
+
31
+	}//end register()
32
+
33
+
34
+	/**
35
+	 * Processes this sniff, when one of its tokens is encountered.
36
+	 *
37
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked.
38
+	 * @param int                         $stackPtr  The position of the current token in
39
+	 *                                               the stack passed in $tokens.
40
+	 *
41
+	 * @return void
42
+	 */
43
+	public function process(File $phpcsFile, $stackPtr)
44
+	{
45
+		$tokens = $phpcsFile->getTokens();
46
+
47
+		if ($tokens[$stackPtr]['code'] === T_ARRAY) {
48
+			$phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'no');
49
+
50
+			$arrayStart = $tokens[$stackPtr]['parenthesis_opener'];
51
+			if (isset($tokens[$arrayStart]['parenthesis_closer']) === false) {
52
+				// Incomplete array.
53
+				return;
54
+			}
55
+
56
+			$arrayEnd = $tokens[$arrayStart]['parenthesis_closer'];
57
+		} else {
58
+			$phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'yes');
59
+			$arrayStart = $stackPtr;
60
+			$arrayEnd   = $tokens[$stackPtr]['bracket_closer'];
61
+		}
62
+
63
+		$lastContent = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($arrayEnd - 1), null, true);
64
+		if ($tokens[$lastContent]['code'] === T_COMMA) {
65
+			// Last array item ends with a comma.
66
+			$phpcsFile->recordMetric($stackPtr, 'Array end comma', 'yes');
67
+			$lastArrayToken = $lastContent;
68
+		} else {
69
+			$phpcsFile->recordMetric($stackPtr, 'Array end comma', 'no');
70
+			$lastArrayToken = $arrayEnd;
71
+		}
72
+
73
+		if ($tokens[$stackPtr]['code'] === T_ARRAY) {
74
+			$lastToken = $tokens[$stackPtr]['parenthesis_opener'];
75
+		} else {
76
+			$lastToken = $stackPtr;
77
+		}
78
+
79
+		$keyUsed = false;
80
+		$indices = [];
81
+
82
+		for ($checkToken = ($stackPtr + 1); $checkToken <= $lastArrayToken; $checkToken++) {
83
+			// Skip bracketed statements, like function calls.
84
+			if ($tokens[$checkToken]['code'] === T_OPEN_PARENTHESIS
85
+				&& (isset($tokens[$checkToken]['parenthesis_owner']) === false
86
+				|| $tokens[$checkToken]['parenthesis_owner'] !== $stackPtr)
87
+			) {
88
+				$checkToken = $tokens[$checkToken]['parenthesis_closer'];
89
+				continue;
90
+			}
91
+
92
+			if ($tokens[$checkToken]['code'] === T_ARRAY
93
+				|| $tokens[$checkToken]['code'] === T_OPEN_SHORT_ARRAY
94
+				|| $tokens[$checkToken]['code'] === T_CLOSURE
95
+			) {
96
+				// Let subsequent calls of this test handle nested arrays.
97
+				if ($tokens[$lastToken]['code'] !== T_DOUBLE_ARROW) {
98
+					$indices[] = ['value_start' => $checkToken];
99
+					$lastToken = $checkToken;
100
+				}
101
+
102
+				if ($tokens[$checkToken]['code'] === T_ARRAY) {
103
+					$checkToken = $tokens[$tokens[$checkToken]['parenthesis_opener']]['parenthesis_closer'];
104
+				} else if ($tokens[$checkToken]['code'] === T_OPEN_SHORT_ARRAY) {
105
+					$checkToken = $tokens[$checkToken]['bracket_closer'];
106
+				} else {
107
+					// T_CLOSURE.
108
+					$checkToken = $tokens[$checkToken]['scope_closer'];
109
+				}
110
+
111
+				$checkToken = $phpcsFile->findNext(T_WHITESPACE, ($checkToken + 1), null, true);
112
+				$lastToken  = $checkToken;
113
+				if ($tokens[$checkToken]['code'] !== T_COMMA) {
114
+					$checkToken--;
115
+				}
116
+
117
+				continue;
118
+			}//end if
119
+
120
+			if ($tokens[$checkToken]['code'] !== T_DOUBLE_ARROW
121
+				&& $tokens[$checkToken]['code'] !== T_COMMA
122
+				&& $checkToken !== $arrayEnd
123
+			) {
124
+				continue;
125
+			}
126
+
127
+			if ($tokens[$checkToken]['code'] === T_COMMA
128
+				|| $checkToken === $arrayEnd
129
+			) {
130
+				$stackPtrCount = 0;
131
+				if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
132
+					$stackPtrCount = count($tokens[$stackPtr]['nested_parenthesis']);
133
+				}
134
+
135
+				$commaCount = 0;
136
+				if (isset($tokens[$checkToken]['nested_parenthesis']) === true) {
137
+					$commaCount = count($tokens[$checkToken]['nested_parenthesis']);
138
+					if ($tokens[$stackPtr]['code'] === T_ARRAY) {
139
+						// Remove parenthesis that are used to define the array.
140
+						$commaCount--;
141
+					}
142
+				}
143
+
144
+				if ($commaCount > $stackPtrCount) {
145
+					// This comma is inside more parenthesis than the ARRAY keyword,
146
+					// so it is actually a comma used to do things like
147
+					// separate arguments in a function call.
148
+					continue;
149
+				}
150
+
151
+				if ($keyUsed === false) {
152
+					$valueContent = $phpcsFile->findNext(
153
+						Tokens::$emptyTokens,
154
+						($lastToken + 1),
155
+						$checkToken,
156
+						true
157
+					);
158
+
159
+					$indices[] = ['value_start' => $valueContent];
160
+				}
161
+
162
+				$lastToken = $checkToken;
163
+				$keyUsed   = false;
164
+				continue;
165
+			}//end if
166
+
167
+			if ($tokens[$checkToken]['code'] === T_DOUBLE_ARROW) {
168
+				$keyUsed = true;
169
+
170
+				// Find the start of index that uses this double arrow.
171
+				$indexEnd   = $phpcsFile->findPrevious(T_WHITESPACE, ($checkToken - 1), $arrayStart, true);
172
+				$indexStart = $phpcsFile->findStartOfStatement($indexEnd);
173
+
174
+				// Find the value of this index.
175
+				$nextContent = $phpcsFile->findNext(
176
+					Tokens::$emptyTokens,
177
+					($checkToken + 1),
178
+					$arrayEnd,
179
+					true
180
+				);
181
+
182
+				$indices[] = [
183
+					'index_start' => $indexStart,
184
+					'index_end'   => $indexEnd,
185
+					'arrow'       => $checkToken,
186
+					'value_start' => $nextContent,
187
+				];
188
+
189
+				$lastToken = $checkToken;
190
+			}//end if
191
+		}//end for
192
+
193
+		if ($tokens[$arrayStart]['line'] === $tokens[$arrayEnd]['line']) {
194
+			$this->processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
195
+		} else {
196
+			$this->processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
197
+		}
198
+
199
+	}//end process()
200
+
201
+
202
+	/**
203
+	 * Processes a single-line array definition.
204
+	 *
205
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile  The current file being checked.
206
+	 * @param int                         $stackPtr   The position of the current token
207
+	 *                                                in the stack passed in $tokens.
208
+	 * @param int                         $arrayStart The token that starts the array definition.
209
+	 * @param int                         $arrayEnd   The token that ends the array definition.
210
+	 * @param array                       $indices    An array of token positions for the array keys,
211
+	 *                                                double arrows, and values.
212
+	 *
213
+	 * @return void
214
+	 */
215
+	abstract protected function processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
216
+
217
+
218
+	/**
219
+	 * Processes a multi-line array definition.
220
+	 *
221
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile  The current file being checked.
222
+	 * @param int                         $stackPtr   The position of the current token
223
+	 *                                                in the stack passed in $tokens.
224
+	 * @param int                         $arrayStart The token that starts the array definition.
225
+	 * @param int                         $arrayEnd   The token that ends the array definition.
226
+	 * @param array                       $indices    An array of token positions for the array keys,
227
+	 *                                                double arrows, and values.
228
+	 *
229
+	 * @return void
230
+	 */
231
+	abstract protected function processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
232 232
 
233 233
 
234 234
 }//end class
Please login to merge, or discard this patch.
vendor/squizlabs/php_codesniffer/src/Sniffs/AbstractVariableSniff.php 1 patch
Indentation   +204 added lines, -204 removed lines patch added patch discarded remove patch
@@ -22,210 +22,210 @@
 block discarded – undo
22 22
 {
23 23
 
24 24
 
25
-    /**
26
-     * List of PHP Reserved variables.
27
-     *
28
-     * Used by various naming convention sniffs.
29
-     *
30
-     * @var array
31
-     */
32
-    protected $phpReservedVars = [
33
-        '_SERVER'              => true,
34
-        '_GET'                 => true,
35
-        '_POST'                => true,
36
-        '_REQUEST'             => true,
37
-        '_SESSION'             => true,
38
-        '_ENV'                 => true,
39
-        '_COOKIE'              => true,
40
-        '_FILES'               => true,
41
-        'GLOBALS'              => true,
42
-        'http_response_header' => true,
43
-        'HTTP_RAW_POST_DATA'   => true,
44
-        'php_errormsg'         => true,
45
-    ];
46
-
47
-
48
-    /**
49
-     * Constructs an AbstractVariableTest.
50
-     */
51
-    public function __construct()
52
-    {
53
-        $scopes = Tokens::$ooScopeTokens;
54
-
55
-        $listen = [
56
-            T_VARIABLE,
57
-            T_DOUBLE_QUOTED_STRING,
58
-            T_HEREDOC,
59
-        ];
60
-
61
-        parent::__construct($scopes, $listen, true);
62
-
63
-    }//end __construct()
64
-
65
-
66
-    /**
67
-     * Processes the token in the specified PHP_CodeSniffer\Files\File.
68
-     *
69
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
70
-     *                                               token was found.
71
-     * @param int                         $stackPtr  The position where the token was found.
72
-     * @param int                         $currScope The current scope opener token.
73
-     *
74
-     * @return void|int Optionally returns a stack pointer. The sniff will not be
75
-     *                  called again on the current file until the returned stack
76
-     *                  pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
77
-     *                  the rest of the file.
78
-     */
79
-    final protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
80
-    {
81
-        $tokens = $phpcsFile->getTokens();
82
-
83
-        if ($tokens[$stackPtr]['code'] === T_DOUBLE_QUOTED_STRING
84
-            || $tokens[$stackPtr]['code'] === T_HEREDOC
85
-        ) {
86
-            // Check to see if this string has a variable in it.
87
-            $pattern = '|(?<!\\\\)(?:\\\\{2})*\${?[a-zA-Z0-9_]+}?|';
88
-            if (preg_match($pattern, $tokens[$stackPtr]['content']) !== 0) {
89
-                return $this->processVariableInString($phpcsFile, $stackPtr);
90
-            }
91
-
92
-            return;
93
-        }
94
-
95
-        // If this token is nested inside a function at a deeper
96
-        // level than the current OO scope that was found, it's a normal
97
-        // variable and not a member var.
98
-        $conditions = array_reverse($tokens[$stackPtr]['conditions'], true);
99
-        $inFunction = false;
100
-        foreach ($conditions as $scope => $code) {
101
-            if (isset(Tokens::$ooScopeTokens[$code]) === true) {
102
-                break;
103
-            }
104
-
105
-            if ($code === T_FUNCTION || $code === T_CLOSURE) {
106
-                $inFunction = true;
107
-            }
108
-        }
109
-
110
-        if ($scope !== $currScope) {
111
-            // We found a closer scope to this token, so ignore
112
-            // this particular time through the sniff. We will process
113
-            // this token when this closer scope is found to avoid
114
-            // duplicate checks.
115
-            return;
116
-        }
117
-
118
-        // Just make sure this isn't a variable in a function declaration.
119
-        if ($inFunction === false && isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
120
-            foreach ($tokens[$stackPtr]['nested_parenthesis'] as $opener => $closer) {
121
-                if (isset($tokens[$opener]['parenthesis_owner']) === false) {
122
-                    // Check if this is a USE statement for a closure.
123
-                    $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), null, true);
124
-                    if ($tokens[$prev]['code'] === T_USE) {
125
-                        $inFunction = true;
126
-                        break;
127
-                    }
128
-
129
-                    continue;
130
-                }
131
-
132
-                $owner = $tokens[$opener]['parenthesis_owner'];
133
-                if ($tokens[$owner]['code'] === T_FUNCTION
134
-                    || $tokens[$owner]['code'] === T_CLOSURE
135
-                ) {
136
-                    $inFunction = true;
137
-                    break;
138
-                }
139
-            }
140
-        }//end if
141
-
142
-        if ($inFunction === true) {
143
-            return $this->processVariable($phpcsFile, $stackPtr);
144
-        } else {
145
-            return $this->processMemberVar($phpcsFile, $stackPtr);
146
-        }
147
-
148
-    }//end processTokenWithinScope()
149
-
150
-
151
-    /**
152
-     * Processes the token outside the scope in the file.
153
-     *
154
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
155
-     *                                               token was found.
156
-     * @param int                         $stackPtr  The position where the token was found.
157
-     *
158
-     * @return void|int Optionally returns a stack pointer. The sniff will not be
159
-     *                  called again on the current file until the returned stack
160
-     *                  pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
161
-     *                  the rest of the file.
162
-     */
163
-    final protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
164
-    {
165
-        $tokens = $phpcsFile->getTokens();
166
-        // These variables are not member vars.
167
-        if ($tokens[$stackPtr]['code'] === T_VARIABLE) {
168
-            return $this->processVariable($phpcsFile, $stackPtr);
169
-        } else if ($tokens[$stackPtr]['code'] === T_DOUBLE_QUOTED_STRING
170
-            || $tokens[$stackPtr]['code'] === T_HEREDOC
171
-        ) {
172
-            // Check to see if this string has a variable in it.
173
-            $pattern = '|(?<!\\\\)(?:\\\\{2})*\${?[a-zA-Z0-9_]+}?|';
174
-            if (preg_match($pattern, $tokens[$stackPtr]['content']) !== 0) {
175
-                return $this->processVariableInString($phpcsFile, $stackPtr);
176
-            }
177
-        }
178
-
179
-    }//end processTokenOutsideScope()
180
-
181
-
182
-    /**
183
-     * Called to process class member vars.
184
-     *
185
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
186
-     *                                               token was found.
187
-     * @param int                         $stackPtr  The position where the token was found.
188
-     *
189
-     * @return void|int Optionally returns a stack pointer. The sniff will not be
190
-     *                  called again on the current file until the returned stack
191
-     *                  pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
192
-     *                  the rest of the file.
193
-     */
194
-    abstract protected function processMemberVar(File $phpcsFile, $stackPtr);
195
-
196
-
197
-    /**
198
-     * Called to process normal member vars.
199
-     *
200
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
201
-     *                                               token was found.
202
-     * @param int                         $stackPtr  The position where the token was found.
203
-     *
204
-     * @return void|int Optionally returns a stack pointer. The sniff will not be
205
-     *                  called again on the current file until the returned stack
206
-     *                  pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
207
-     *                  the rest of the file.
208
-     */
209
-    abstract protected function processVariable(File $phpcsFile, $stackPtr);
210
-
211
-
212
-    /**
213
-     * Called to process variables found in double quoted strings or heredocs.
214
-     *
215
-     * Note that there may be more than one variable in the string, which will
216
-     * result only in one call for the string or one call per line for heredocs.
217
-     *
218
-     * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
219
-     *                                               token was found.
220
-     * @param int                         $stackPtr  The position where the double quoted
221
-     *                                               string was found.
222
-     *
223
-     * @return void|int Optionally returns a stack pointer. The sniff will not be
224
-     *                  called again on the current file until the returned stack
225
-     *                  pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
226
-     *                  the rest of the file.
227
-     */
228
-    abstract protected function processVariableInString(File $phpcsFile, $stackPtr);
25
+	/**
26
+	 * List of PHP Reserved variables.
27
+	 *
28
+	 * Used by various naming convention sniffs.
29
+	 *
30
+	 * @var array
31
+	 */
32
+	protected $phpReservedVars = [
33
+		'_SERVER'              => true,
34
+		'_GET'                 => true,
35
+		'_POST'                => true,
36
+		'_REQUEST'             => true,
37
+		'_SESSION'             => true,
38
+		'_ENV'                 => true,
39
+		'_COOKIE'              => true,
40
+		'_FILES'               => true,
41
+		'GLOBALS'              => true,
42
+		'http_response_header' => true,
43
+		'HTTP_RAW_POST_DATA'   => true,
44
+		'php_errormsg'         => true,
45
+	];
46
+
47
+
48
+	/**
49
+	 * Constructs an AbstractVariableTest.
50
+	 */
51
+	public function __construct()
52
+	{
53
+		$scopes = Tokens::$ooScopeTokens;
54
+
55
+		$listen = [
56
+			T_VARIABLE,
57
+			T_DOUBLE_QUOTED_STRING,
58
+			T_HEREDOC,
59
+		];
60
+
61
+		parent::__construct($scopes, $listen, true);
62
+
63
+	}//end __construct()
64
+
65
+
66
+	/**
67
+	 * Processes the token in the specified PHP_CodeSniffer\Files\File.
68
+	 *
69
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
70
+	 *                                               token was found.
71
+	 * @param int                         $stackPtr  The position where the token was found.
72
+	 * @param int                         $currScope The current scope opener token.
73
+	 *
74
+	 * @return void|int Optionally returns a stack pointer. The sniff will not be
75
+	 *                  called again on the current file until the returned stack
76
+	 *                  pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
77
+	 *                  the rest of the file.
78
+	 */
79
+	final protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
80
+	{
81
+		$tokens = $phpcsFile->getTokens();
82
+
83
+		if ($tokens[$stackPtr]['code'] === T_DOUBLE_QUOTED_STRING
84
+			|| $tokens[$stackPtr]['code'] === T_HEREDOC
85
+		) {
86
+			// Check to see if this string has a variable in it.
87
+			$pattern = '|(?<!\\\\)(?:\\\\{2})*\${?[a-zA-Z0-9_]+}?|';
88
+			if (preg_match($pattern, $tokens[$stackPtr]['content']) !== 0) {
89
+				return $this->processVariableInString($phpcsFile, $stackPtr);
90
+			}
91
+
92
+			return;
93
+		}
94
+
95
+		// If this token is nested inside a function at a deeper
96
+		// level than the current OO scope that was found, it's a normal
97
+		// variable and not a member var.
98
+		$conditions = array_reverse($tokens[$stackPtr]['conditions'], true);
99
+		$inFunction = false;
100
+		foreach ($conditions as $scope => $code) {
101
+			if (isset(Tokens::$ooScopeTokens[$code]) === true) {
102
+				break;
103
+			}
104
+
105
+			if ($code === T_FUNCTION || $code === T_CLOSURE) {
106
+				$inFunction = true;
107
+			}
108
+		}
109
+
110
+		if ($scope !== $currScope) {
111
+			// We found a closer scope to this token, so ignore
112
+			// this particular time through the sniff. We will process
113
+			// this token when this closer scope is found to avoid
114
+			// duplicate checks.
115
+			return;
116
+		}
117
+
118
+		// Just make sure this isn't a variable in a function declaration.
119
+		if ($inFunction === false && isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
120
+			foreach ($tokens[$stackPtr]['nested_parenthesis'] as $opener => $closer) {
121
+				if (isset($tokens[$opener]['parenthesis_owner']) === false) {
122
+					// Check if this is a USE statement for a closure.
123
+					$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), null, true);
124
+					if ($tokens[$prev]['code'] === T_USE) {
125
+						$inFunction = true;
126
+						break;
127
+					}
128
+
129
+					continue;
130
+				}
131
+
132
+				$owner = $tokens[$opener]['parenthesis_owner'];
133
+				if ($tokens[$owner]['code'] === T_FUNCTION
134
+					|| $tokens[$owner]['code'] === T_CLOSURE
135
+				) {
136
+					$inFunction = true;
137
+					break;
138
+				}
139
+			}
140
+		}//end if
141
+
142
+		if ($inFunction === true) {
143
+			return $this->processVariable($phpcsFile, $stackPtr);
144
+		} else {
145
+			return $this->processMemberVar($phpcsFile, $stackPtr);
146
+		}
147
+
148
+	}//end processTokenWithinScope()
149
+
150
+
151
+	/**
152
+	 * Processes the token outside the scope in the file.
153
+	 *
154
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
155
+	 *                                               token was found.
156
+	 * @param int                         $stackPtr  The position where the token was found.
157
+	 *
158
+	 * @return void|int Optionally returns a stack pointer. The sniff will not be
159
+	 *                  called again on the current file until the returned stack
160
+	 *                  pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
161
+	 *                  the rest of the file.
162
+	 */
163
+	final protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
164
+	{
165
+		$tokens = $phpcsFile->getTokens();
166
+		// These variables are not member vars.
167
+		if ($tokens[$stackPtr]['code'] === T_VARIABLE) {
168
+			return $this->processVariable($phpcsFile, $stackPtr);
169
+		} else if ($tokens[$stackPtr]['code'] === T_DOUBLE_QUOTED_STRING
170
+			|| $tokens[$stackPtr]['code'] === T_HEREDOC
171
+		) {
172
+			// Check to see if this string has a variable in it.
173
+			$pattern = '|(?<!\\\\)(?:\\\\{2})*\${?[a-zA-Z0-9_]+}?|';
174
+			if (preg_match($pattern, $tokens[$stackPtr]['content']) !== 0) {
175
+				return $this->processVariableInString($phpcsFile, $stackPtr);
176
+			}
177
+		}
178
+
179
+	}//end processTokenOutsideScope()
180
+
181
+
182
+	/**
183
+	 * Called to process class member vars.
184
+	 *
185
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
186
+	 *                                               token was found.
187
+	 * @param int                         $stackPtr  The position where the token was found.
188
+	 *
189
+	 * @return void|int Optionally returns a stack pointer. The sniff will not be
190
+	 *                  called again on the current file until the returned stack
191
+	 *                  pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
192
+	 *                  the rest of the file.
193
+	 */
194
+	abstract protected function processMemberVar(File $phpcsFile, $stackPtr);
195
+
196
+
197
+	/**
198
+	 * Called to process normal member vars.
199
+	 *
200
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
201
+	 *                                               token was found.
202
+	 * @param int                         $stackPtr  The position where the token was found.
203
+	 *
204
+	 * @return void|int Optionally returns a stack pointer. The sniff will not be
205
+	 *                  called again on the current file until the returned stack
206
+	 *                  pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
207
+	 *                  the rest of the file.
208
+	 */
209
+	abstract protected function processVariable(File $phpcsFile, $stackPtr);
210
+
211
+
212
+	/**
213
+	 * Called to process variables found in double quoted strings or heredocs.
214
+	 *
215
+	 * Note that there may be more than one variable in the string, which will
216
+	 * result only in one call for the string or one call per line for heredocs.
217
+	 *
218
+	 * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
219
+	 *                                               token was found.
220
+	 * @param int                         $stackPtr  The position where the double quoted
221
+	 *                                               string was found.
222
+	 *
223
+	 * @return void|int Optionally returns a stack pointer. The sniff will not be
224
+	 *                  called again on the current file until the returned stack
225
+	 *                  pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
226
+	 *                  the rest of the file.
227
+	 */
228
+	abstract protected function processVariableInString(File $phpcsFile, $stackPtr);
229 229
 
230 230
 
231 231
 }//end class
Please login to merge, or discard this patch.