comparison vendor/squizlabs/php_codesniffer/src/Reports/Code.php @ 17:129ea1e6d783

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:21:36 +0000
parents
children
comparison
equal deleted inserted replaced
16:c2387f117808 17:129ea1e6d783
1 <?php
2 /**
3 * Full report for PHP_CodeSniffer.
4 *
5 * @author Greg Sherwood <gsherwood@squiz.net>
6 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
7 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8 */
9
10 namespace PHP_CodeSniffer\Reports;
11
12 use PHP_CodeSniffer\Files\File;
13 use PHP_CodeSniffer\Util;
14
15 class Code implements Report
16 {
17
18
19 /**
20 * Generate a partial report for a single processed file.
21 *
22 * Function should return TRUE if it printed or stored data about the file
23 * and FALSE if it ignored the file. Returning TRUE indicates that the file and
24 * its data should be counted in the grand totals.
25 *
26 * @param array $report Prepared report data.
27 * @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
28 * @param bool $showSources Show sources?
29 * @param int $width Maximum allowed line width.
30 *
31 * @return bool
32 */
33 public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
34 {
35 if ($report['errors'] === 0 && $report['warnings'] === 0) {
36 // Nothing to print.
37 return false;
38 }
39
40 // How many lines to show about and below the error line.
41 $surroundingLines = 2;
42
43 $file = $report['filename'];
44 $tokens = $phpcsFile->getTokens();
45 if (empty($tokens) === true) {
46 if (PHP_CODESNIFFER_VERBOSITY === 1) {
47 $startTime = microtime(true);
48 echo 'CODE report is parsing '.basename($file).' ';
49 } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
50 echo "CODE report is forcing parse of $file".PHP_EOL;
51 }
52
53 try {
54 $phpcsFile->parse();
55 } catch (\Exception $e) {
56 // This is a second parse, so ignore exceptions.
57 // They would have been added to the file's error list already.
58 }
59
60 if (PHP_CODESNIFFER_VERBOSITY === 1) {
61 $timeTaken = ((microtime(true) - $startTime) * 1000);
62 if ($timeTaken < 1000) {
63 $timeTaken = round($timeTaken);
64 echo "DONE in {$timeTaken}ms";
65 } else {
66 $timeTaken = round(($timeTaken / 1000), 2);
67 echo "DONE in $timeTaken secs";
68 }
69
70 echo PHP_EOL;
71 }
72
73 $tokens = $phpcsFile->getTokens();
74 }//end if
75
76 // Create an array that maps lines to the first token on the line.
77 $lineTokens = [];
78 $lastLine = 0;
79 $stackPtr = 0;
80 foreach ($tokens as $stackPtr => $token) {
81 if ($token['line'] !== $lastLine) {
82 if ($lastLine > 0) {
83 $lineTokens[$lastLine]['end'] = ($stackPtr - 1);
84 }
85
86 $lastLine++;
87 $lineTokens[$lastLine] = [
88 'start' => $stackPtr,
89 'end' => null,
90 ];
91 }
92 }
93
94 // Make sure the last token in the file sits on an imaginary
95 // last line so it is easier to generate code snippets at the
96 // end of the file.
97 $lineTokens[$lastLine]['end'] = $stackPtr;
98
99 // Determine the longest code line we will be showing.
100 $maxSnippetLength = 0;
101 $eolLen = strlen($phpcsFile->eolChar);
102 foreach ($report['messages'] as $line => $lineErrors) {
103 $startLine = max(($line - $surroundingLines), 1);
104 $endLine = min(($line + $surroundingLines), $lastLine);
105
106 $maxLineNumLength = strlen($endLine);
107
108 for ($i = $startLine; $i <= $endLine; $i++) {
109 if ($i === 1) {
110 continue;
111 }
112
113 $lineLength = ($tokens[($lineTokens[$i]['start'] - 1)]['column'] + $tokens[($lineTokens[$i]['start'] - 1)]['length'] - $eolLen);
114 $maxSnippetLength = max($lineLength, $maxSnippetLength);
115 }
116 }
117
118 $maxSnippetLength += ($maxLineNumLength + 8);
119
120 // Determine the longest error message we will be showing.
121 $maxErrorLength = 0;
122 foreach ($report['messages'] as $line => $lineErrors) {
123 foreach ($lineErrors as $column => $colErrors) {
124 foreach ($colErrors as $error) {
125 $length = strlen($error['message']);
126 if ($showSources === true) {
127 $length += (strlen($error['source']) + 3);
128 }
129
130 $maxErrorLength = max($maxErrorLength, ($length + 1));
131 }
132 }
133 }
134
135 // The padding that all lines will require that are printing an error message overflow.
136 if ($report['warnings'] > 0) {
137 $typeLength = 7;
138 } else {
139 $typeLength = 5;
140 }
141
142 $errorPadding = str_repeat(' ', ($maxLineNumLength + 7));
143 $errorPadding .= str_repeat(' ', $typeLength);
144 $errorPadding .= ' ';
145 if ($report['fixable'] > 0) {
146 $errorPadding .= ' ';
147 }
148
149 $errorPaddingLength = strlen($errorPadding);
150
151 // The maximum amount of space an error message can use.
152 $maxErrorSpace = ($width - $errorPaddingLength);
153 if ($showSources === true) {
154 // Account for the chars used to print colors.
155 $maxErrorSpace += 8;
156 }
157
158 // Figure out the max report width we need and can use.
159 $fileLength = strlen($file);
160 $maxWidth = max(($fileLength + 6), ($maxErrorLength + $errorPaddingLength));
161 $width = max(min($width, $maxWidth), $maxSnippetLength);
162 if ($width < 70) {
163 $width = 70;
164 }
165
166 // Print the file header.
167 echo PHP_EOL."\033[1mFILE: ";
168 if ($fileLength <= ($width - 6)) {
169 echo $file;
170 } else {
171 echo '...'.substr($file, ($fileLength - ($width - 6)));
172 }
173
174 echo "\033[0m".PHP_EOL;
175 echo str_repeat('-', $width).PHP_EOL;
176
177 echo "\033[1m".'FOUND '.$report['errors'].' ERROR';
178 if ($report['errors'] !== 1) {
179 echo 'S';
180 }
181
182 if ($report['warnings'] > 0) {
183 echo ' AND '.$report['warnings'].' WARNING';
184 if ($report['warnings'] !== 1) {
185 echo 'S';
186 }
187 }
188
189 echo ' AFFECTING '.count($report['messages']).' LINE';
190 if (count($report['messages']) !== 1) {
191 echo 'S';
192 }
193
194 echo "\033[0m".PHP_EOL;
195
196 foreach ($report['messages'] as $line => $lineErrors) {
197 $startLine = max(($line - $surroundingLines), 1);
198 $endLine = min(($line + $surroundingLines), $lastLine);
199
200 $snippet = '';
201 if (isset($lineTokens[$startLine]) === true) {
202 for ($i = $lineTokens[$startLine]['start']; $i <= $lineTokens[$endLine]['end']; $i++) {
203 $snippetLine = $tokens[$i]['line'];
204 if ($lineTokens[$snippetLine]['start'] === $i) {
205 // Starting a new line.
206 if ($snippetLine === $line) {
207 $snippet .= "\033[1m".'>> ';
208 } else {
209 $snippet .= ' ';
210 }
211
212 $snippet .= str_repeat(' ', ($maxLineNumLength - strlen($snippetLine)));
213 $snippet .= $snippetLine.': ';
214 if ($snippetLine === $line) {
215 $snippet .= "\033[0m";
216 }
217 }
218
219 if (isset($tokens[$i]['orig_content']) === true) {
220 $tokenContent = $tokens[$i]['orig_content'];
221 } else {
222 $tokenContent = $tokens[$i]['content'];
223 }
224
225 if (strpos($tokenContent, "\t") !== false) {
226 $token = $tokens[$i];
227 $token['content'] = $tokenContent;
228 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
229 $tab = "\000";
230 } else {
231 $tab = "\033[30;1m»\033[0m";
232 }
233
234 $phpcsFile->tokenizer->replaceTabsInToken($token, $tab, "\000");
235 $tokenContent = $token['content'];
236 }
237
238 $tokenContent = Util\Common::prepareForOutput($tokenContent, ["\r", "\n", "\t"]);
239 $tokenContent = str_replace("\000", ' ', $tokenContent);
240
241 $underline = false;
242 if ($snippetLine === $line && isset($lineErrors[$tokens[$i]['column']]) === true) {
243 $underline = true;
244 }
245
246 // Underline invisible characters as well.
247 if ($underline === true && trim($tokenContent) === '') {
248 $snippet .= "\033[4m".' '."\033[0m".$tokenContent;
249 } else {
250 if ($underline === true) {
251 $snippet .= "\033[4m";
252 }
253
254 $snippet .= $tokenContent;
255
256 if ($underline === true) {
257 $snippet .= "\033[0m";
258 }
259 }
260 }//end for
261 }//end if
262
263 echo str_repeat('-', $width).PHP_EOL;
264
265 foreach ($lineErrors as $column => $colErrors) {
266 foreach ($colErrors as $error) {
267 $padding = ($maxLineNumLength - strlen($line));
268 echo 'LINE '.str_repeat(' ', $padding).$line.': ';
269
270 if ($error['type'] === 'ERROR') {
271 echo "\033[31mERROR\033[0m";
272 if ($report['warnings'] > 0) {
273 echo ' ';
274 }
275 } else {
276 echo "\033[33mWARNING\033[0m";
277 }
278
279 echo ' ';
280 if ($report['fixable'] > 0) {
281 echo '[';
282 if ($error['fixable'] === true) {
283 echo 'x';
284 } else {
285 echo ' ';
286 }
287
288 echo '] ';
289 }
290
291 $message = $error['message'];
292 $message = str_replace("\n", "\n".$errorPadding, $message);
293 if ($showSources === true) {
294 $message = "\033[1m".$message."\033[0m".' ('.$error['source'].')';
295 }
296
297 $errorMsg = wordwrap(
298 $message,
299 $maxErrorSpace,
300 PHP_EOL.$errorPadding
301 );
302
303 echo $errorMsg.PHP_EOL;
304 }//end foreach
305 }//end foreach
306
307 echo str_repeat('-', $width).PHP_EOL;
308 echo rtrim($snippet).PHP_EOL;
309 }//end foreach
310
311 echo str_repeat('-', $width).PHP_EOL;
312 if ($report['fixable'] > 0) {
313 echo "\033[1m".'PHPCBF CAN FIX THE '.$report['fixable'].' MARKED SNIFF VIOLATIONS AUTOMATICALLY'."\033[0m".PHP_EOL;
314 echo str_repeat('-', $width).PHP_EOL;
315 }
316
317 return true;
318
319 }//end generateFileReport()
320
321
322 /**
323 * Prints all errors and warnings for each file processed.
324 *
325 * @param string $cachedData Any partial report data that was returned from
326 * generateFileReport during the run.
327 * @param int $totalFiles Total number of files processed during the run.
328 * @param int $totalErrors Total number of errors found during the run.
329 * @param int $totalWarnings Total number of warnings found during the run.
330 * @param int $totalFixable Total number of problems that can be fixed.
331 * @param bool $showSources Show sources?
332 * @param int $width Maximum allowed line width.
333 * @param bool $interactive Are we running in interactive mode?
334 * @param bool $toScreen Is the report being printed to screen?
335 *
336 * @return void
337 */
338 public function generate(
339 $cachedData,
340 $totalFiles,
341 $totalErrors,
342 $totalWarnings,
343 $totalFixable,
344 $showSources=false,
345 $width=80,
346 $interactive=false,
347 $toScreen=true
348 ) {
349 if ($cachedData === '') {
350 return;
351 }
352
353 echo $cachedData;
354
355 if ($toScreen === true && $interactive === false) {
356 Util\Timing::printRunTime();
357 }
358
359 }//end generate()
360
361
362 }//end class