|
14 | 14 | use PHP_CodeSniffer\Exceptions\RuntimeException;
|
15 | 15 | use PHP_CodeSniffer\Sniffs\DeprecatedSniff;
|
16 | 16 | use PHP_CodeSniffer\Util\Common;
|
| 17 | +use PHP_CodeSniffer\Util\MessageCollector; |
17 | 18 | use PHP_CodeSniffer\Util\Standards;
|
18 | 19 | use RecursiveDirectoryIterator;
|
19 | 20 | use RecursiveIteratorIterator;
|
@@ -131,21 +132,36 @@ class Ruleset
|
131 | 132 | */
|
132 | 133 | private $deprecatedSniffs = [];
|
133 | 134 |
|
| 135 | + /** |
| 136 | + * Message collector object. |
| 137 | + * |
| 138 | + * User-facing messages should be collected via this object for display once the ruleset processing has finished. |
| 139 | + * |
| 140 | + * The following type of errors should *NOT* be collected, but should still throw their own `RuntimeException`: |
| 141 | + * - Errors which could cause other (uncollectable) errors further into the ruleset processing, like a missing autoload file. |
| 142 | + * - Errors which are directly aimed at and only intended for sniff developers or integrators |
| 143 | + * (in contrast to ruleset maintainers or end-users). |
| 144 | + * |
| 145 | + * @var \PHP_CodeSniffer\Util\MessageCollector |
| 146 | + */ |
| 147 | + private $msgCache; |
| 148 | + |
134 | 149 |
|
135 | 150 | /**
|
136 | 151 | * Initialise the ruleset that the run will use.
|
137 | 152 | *
|
138 | 153 | * @param \PHP_CodeSniffer\Config $config The config data for the run.
|
139 | 154 | *
|
140 | 155 | * @return void
|
141 |
| - * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If no sniffs were registered. |
| 156 | + * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If blocking errors were encountered when processing the ruleset. |
142 | 157 | */
|
143 | 158 | public function __construct(Config $config)
|
144 | 159 | {
|
145 |
| - $this->config = $config; |
146 |
| - $restrictions = $config->sniffs; |
147 |
| - $exclusions = $config->exclude; |
148 |
| - $sniffs = []; |
| 160 | + $this->config = $config; |
| 161 | + $restrictions = $config->sniffs; |
| 162 | + $exclusions = $config->exclude; |
| 163 | + $sniffs = []; |
| 164 | + $this->msgCache = new MessageCollector(); |
149 | 165 |
|
150 | 166 | $standardPaths = [];
|
151 | 167 | foreach ($config->standards as $standard) {
|
@@ -186,11 +202,11 @@ public function __construct(Config $config)
|
186 | 202 |
|
187 | 203 | if (defined('PHP_CODESNIFFER_IN_TESTS') === true && empty($restrictions) === false) {
|
188 | 204 | // In unit tests, only register the sniffs that the test wants and not the entire standard.
|
189 |
| - try { |
190 |
| - foreach ($restrictions as $restriction) { |
191 |
| - $sniffs = array_merge($sniffs, $this->expandRulesetReference($restriction, dirname($standard))); |
192 |
| - } |
193 |
| - } catch (RuntimeException $e) { |
| 205 | + foreach ($restrictions as $restriction) { |
| 206 | + $sniffs = array_merge($sniffs, $this->expandRulesetReference($restriction, dirname($standard))); |
| 207 | + } |
| 208 | + |
| 209 | + if (empty($sniffs) === true) { |
194 | 210 | // Sniff reference could not be expanded, which probably means this
|
195 | 211 | // is an installed standard. Let the unit test system take care of
|
196 | 212 | // setting the correct sniff for testing.
|
@@ -239,9 +255,11 @@ public function __construct(Config $config)
|
239 | 255 | }
|
240 | 256 |
|
241 | 257 | if ($numSniffs === 0) {
|
242 |
| - throw new RuntimeException('ERROR: No sniffs were registered'); |
| 258 | + $this->msgCache->add('No sniffs were registered.', MessageCollector::ERROR); |
243 | 259 | }
|
244 | 260 |
|
| 261 | + $this->displayCachedMessages(); |
| 262 | + |
245 | 263 | }//end __construct()
|
246 | 264 |
|
247 | 265 |
|
@@ -461,6 +479,35 @@ public function showSniffDeprecations()
|
461 | 479 | }//end showSniffDeprecations()
|
462 | 480 |
|
463 | 481 |
|
| 482 | + /** |
| 483 | + * Print any notices encountered while processing the ruleset(s). |
| 484 | + * |
| 485 | + * Note: these messages aren't shown at the time they are encountered to avoid "one error hiding behind another". |
| 486 | + * This way the (end-)user gets to see all of them in one go. |
| 487 | + * |
| 488 | + * @return void |
| 489 | + * |
| 490 | + * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If blocking errors were encountered. |
| 491 | + */ |
| 492 | + private function displayCachedMessages() |
| 493 | + { |
| 494 | + // Don't show deprecations/notices/warnings in quiet mode, in explain mode |
| 495 | + // or when the documentation is being shown. |
| 496 | + // Documentation and explain will call the Ruleset multiple times which |
| 497 | + // would lead to duplicate display of the messages. |
| 498 | + if ($this->msgCache->containsBlockingErrors() === false |
| 499 | + && ($this->config->quiet === true |
| 500 | + || $this->config->explain === true |
| 501 | + || $this->config->generator !== null) |
| 502 | + ) { |
| 503 | + return; |
| 504 | + } |
| 505 | + |
| 506 | + $this->msgCache->display(); |
| 507 | + |
| 508 | + }//end displayCachedMessages() |
| 509 | + |
| 510 | + |
464 | 511 | /**
|
465 | 512 | * Processes a single ruleset and returns a list of the sniffs it represents.
|
466 | 513 | *
|
@@ -993,8 +1040,8 @@ private function expandRulesetReference($ref, $rulesetDir, $depth=0)
|
993 | 1040 | }
|
994 | 1041 | } else {
|
995 | 1042 | if (is_file($ref) === false) {
|
996 |
| - $error = "ERROR: Referenced sniff \"$ref\" does not exist"; |
997 |
| - throw new RuntimeException($error); |
| 1043 | + $this->msgCache->add("Referenced sniff \"$ref\" does not exist.", MessageCollector::ERROR); |
| 1044 | + return []; |
998 | 1045 | }
|
999 | 1046 |
|
1000 | 1047 | if (substr($ref, -9) === 'Sniff.php') {
|
@@ -1083,18 +1130,19 @@ private function processRule($rule, $newSniffs, $depth=0)
|
1083 | 1130 |
|
1084 | 1131 | $type = strtolower((string) $rule->type);
|
1085 | 1132 | if ($type !== 'error' && $type !== 'warning') {
|
1086 |
| - throw new RuntimeException("ERROR: Message type \"$type\" is invalid; must be \"error\" or \"warning\""); |
1087 |
| - } |
| 1133 | + $message = "Message type \"$type\" for \"$code\" is invalid; must be \"error\" or \"warning\"."; |
| 1134 | + $this->msgCache->add($message, MessageCollector::ERROR); |
| 1135 | + } else { |
| 1136 | + $this->ruleset[$code]['type'] = $type; |
| 1137 | + if (PHP_CODESNIFFER_VERBOSITY > 1) { |
| 1138 | + echo str_repeat("\t", $depth); |
| 1139 | + echo "\t\t=> message type set to ".(string) $rule->type; |
| 1140 | + if ($code !== $ref) { |
| 1141 | + echo " for $code"; |
| 1142 | + } |
1088 | 1143 |
|
1089 |
| - $this->ruleset[$code]['type'] = $type; |
1090 |
| - if (PHP_CODESNIFFER_VERBOSITY > 1) { |
1091 |
| - echo str_repeat("\t", $depth); |
1092 |
| - echo "\t\t=> message type set to ".(string) $rule->type; |
1093 |
| - if ($code !== $ref) { |
1094 |
| - echo " for $code"; |
| 1144 | + echo PHP_EOL; |
1095 | 1145 | }
|
1096 |
| - |
1097 |
| - echo PHP_EOL; |
1098 | 1146 | }
|
1099 | 1147 | }//end if
|
1100 | 1148 |
|
@@ -1414,8 +1462,12 @@ public function populateTokenListeners()
|
1414 | 1462 |
|
1415 | 1463 | $tokens = $this->sniffs[$sniffClass]->register();
|
1416 | 1464 | if (is_array($tokens) === false) {
|
1417 |
| - $msg = "ERROR: Sniff $sniffClass register() method must return an array"; |
1418 |
| - throw new RuntimeException($msg); |
| 1465 | + $msg = "The sniff {$sniffClass}::register() method must return an array."; |
| 1466 | + $this->msgCache->add($msg, MessageCollector::ERROR); |
| 1467 | + |
| 1468 | + // Unregister the sniff. |
| 1469 | + unset($this->sniffs[$sniffClass], $this->sniffCodes[$sniffCode], $this->deprecatedSniffs[$sniffCode]); |
| 1470 | + continue; |
1419 | 1471 | }
|
1420 | 1472 |
|
1421 | 1473 | $ignorePatterns = [];
|
@@ -1525,9 +1577,9 @@ public function setSniffProperty($sniffClass, $name, $settings)
|
1525 | 1577 |
|
1526 | 1578 | if ($isSettable === false) {
|
1527 | 1579 | if ($settings['scope'] === 'sniff') {
|
1528 |
| - $notice = "ERROR: Ruleset invalid. Property \"$propertyName\" does not exist on sniff "; |
1529 |
| - $notice .= array_search($sniffClass, $this->sniffCodes, true); |
1530 |
| - throw new RuntimeException($notice); |
| 1580 | + $notice = "Property \"$propertyName\" does not exist on sniff "; |
| 1581 | + $notice .= array_search($sniffClass, $this->sniffCodes, true).'.'; |
| 1582 | + $this->msgCache->add($notice, MessageCollector::ERROR); |
1531 | 1583 | }
|
1532 | 1584 |
|
1533 | 1585 | return;
|
|
0 commit comments