comparison vendor/squizlabs/php_codesniffer/src/Reporter.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 af1871eacc83
comparison
equal deleted inserted replaced
16:c2387f117808 17:129ea1e6d783
1 <?php
2 /**
3 * Manages reporting of errors and warnings.
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;
11
12 use PHP_CodeSniffer\Reports\Report;
13 use PHP_CodeSniffer\Files\File;
14 use PHP_CodeSniffer\Exceptions\RuntimeException;
15 use PHP_CodeSniffer\Exceptions\DeepExitException;
16 use PHP_CodeSniffer\Util\Common;
17
18 class Reporter
19 {
20
21 /**
22 * The config data for the run.
23 *
24 * @var \PHP_CodeSniffer\Config
25 */
26 public $config = null;
27
28 /**
29 * Total number of files that contain errors or warnings.
30 *
31 * @var integer
32 */
33 public $totalFiles = 0;
34
35 /**
36 * Total number of errors found during the run.
37 *
38 * @var integer
39 */
40 public $totalErrors = 0;
41
42 /**
43 * Total number of warnings found during the run.
44 *
45 * @var integer
46 */
47 public $totalWarnings = 0;
48
49 /**
50 * Total number of errors/warnings that can be fixed.
51 *
52 * @var integer
53 */
54 public $totalFixable = 0;
55
56 /**
57 * Total number of errors/warnings that were fixed.
58 *
59 * @var integer
60 */
61 public $totalFixed = 0;
62
63 /**
64 * When the PHPCS run started.
65 *
66 * @var float
67 */
68 public static $startTime = 0;
69
70 /**
71 * A cache of report objects.
72 *
73 * @var array
74 */
75 private $reports = [];
76
77 /**
78 * A cache of opened temporary files.
79 *
80 * @var array
81 */
82 private $tmpFiles = [];
83
84
85 /**
86 * Initialise the reporter.
87 *
88 * All reports specified in the config will be created and their
89 * output file (or a temp file if none is specified) initialised by
90 * clearing the current contents.
91 *
92 * @param \PHP_CodeSniffer\Config $config The config data for the run.
93 *
94 * @return void
95 * @throws RuntimeException If a report is not available.
96 */
97 public function __construct(Config $config)
98 {
99 $this->config = $config;
100
101 foreach ($config->reports as $type => $output) {
102 if ($output === null) {
103 $output = $config->reportFile;
104 }
105
106 $reportClassName = '';
107 if (strpos($type, '.') !== false) {
108 // This is a path to a custom report class.
109 $filename = realpath($type);
110 if ($filename === false) {
111 $error = "ERROR: Custom report \"$type\" not found".PHP_EOL;
112 throw new DeepExitException($error, 3);
113 }
114
115 $reportClassName = Autoload::loadFile($filename);
116 } else if (class_exists('PHP_CodeSniffer\Reports\\'.ucfirst($type)) === true) {
117 // PHPCS native report.
118 $reportClassName = 'PHP_CodeSniffer\Reports\\'.ucfirst($type);
119 } else if (class_exists($type) === true) {
120 // FQN of a custom report.
121 $reportClassName = $type;
122 } else {
123 // OK, so not a FQN, try and find the report using the registered namespaces.
124 $registeredNamespaces = Autoload::getSearchPaths();
125 $trimmedType = ltrim($type, '\\');
126
127 foreach ($registeredNamespaces as $nsPrefix) {
128 if ($nsPrefix === '') {
129 continue;
130 }
131
132 if (class_exists($nsPrefix.'\\'.$trimmedType) === true) {
133 $reportClassName = $nsPrefix.'\\'.$trimmedType;
134 break;
135 }
136 }
137 }//end if
138
139 if ($reportClassName === '') {
140 $error = "ERROR: Class file for report \"$type\" not found".PHP_EOL;
141 throw new DeepExitException($error, 3);
142 }
143
144 $reportClass = new $reportClassName();
145 if (false === ($reportClass instanceof Report)) {
146 throw new RuntimeException('Class "'.$reportClassName.'" must implement the "PHP_CodeSniffer\Report" interface.');
147 }
148
149 $this->reports[$type] = [
150 'output' => $output,
151 'class' => $reportClass,
152 ];
153
154 if ($output === null) {
155 // Using a temp file.
156 // This needs to be set in the constructor so that all
157 // child procs use the same report file when running in parallel.
158 $this->tmpFiles[$type] = tempnam(sys_get_temp_dir(), 'phpcs');
159 file_put_contents($this->tmpFiles[$type], '');
160 } else {
161 file_put_contents($output, '');
162 }
163 }//end foreach
164
165 }//end __construct()
166
167
168 /**
169 * Generates and prints final versions of all reports.
170 *
171 * Returns TRUE if any of the reports output content to the screen
172 * or FALSE if all reports were silently printed to a file.
173 *
174 * @return bool
175 */
176 public function printReports()
177 {
178 $toScreen = false;
179 foreach ($this->reports as $type => $report) {
180 if ($report['output'] === null) {
181 $toScreen = true;
182 }
183
184 $this->printReport($type);
185 }
186
187 return $toScreen;
188
189 }//end printReports()
190
191
192 /**
193 * Generates and prints a single final report.
194 *
195 * @param string $report The report type to print.
196 *
197 * @return void
198 */
199 public function printReport($report)
200 {
201 $reportClass = $this->reports[$report]['class'];
202 $reportFile = $this->reports[$report]['output'];
203
204 if ($reportFile !== null) {
205 $filename = $reportFile;
206 $toScreen = false;
207 } else {
208 if (isset($this->tmpFiles[$report]) === true) {
209 $filename = $this->tmpFiles[$report];
210 } else {
211 $filename = null;
212 }
213
214 $toScreen = true;
215 }
216
217 $reportCache = '';
218 if ($filename !== null) {
219 $reportCache = file_get_contents($filename);
220 }
221
222 ob_start();
223 $reportClass->generate(
224 $reportCache,
225 $this->totalFiles,
226 $this->totalErrors,
227 $this->totalWarnings,
228 $this->totalFixable,
229 $this->config->showSources,
230 $this->config->reportWidth,
231 $this->config->interactive,
232 $toScreen
233 );
234 $generatedReport = ob_get_contents();
235 ob_end_clean();
236
237 if ($this->config->colors !== true || $reportFile !== null) {
238 $generatedReport = preg_replace('`\033\[[0-9;]+m`', '', $generatedReport);
239 }
240
241 if ($reportFile !== null) {
242 if (PHP_CODESNIFFER_VERBOSITY > 0) {
243 echo $generatedReport;
244 }
245
246 file_put_contents($reportFile, $generatedReport.PHP_EOL);
247 } else {
248 echo $generatedReport;
249 if ($filename !== null && file_exists($filename) === true) {
250 unlink($filename);
251 unset($this->tmpFiles[$report]);
252 }
253 }
254
255 }//end printReport()
256
257
258 /**
259 * Caches the result of a single processed file for all reports.
260 *
261 * The report content that is generated is appended to the output file
262 * assigned to each report. This content may be an intermediate report format
263 * and not reflect the final report output.
264 *
265 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file that has been processed.
266 *
267 * @return void
268 */
269 public function cacheFileReport(File $phpcsFile)
270 {
271 if (isset($this->config->reports) === false) {
272 // This happens during unit testing, or any time someone just wants
273 // the error data and not the printed report.
274 return;
275 }
276
277 $reportData = $this->prepareFileReport($phpcsFile);
278 $errorsShown = false;
279
280 foreach ($this->reports as $type => $report) {
281 $reportClass = $report['class'];
282
283 ob_start();
284 $result = $reportClass->generateFileReport($reportData, $phpcsFile, $this->config->showSources, $this->config->reportWidth);
285 if ($result === true) {
286 $errorsShown = true;
287 }
288
289 $generatedReport = ob_get_contents();
290 ob_end_clean();
291
292 if ($report['output'] === null) {
293 // Using a temp file.
294 if (isset($this->tmpFiles[$type]) === false) {
295 // When running in interactive mode, the reporter prints the full
296 // report many times, which will unlink the temp file. So we need
297 // to create a new one if it doesn't exist.
298 $this->tmpFiles[$type] = tempnam(sys_get_temp_dir(), 'phpcs');
299 file_put_contents($this->tmpFiles[$type], '');
300 }
301
302 file_put_contents($this->tmpFiles[$type], $generatedReport, (FILE_APPEND | LOCK_EX));
303 } else {
304 file_put_contents($report['output'], $generatedReport, (FILE_APPEND | LOCK_EX));
305 }//end if
306 }//end foreach
307
308 if ($errorsShown === true || PHP_CODESNIFFER_CBF === true) {
309 $this->totalFiles++;
310 $this->totalErrors += $reportData['errors'];
311 $this->totalWarnings += $reportData['warnings'];
312
313 // When PHPCBF is running, we need to use the fixable error values
314 // after the report has run and fixed what it can.
315 if (PHP_CODESNIFFER_CBF === true) {
316 $this->totalFixable += $phpcsFile->getFixableCount();
317 $this->totalFixed += $phpcsFile->getFixedCount();
318 } else {
319 $this->totalFixable += $reportData['fixable'];
320 }
321 }
322
323 }//end cacheFileReport()
324
325
326 /**
327 * Generate summary information to be used during report generation.
328 *
329 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file that has been processed.
330 *
331 * @return array
332 */
333 public function prepareFileReport(File $phpcsFile)
334 {
335 $report = [
336 'filename' => Common::stripBasepath($phpcsFile->getFilename(), $this->config->basepath),
337 'errors' => $phpcsFile->getErrorCount(),
338 'warnings' => $phpcsFile->getWarningCount(),
339 'fixable' => $phpcsFile->getFixableCount(),
340 'messages' => [],
341 ];
342
343 if ($report['errors'] === 0 && $report['warnings'] === 0) {
344 // Prefect score!
345 return $report;
346 }
347
348 if ($this->config->recordErrors === false) {
349 $message = 'Errors are not being recorded but this report requires error messages. ';
350 $message .= 'This report will not show the correct information.';
351 $report['messages'][1][1] = [
352 [
353 'message' => $message,
354 'source' => 'Internal.RecordErrors',
355 'severity' => 5,
356 'fixable' => false,
357 'type' => 'ERROR',
358 ],
359 ];
360 return $report;
361 }
362
363 $errors = [];
364
365 // Merge errors and warnings.
366 foreach ($phpcsFile->getErrors() as $line => $lineErrors) {
367 foreach ($lineErrors as $column => $colErrors) {
368 $newErrors = [];
369 foreach ($colErrors as $data) {
370 $newErrors[] = [
371 'message' => $data['message'],
372 'source' => $data['source'],
373 'severity' => $data['severity'],
374 'fixable' => $data['fixable'],
375 'type' => 'ERROR',
376 ];
377 }
378
379 $errors[$line][$column] = $newErrors;
380 }
381
382 ksort($errors[$line]);
383 }//end foreach
384
385 foreach ($phpcsFile->getWarnings() as $line => $lineWarnings) {
386 foreach ($lineWarnings as $column => $colWarnings) {
387 $newWarnings = [];
388 foreach ($colWarnings as $data) {
389 $newWarnings[] = [
390 'message' => $data['message'],
391 'source' => $data['source'],
392 'severity' => $data['severity'],
393 'fixable' => $data['fixable'],
394 'type' => 'WARNING',
395 ];
396 }
397
398 if (isset($errors[$line]) === false) {
399 $errors[$line] = [];
400 }
401
402 if (isset($errors[$line][$column]) === true) {
403 $errors[$line][$column] = array_merge(
404 $newWarnings,
405 $errors[$line][$column]
406 );
407 } else {
408 $errors[$line][$column] = $newWarnings;
409 }
410 }//end foreach
411
412 ksort($errors[$line]);
413 }//end foreach
414
415 ksort($errors);
416 $report['messages'] = $errors;
417 return $report;
418
419 }//end prepareFileReport()
420
421
422 }//end class