Mercurial > hg > cmmr2012-drupal-site
comparison vendor/squizlabs/php_codesniffer/src/Runner.php @ 4:a9cd425dd02b
Update, including to Drupal core 8.6.10
author | Chris Cannam |
---|---|
date | Thu, 28 Feb 2019 13:11:55 +0000 |
parents | |
children | 12f9dff5fda9 |
comparison
equal
deleted
inserted
replaced
3:307d7a7fd348 | 4:a9cd425dd02b |
---|---|
1 <?php | |
2 /** | |
3 * Responsible for running PHPCS and PHPCBF. | |
4 * | |
5 * After creating an object of this class, you probably just want to | |
6 * call runPHPCS() or runPHPCBF(). | |
7 * | |
8 * @author Greg Sherwood <gsherwood@squiz.net> | |
9 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) | |
10 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence | |
11 */ | |
12 | |
13 namespace PHP_CodeSniffer; | |
14 | |
15 use PHP_CodeSniffer\Files\FileList; | |
16 use PHP_CodeSniffer\Files\File; | |
17 use PHP_CodeSniffer\Files\DummyFile; | |
18 use PHP_CodeSniffer\Util\Cache; | |
19 use PHP_CodeSniffer\Util\Common; | |
20 use PHP_CodeSniffer\Util\Standards; | |
21 use PHP_CodeSniffer\Exceptions\RuntimeException; | |
22 use PHP_CodeSniffer\Exceptions\DeepExitException; | |
23 | |
24 class Runner | |
25 { | |
26 | |
27 /** | |
28 * The config data for the run. | |
29 * | |
30 * @var \PHP_CodeSniffer\Config | |
31 */ | |
32 public $config = null; | |
33 | |
34 /** | |
35 * The ruleset used for the run. | |
36 * | |
37 * @var \PHP_CodeSniffer\Ruleset | |
38 */ | |
39 public $ruleset = null; | |
40 | |
41 /** | |
42 * The reporter used for generating reports after the run. | |
43 * | |
44 * @var \PHP_CodeSniffer\Reporter | |
45 */ | |
46 public $reporter = null; | |
47 | |
48 | |
49 /** | |
50 * Run the PHPCS script. | |
51 * | |
52 * @return array | |
53 */ | |
54 public function runPHPCS() | |
55 { | |
56 try { | |
57 Util\Timing::startTiming(); | |
58 Runner::checkRequirements(); | |
59 | |
60 if (defined('PHP_CODESNIFFER_CBF') === false) { | |
61 define('PHP_CODESNIFFER_CBF', false); | |
62 } | |
63 | |
64 // Creating the Config object populates it with all required settings | |
65 // based on the CLI arguments provided to the script and any config | |
66 // values the user has set. | |
67 $this->config = new Config(); | |
68 | |
69 // Init the run and load the rulesets to set additional config vars. | |
70 $this->init(); | |
71 | |
72 // Print a list of sniffs in each of the supplied standards. | |
73 // We fudge the config here so that each standard is explained in isolation. | |
74 if ($this->config->explain === true) { | |
75 $standards = $this->config->standards; | |
76 foreach ($standards as $standard) { | |
77 $this->config->standards = [$standard]; | |
78 $ruleset = new Ruleset($this->config); | |
79 $ruleset->explain(); | |
80 } | |
81 | |
82 return 0; | |
83 } | |
84 | |
85 // Generate documentation for each of the supplied standards. | |
86 if ($this->config->generator !== null) { | |
87 $standards = $this->config->standards; | |
88 foreach ($standards as $standard) { | |
89 $this->config->standards = [$standard]; | |
90 $ruleset = new Ruleset($this->config); | |
91 $class = 'PHP_CodeSniffer\Generators\\'.$this->config->generator; | |
92 $generator = new $class($ruleset); | |
93 $generator->generate(); | |
94 } | |
95 | |
96 return 0; | |
97 } | |
98 | |
99 // Other report formats don't really make sense in interactive mode | |
100 // so we hard-code the full report here and when outputting. | |
101 // We also ensure parallel processing is off because we need to do one file at a time. | |
102 if ($this->config->interactive === true) { | |
103 $this->config->reports = ['full' => null]; | |
104 $this->config->parallel = 1; | |
105 $this->config->showProgress = false; | |
106 } | |
107 | |
108 // Disable caching if we are processing STDIN as we can't be 100% | |
109 // sure where the file came from or if it will change in the future. | |
110 if ($this->config->stdin === true) { | |
111 $this->config->cache = false; | |
112 } | |
113 | |
114 $numErrors = $this->run(); | |
115 | |
116 // Print all the reports for this run. | |
117 $toScreen = $this->reporter->printReports(); | |
118 | |
119 // Only print timer output if no reports were | |
120 // printed to the screen so we don't put additional output | |
121 // in something like an XML report. If we are printing to screen, | |
122 // the report types would have already worked out who should | |
123 // print the timer info. | |
124 if ($this->config->interactive === false | |
125 && ($toScreen === false | |
126 || (($this->reporter->totalErrors + $this->reporter->totalWarnings) === 0 && $this->config->showProgress === true)) | |
127 ) { | |
128 Util\Timing::printRunTime(); | |
129 } | |
130 } catch (DeepExitException $e) { | |
131 echo $e->getMessage(); | |
132 return $e->getCode(); | |
133 }//end try | |
134 | |
135 if ($numErrors === 0) { | |
136 // No errors found. | |
137 return 0; | |
138 } else if ($this->reporter->totalFixable === 0) { | |
139 // Errors found, but none of them can be fixed by PHPCBF. | |
140 return 1; | |
141 } else { | |
142 // Errors found, and some can be fixed by PHPCBF. | |
143 return 2; | |
144 } | |
145 | |
146 }//end runPHPCS() | |
147 | |
148 | |
149 /** | |
150 * Run the PHPCBF script. | |
151 * | |
152 * @return array | |
153 */ | |
154 public function runPHPCBF() | |
155 { | |
156 if (defined('PHP_CODESNIFFER_CBF') === false) { | |
157 define('PHP_CODESNIFFER_CBF', true); | |
158 } | |
159 | |
160 try { | |
161 Util\Timing::startTiming(); | |
162 Runner::checkRequirements(); | |
163 | |
164 // Creating the Config object populates it with all required settings | |
165 // based on the CLI arguments provided to the script and any config | |
166 // values the user has set. | |
167 $this->config = new Config(); | |
168 | |
169 // When processing STDIN, we can't output anything to the screen | |
170 // or it will end up mixed in with the file output. | |
171 if ($this->config->stdin === true) { | |
172 $this->config->verbosity = 0; | |
173 } | |
174 | |
175 // Init the run and load the rulesets to set additional config vars. | |
176 $this->init(); | |
177 | |
178 // When processing STDIN, we only process one file at a time and | |
179 // we don't process all the way through, so we can't use the parallel | |
180 // running system. | |
181 if ($this->config->stdin === true) { | |
182 $this->config->parallel = 1; | |
183 } | |
184 | |
185 // Override some of the command line settings that might break the fixes. | |
186 $this->config->generator = null; | |
187 $this->config->explain = false; | |
188 $this->config->interactive = false; | |
189 $this->config->cache = false; | |
190 $this->config->showSources = false; | |
191 $this->config->recordErrors = false; | |
192 $this->config->reportFile = null; | |
193 $this->config->reports = ['cbf' => null]; | |
194 | |
195 // If a standard tries to set command line arguments itself, some | |
196 // may be blocked because PHPCBF is running, so stop the script | |
197 // dying if any are found. | |
198 $this->config->dieOnUnknownArg = false; | |
199 | |
200 $this->run(); | |
201 $this->reporter->printReports(); | |
202 | |
203 echo PHP_EOL; | |
204 Util\Timing::printRunTime(); | |
205 } catch (DeepExitException $e) { | |
206 echo $e->getMessage(); | |
207 return $e->getCode(); | |
208 }//end try | |
209 | |
210 if ($this->reporter->totalFixed === 0) { | |
211 // Nothing was fixed by PHPCBF. | |
212 if ($this->reporter->totalFixable === 0) { | |
213 // Nothing found that could be fixed. | |
214 return 0; | |
215 } else { | |
216 // Something failed to fix. | |
217 return 2; | |
218 } | |
219 } | |
220 | |
221 if ($this->reporter->totalFixable === 0) { | |
222 // PHPCBF fixed all fixable errors. | |
223 return 1; | |
224 } | |
225 | |
226 // PHPCBF fixed some fixable errors, but others failed to fix. | |
227 return 2; | |
228 | |
229 }//end runPHPCBF() | |
230 | |
231 | |
232 /** | |
233 * Exits if the minimum requirements of PHP_CodSniffer are not met. | |
234 * | |
235 * @return array | |
236 */ | |
237 public function checkRequirements() | |
238 { | |
239 // Check the PHP version. | |
240 if (PHP_VERSION_ID < 50400) { | |
241 $error = 'ERROR: PHP_CodeSniffer requires PHP version 5.4.0 or greater.'.PHP_EOL; | |
242 throw new DeepExitException($error, 3); | |
243 } | |
244 | |
245 if (extension_loaded('tokenizer') === false) { | |
246 $error = 'ERROR: PHP_CodeSniffer requires the tokenizer extension to be enabled.'.PHP_EOL; | |
247 throw new DeepExitException($error, 3); | |
248 } | |
249 | |
250 }//end checkRequirements() | |
251 | |
252 | |
253 /** | |
254 * Init the rulesets and other high-level settings. | |
255 * | |
256 * @return void | |
257 */ | |
258 public function init() | |
259 { | |
260 if (defined('PHP_CODESNIFFER_CBF') === false) { | |
261 define('PHP_CODESNIFFER_CBF', false); | |
262 } | |
263 | |
264 // Ensure this option is enabled or else line endings will not always | |
265 // be detected properly for files created on a Mac with the /r line ending. | |
266 ini_set('auto_detect_line_endings', true); | |
267 | |
268 // Check that the standards are valid. | |
269 foreach ($this->config->standards as $standard) { | |
270 if (Util\Standards::isInstalledStandard($standard) === false) { | |
271 // They didn't select a valid coding standard, so help them | |
272 // out by letting them know which standards are installed. | |
273 $error = 'ERROR: the "'.$standard.'" coding standard is not installed. '; | |
274 ob_start(); | |
275 Util\Standards::printInstalledStandards(); | |
276 $error .= ob_get_contents(); | |
277 ob_end_clean(); | |
278 throw new DeepExitException($error, 3); | |
279 } | |
280 } | |
281 | |
282 // Saves passing the Config object into other objects that only need | |
283 // the verbostity flag for deubg output. | |
284 if (defined('PHP_CODESNIFFER_VERBOSITY') === false) { | |
285 define('PHP_CODESNIFFER_VERBOSITY', $this->config->verbosity); | |
286 } | |
287 | |
288 // Create this class so it is autoloaded and sets up a bunch | |
289 // of PHP_CodeSniffer-specific token type constants. | |
290 $tokens = new Util\Tokens(); | |
291 | |
292 // Allow autoloading of custom files inside installed standards. | |
293 $installedStandards = Standards::getInstalledStandardDetails(); | |
294 foreach ($installedStandards as $name => $details) { | |
295 Autoload::addSearchPath($details['path'], $details['namespace']); | |
296 } | |
297 | |
298 // The ruleset contains all the information about how the files | |
299 // should be checked and/or fixed. | |
300 try { | |
301 $this->ruleset = new Ruleset($this->config); | |
302 } catch (RuntimeException $e) { | |
303 $error = 'ERROR: '.$e->getMessage().PHP_EOL.PHP_EOL; | |
304 $error .= $this->config->printShortUsage(true); | |
305 throw new DeepExitException($error, 3); | |
306 } | |
307 | |
308 }//end init() | |
309 | |
310 | |
311 /** | |
312 * Performs the run. | |
313 * | |
314 * @return int The number of errors and warnings found. | |
315 */ | |
316 private function run() | |
317 { | |
318 // The class that manages all reporters for the run. | |
319 $this->reporter = new Reporter($this->config); | |
320 | |
321 // Include bootstrap files. | |
322 foreach ($this->config->bootstrap as $bootstrap) { | |
323 include $bootstrap; | |
324 } | |
325 | |
326 if ($this->config->stdin === true) { | |
327 $fileContents = $this->config->stdinContent; | |
328 if ($fileContents === null) { | |
329 $handle = fopen('php://stdin', 'r'); | |
330 stream_set_blocking($handle, true); | |
331 $fileContents = stream_get_contents($handle); | |
332 fclose($handle); | |
333 } | |
334 | |
335 $todo = new FileList($this->config, $this->ruleset); | |
336 $dummy = new DummyFile($fileContents, $this->ruleset, $this->config); | |
337 $todo->addFile($dummy->path, $dummy); | |
338 } else { | |
339 if (empty($this->config->files) === true) { | |
340 $error = 'ERROR: You must supply at least one file or directory to process.'.PHP_EOL.PHP_EOL; | |
341 $error .= $this->config->printShortUsage(true); | |
342 throw new DeepExitException($error, 3); | |
343 } | |
344 | |
345 if (PHP_CODESNIFFER_VERBOSITY > 0) { | |
346 echo 'Creating file list... '; | |
347 } | |
348 | |
349 $todo = new FileList($this->config, $this->ruleset); | |
350 | |
351 if (PHP_CODESNIFFER_VERBOSITY > 0) { | |
352 $numFiles = count($todo); | |
353 echo "DONE ($numFiles files in queue)".PHP_EOL; | |
354 } | |
355 | |
356 if ($this->config->cache === true) { | |
357 if (PHP_CODESNIFFER_VERBOSITY > 0) { | |
358 echo 'Loading cache... '; | |
359 } | |
360 | |
361 Cache::load($this->ruleset, $this->config); | |
362 | |
363 if (PHP_CODESNIFFER_VERBOSITY > 0) { | |
364 $size = Cache::getSize(); | |
365 echo "DONE ($size files in cache)".PHP_EOL; | |
366 } | |
367 } | |
368 }//end if | |
369 | |
370 // Turn all sniff errors into exceptions. | |
371 set_error_handler([$this, 'handleErrors']); | |
372 | |
373 // If verbosity is too high, turn off parallelism so the | |
374 // debug output is clean. | |
375 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
376 $this->config->parallel = 1; | |
377 } | |
378 | |
379 // If the PCNTL extension isn't installed, we can't fork. | |
380 if (function_exists('pcntl_fork') === false) { | |
381 $this->config->parallel = 1; | |
382 } | |
383 | |
384 $lastDir = ''; | |
385 $numFiles = count($todo); | |
386 | |
387 if ($this->config->parallel === 1) { | |
388 // Running normally. | |
389 $numProcessed = 0; | |
390 foreach ($todo as $path => $file) { | |
391 if ($file->ignored === false) { | |
392 $currDir = dirname($path); | |
393 if ($lastDir !== $currDir) { | |
394 if (PHP_CODESNIFFER_VERBOSITY > 0) { | |
395 echo 'Changing into directory '.Common::stripBasepath($currDir, $this->config->basepath).PHP_EOL; | |
396 } | |
397 | |
398 $lastDir = $currDir; | |
399 } | |
400 | |
401 $this->processFile($file); | |
402 } else if (PHP_CODESNIFFER_VERBOSITY > 0) { | |
403 echo 'Skipping '.basename($file->path).PHP_EOL; | |
404 } | |
405 | |
406 $numProcessed++; | |
407 $this->printProgress($file, $numFiles, $numProcessed); | |
408 } | |
409 } else { | |
410 // Batching and forking. | |
411 $childProcs = []; | |
412 $numPerBatch = ceil($numFiles / $this->config->parallel); | |
413 | |
414 for ($batch = 0; $batch < $this->config->parallel; $batch++) { | |
415 $startAt = ($batch * $numPerBatch); | |
416 if ($startAt >= $numFiles) { | |
417 break; | |
418 } | |
419 | |
420 $endAt = ($startAt + $numPerBatch); | |
421 if ($endAt > $numFiles) { | |
422 $endAt = $numFiles; | |
423 } | |
424 | |
425 $childOutFilename = tempnam(sys_get_temp_dir(), 'phpcs-child'); | |
426 $pid = pcntl_fork(); | |
427 if ($pid === -1) { | |
428 throw new RuntimeException('Failed to create child process'); | |
429 } else if ($pid !== 0) { | |
430 $childProcs[] = [ | |
431 'pid' => $pid, | |
432 'out' => $childOutFilename, | |
433 ]; | |
434 } else { | |
435 // Move forward to the start of the batch. | |
436 $todo->rewind(); | |
437 for ($i = 0; $i < $startAt; $i++) { | |
438 $todo->next(); | |
439 } | |
440 | |
441 // Reset the reporter to make sure only figures from this | |
442 // file batch are recorded. | |
443 $this->reporter->totalFiles = 0; | |
444 $this->reporter->totalErrors = 0; | |
445 $this->reporter->totalWarnings = 0; | |
446 $this->reporter->totalFixable = 0; | |
447 $this->reporter->totalFixed = 0; | |
448 | |
449 // Process the files. | |
450 $pathsProcessed = []; | |
451 ob_start(); | |
452 for ($i = $startAt; $i < $endAt; $i++) { | |
453 $path = $todo->key(); | |
454 $file = $todo->current(); | |
455 | |
456 if ($file->ignored === true) { | |
457 continue; | |
458 } | |
459 | |
460 $currDir = dirname($path); | |
461 if ($lastDir !== $currDir) { | |
462 if (PHP_CODESNIFFER_VERBOSITY > 0) { | |
463 echo 'Changing into directory '.Common::stripBasepath($currDir, $this->config->basepath).PHP_EOL; | |
464 } | |
465 | |
466 $lastDir = $currDir; | |
467 } | |
468 | |
469 $this->processFile($file); | |
470 | |
471 $pathsProcessed[] = $path; | |
472 $todo->next(); | |
473 }//end for | |
474 | |
475 $debugOutput = ob_get_contents(); | |
476 ob_end_clean(); | |
477 | |
478 // Write information about the run to the filesystem | |
479 // so it can be picked up by the main process. | |
480 $childOutput = [ | |
481 'totalFiles' => $this->reporter->totalFiles, | |
482 'totalErrors' => $this->reporter->totalErrors, | |
483 'totalWarnings' => $this->reporter->totalWarnings, | |
484 'totalFixable' => $this->reporter->totalFixable, | |
485 'totalFixed' => $this->reporter->totalFixed, | |
486 ]; | |
487 | |
488 $output = '<'.'?php'."\n".' $childOutput = '; | |
489 $output .= var_export($childOutput, true); | |
490 $output .= ";\n\$debugOutput = "; | |
491 $output .= var_export($debugOutput, true); | |
492 | |
493 if ($this->config->cache === true) { | |
494 $childCache = []; | |
495 foreach ($pathsProcessed as $path) { | |
496 $childCache[$path] = Cache::get($path); | |
497 } | |
498 | |
499 $output .= ";\n\$childCache = "; | |
500 $output .= var_export($childCache, true); | |
501 } | |
502 | |
503 $output .= ";\n?".'>'; | |
504 file_put_contents($childOutFilename, $output); | |
505 exit($pid); | |
506 }//end if | |
507 }//end for | |
508 | |
509 $this->processChildProcs($childProcs); | |
510 }//end if | |
511 | |
512 restore_error_handler(); | |
513 | |
514 if (PHP_CODESNIFFER_VERBOSITY === 0 | |
515 && $this->config->interactive === false | |
516 && $this->config->showProgress === true | |
517 ) { | |
518 echo PHP_EOL.PHP_EOL; | |
519 } | |
520 | |
521 if ($this->config->cache === true) { | |
522 Cache::save(); | |
523 } | |
524 | |
525 $ignoreWarnings = Config::getConfigData('ignore_warnings_on_exit'); | |
526 $ignoreErrors = Config::getConfigData('ignore_errors_on_exit'); | |
527 | |
528 $return = ($this->reporter->totalErrors + $this->reporter->totalWarnings); | |
529 if ($ignoreErrors !== null) { | |
530 $ignoreErrors = (bool) $ignoreErrors; | |
531 if ($ignoreErrors === true) { | |
532 $return -= $this->reporter->totalErrors; | |
533 } | |
534 } | |
535 | |
536 if ($ignoreWarnings !== null) { | |
537 $ignoreWarnings = (bool) $ignoreWarnings; | |
538 if ($ignoreWarnings === true) { | |
539 $return -= $this->reporter->totalWarnings; | |
540 } | |
541 } | |
542 | |
543 return $return; | |
544 | |
545 }//end run() | |
546 | |
547 | |
548 /** | |
549 * Converts all PHP errors into exceptions. | |
550 * | |
551 * This method forces a sniff to stop processing if it is not | |
552 * able to handle a specific piece of code, instead of continuing | |
553 * and potentially getting into a loop. | |
554 * | |
555 * @param int $code The level of error raised. | |
556 * @param string $message The error message. | |
557 * @param string $file The path of the file that raised the error. | |
558 * @param int $line The line number the error was raised at. | |
559 * | |
560 * @return void | |
561 */ | |
562 public function handleErrors($code, $message, $file, $line) | |
563 { | |
564 if ((error_reporting() & $code) === 0) { | |
565 // This type of error is being muted. | |
566 return true; | |
567 } | |
568 | |
569 throw new RuntimeException("$message in $file on line $line"); | |
570 | |
571 }//end handleErrors() | |
572 | |
573 | |
574 /** | |
575 * Processes a single file, including checking and fixing. | |
576 * | |
577 * @param \PHP_CodeSniffer\Files\File $file The file to be processed. | |
578 * | |
579 * @return void | |
580 */ | |
581 public function processFile($file) | |
582 { | |
583 if (PHP_CODESNIFFER_VERBOSITY > 0) { | |
584 $startTime = microtime(true); | |
585 echo 'Processing '.basename($file->path).' '; | |
586 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
587 echo PHP_EOL; | |
588 } | |
589 } | |
590 | |
591 try { | |
592 $file->process(); | |
593 | |
594 if (PHP_CODESNIFFER_VERBOSITY > 0) { | |
595 $timeTaken = ((microtime(true) - $startTime) * 1000); | |
596 if ($timeTaken < 1000) { | |
597 $timeTaken = round($timeTaken); | |
598 echo "DONE in {$timeTaken}ms"; | |
599 } else { | |
600 $timeTaken = round(($timeTaken / 1000), 2); | |
601 echo "DONE in $timeTaken secs"; | |
602 } | |
603 | |
604 if (PHP_CODESNIFFER_CBF === true) { | |
605 $errors = $file->getFixableCount(); | |
606 echo " ($errors fixable violations)".PHP_EOL; | |
607 } else { | |
608 $errors = $file->getErrorCount(); | |
609 $warnings = $file->getWarningCount(); | |
610 echo " ($errors errors, $warnings warnings)".PHP_EOL; | |
611 } | |
612 } | |
613 } catch (\Exception $e) { | |
614 $error = 'An error occurred during processing; checking has been aborted. The error message was: '.$e->getMessage(); | |
615 $file->addErrorOnLine($error, 1, 'Internal.Exception'); | |
616 }//end try | |
617 | |
618 $this->reporter->cacheFileReport($file, $this->config); | |
619 | |
620 if ($this->config->interactive === true) { | |
621 /* | |
622 Running interactively. | |
623 Print the error report for the current file and then wait for user input. | |
624 */ | |
625 | |
626 // Get current violations and then clear the list to make sure | |
627 // we only print violations for a single file each time. | |
628 $numErrors = null; | |
629 while ($numErrors !== 0) { | |
630 $numErrors = ($file->getErrorCount() + $file->getWarningCount()); | |
631 if ($numErrors === 0) { | |
632 continue; | |
633 } | |
634 | |
635 $this->reporter->printReport('full'); | |
636 | |
637 echo '<ENTER> to recheck, [s] to skip or [q] to quit : '; | |
638 $input = fgets(STDIN); | |
639 $input = trim($input); | |
640 | |
641 switch ($input) { | |
642 case 's': | |
643 break(2); | |
644 case 'q': | |
645 throw new DeepExitException('', 0); | |
646 default: | |
647 // Repopulate the sniffs because some of them save their state | |
648 // and only clear it when the file changes, but we are rechecking | |
649 // the same file. | |
650 $file->ruleset->populateTokenListeners(); | |
651 $file->reloadContent(); | |
652 $file->process(); | |
653 $this->reporter->cacheFileReport($file, $this->config); | |
654 break; | |
655 } | |
656 }//end while | |
657 }//end if | |
658 | |
659 // Clean up the file to save (a lot of) memory. | |
660 $file->cleanUp(); | |
661 | |
662 }//end processFile() | |
663 | |
664 | |
665 /** | |
666 * Waits for child processes to complete and cleans up after them. | |
667 * | |
668 * The reporting information returned by each child process is merged | |
669 * into the main reporter class. | |
670 * | |
671 * @param array $childProcs An array of child processes to wait for. | |
672 * | |
673 * @return void | |
674 */ | |
675 private function processChildProcs($childProcs) | |
676 { | |
677 $numProcessed = 0; | |
678 $totalBatches = count($childProcs); | |
679 | |
680 while (count($childProcs) > 0) { | |
681 foreach ($childProcs as $key => $procData) { | |
682 $res = pcntl_waitpid($procData['pid'], $status, WNOHANG); | |
683 if ($res === $procData['pid']) { | |
684 if (file_exists($procData['out']) === true) { | |
685 include $procData['out']; | |
686 if (isset($childOutput) === true) { | |
687 $this->reporter->totalFiles += $childOutput['totalFiles']; | |
688 $this->reporter->totalErrors += $childOutput['totalErrors']; | |
689 $this->reporter->totalWarnings += $childOutput['totalWarnings']; | |
690 $this->reporter->totalFixable += $childOutput['totalFixable']; | |
691 $this->reporter->totalFixed += $childOutput['totalFixed']; | |
692 } | |
693 | |
694 if (isset($debugOutput) === true) { | |
695 echo $debugOutput; | |
696 } | |
697 | |
698 if (isset($childCache) === true) { | |
699 foreach ($childCache as $path => $cache) { | |
700 Cache::set($path, $cache); | |
701 } | |
702 } | |
703 | |
704 unlink($procData['out']); | |
705 unset($childProcs[$key]); | |
706 | |
707 $numProcessed++; | |
708 | |
709 // Fake a processed file so we can print progress output for the batch. | |
710 $file = new DummyFile(null, $this->ruleset, $this->config); | |
711 $file->setErrorCounts( | |
712 $childOutput['totalErrors'], | |
713 $childOutput['totalWarnings'], | |
714 $childOutput['totalFixable'], | |
715 $childOutput['totalFixed'] | |
716 ); | |
717 $this->printProgress($file, $totalBatches, $numProcessed); | |
718 }//end if | |
719 }//end if | |
720 }//end foreach | |
721 }//end while | |
722 | |
723 }//end processChildProcs() | |
724 | |
725 | |
726 /** | |
727 * Print progress information for a single processed file. | |
728 * | |
729 * @param File $file The file that was processed. | |
730 * @param int $numFiles The total number of files to process. | |
731 * @param int $numProcessed The number of files that have been processed, | |
732 * including this one. | |
733 * | |
734 * @return void | |
735 */ | |
736 public function printProgress($file, $numFiles, $numProcessed) | |
737 { | |
738 if (PHP_CODESNIFFER_VERBOSITY > 0 | |
739 || $this->config->showProgress === false | |
740 ) { | |
741 return; | |
742 } | |
743 | |
744 // Show progress information. | |
745 if ($file->ignored === true) { | |
746 echo 'S'; | |
747 } else { | |
748 $errors = $file->getErrorCount(); | |
749 $warnings = $file->getWarningCount(); | |
750 $fixable = $file->getFixableCount(); | |
751 $fixed = $file->getFixedCount(); | |
752 | |
753 if (PHP_CODESNIFFER_CBF === true) { | |
754 // Files with fixed errors or warnings are F (green). | |
755 // Files with unfixable errors or warnings are E (red). | |
756 // Files with no errors or warnings are . (black). | |
757 if ($fixable > 0) { | |
758 if ($this->config->colors === true) { | |
759 echo "\033[31m"; | |
760 } | |
761 | |
762 echo 'E'; | |
763 | |
764 if ($this->config->colors === true) { | |
765 echo "\033[0m"; | |
766 } | |
767 } else if ($fixed > 0) { | |
768 if ($this->config->colors === true) { | |
769 echo "\033[32m"; | |
770 } | |
771 | |
772 echo 'F'; | |
773 | |
774 if ($this->config->colors === true) { | |
775 echo "\033[0m"; | |
776 } | |
777 } else { | |
778 echo '.'; | |
779 }//end if | |
780 } else { | |
781 // Files with errors are E (red). | |
782 // Files with fixable errors are E (green). | |
783 // Files with warnings are W (yellow). | |
784 // Files with fixable warnings are W (green). | |
785 // Files with no errors or warnings are . (black). | |
786 if ($errors > 0) { | |
787 if ($this->config->colors === true) { | |
788 if ($fixable > 0) { | |
789 echo "\033[32m"; | |
790 } else { | |
791 echo "\033[31m"; | |
792 } | |
793 } | |
794 | |
795 echo 'E'; | |
796 | |
797 if ($this->config->colors === true) { | |
798 echo "\033[0m"; | |
799 } | |
800 } else if ($warnings > 0) { | |
801 if ($this->config->colors === true) { | |
802 if ($fixable > 0) { | |
803 echo "\033[32m"; | |
804 } else { | |
805 echo "\033[33m"; | |
806 } | |
807 } | |
808 | |
809 echo 'W'; | |
810 | |
811 if ($this->config->colors === true) { | |
812 echo "\033[0m"; | |
813 } | |
814 } else { | |
815 echo '.'; | |
816 }//end if | |
817 }//end if | |
818 }//end if | |
819 | |
820 $numPerLine = 60; | |
821 if ($numProcessed !== $numFiles && ($numProcessed % $numPerLine) !== 0) { | |
822 return; | |
823 } | |
824 | |
825 $percent = round(($numProcessed / $numFiles) * 100); | |
826 $padding = (strlen($numFiles) - strlen($numProcessed)); | |
827 if ($numProcessed === $numFiles && $numFiles > $numPerLine) { | |
828 $padding += ($numPerLine - ($numFiles - (floor($numFiles / $numPerLine) * $numPerLine))); | |
829 } | |
830 | |
831 echo str_repeat(' ', $padding)." $numProcessed / $numFiles ($percent%)".PHP_EOL; | |
832 | |
833 }//end printProgress() | |
834 | |
835 | |
836 }//end class |