Mercurial > hg > cmmr2012-drupal-site
comparison vendor/squizlabs/php_codesniffer/src/Ruleset.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 * Stores the rules used to check and fix files. | |
4 * | |
5 * A ruleset object directly maps to a ruleset XML file. | |
6 * | |
7 * @author Greg Sherwood <gsherwood@squiz.net> | |
8 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) | |
9 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence | |
10 */ | |
11 | |
12 namespace PHP_CodeSniffer; | |
13 | |
14 use PHP_CodeSniffer\Util; | |
15 use PHP_CodeSniffer\Exceptions\RuntimeException; | |
16 | |
17 class Ruleset | |
18 { | |
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\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\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 $this->paths[] = $standard; | |
166 | |
167 // Allow autoloading of custom files inside this standard. | |
168 if (isset($ruleset['namespace']) === true) { | |
169 $namespace = (string) $ruleset['namespace']; | |
170 } else { | |
171 $namespace = basename(dirname($standard)); | |
172 } | |
173 | |
174 Autoload::addSearchPath(dirname($standard), $namespace); | |
175 } | |
176 | |
177 if (defined('PHP_CODESNIFFER_IN_TESTS') === true && empty($restrictions) === false) { | |
178 // In unit tests, only register the sniffs that the test wants and not the entire standard. | |
179 try { | |
180 foreach ($restrictions as $restriction) { | |
181 $sniffs = array_merge($sniffs, $this->expandRulesetReference($restriction, dirname($standard))); | |
182 } | |
183 } catch (RuntimeException $e) { | |
184 // Sniff reference could not be expanded, which probably means this | |
185 // is an installed standard. Let the unit test system take care of | |
186 // setting the correct sniff for testing. | |
187 return; | |
188 } | |
189 | |
190 break; | |
191 } | |
192 | |
193 if (PHP_CODESNIFFER_VERBOSITY === 1) { | |
194 echo "Registering sniffs in the $standardName standard... "; | |
195 if (count($config->standards) > 1 || PHP_CODESNIFFER_VERBOSITY > 2) { | |
196 echo PHP_EOL; | |
197 } | |
198 } | |
199 | |
200 $sniffs = array_merge($sniffs, $this->processRuleset($standard)); | |
201 }//end foreach | |
202 | |
203 $sniffRestrictions = []; | |
204 foreach ($restrictions as $sniffCode) { | |
205 $parts = explode('.', strtolower($sniffCode)); | |
206 $sniffName = $parts[0].'\sniffs\\'.$parts[1].'\\'.$parts[2].'sniff'; | |
207 $sniffRestrictions[$sniffName] = true; | |
208 } | |
209 | |
210 $sniffExclusions = []; | |
211 foreach ($exclusions as $sniffCode) { | |
212 $parts = explode('.', strtolower($sniffCode)); | |
213 $sniffName = $parts[0].'\sniffs\\'.$parts[1].'\\'.$parts[2].'sniff'; | |
214 $sniffExclusions[$sniffName] = true; | |
215 } | |
216 | |
217 $this->registerSniffs($sniffs, $sniffRestrictions, $sniffExclusions); | |
218 $this->populateTokenListeners(); | |
219 | |
220 $numSniffs = count($this->sniffs); | |
221 if (PHP_CODESNIFFER_VERBOSITY === 1) { | |
222 echo "DONE ($numSniffs sniffs registered)".PHP_EOL; | |
223 } | |
224 | |
225 if ($numSniffs === 0) { | |
226 throw new RuntimeException('No sniffs were registered'); | |
227 } | |
228 | |
229 }//end __construct() | |
230 | |
231 | |
232 /** | |
233 * Prints a report showing the sniffs contained in a standard. | |
234 * | |
235 * @return void | |
236 */ | |
237 public function explain() | |
238 { | |
239 $sniffs = array_keys($this->sniffCodes); | |
240 sort($sniffs); | |
241 | |
242 ob_start(); | |
243 | |
244 $lastStandard = null; | |
245 $lastCount = ''; | |
246 $sniffCount = count($sniffs); | |
247 | |
248 // Add a dummy entry to the end so we loop | |
249 // one last time and clear the output buffer. | |
250 $sniffs[] = ''; | |
251 | |
252 echo PHP_EOL."The $this->name standard contains $sniffCount sniffs".PHP_EOL; | |
253 | |
254 ob_start(); | |
255 | |
256 foreach ($sniffs as $i => $sniff) { | |
257 if ($i === $sniffCount) { | |
258 $currentStandard = null; | |
259 } else { | |
260 $currentStandard = substr($sniff, 0, strpos($sniff, '.')); | |
261 if ($lastStandard === null) { | |
262 $lastStandard = $currentStandard; | |
263 } | |
264 } | |
265 | |
266 if ($currentStandard !== $lastStandard) { | |
267 $sniffList = ob_get_contents(); | |
268 ob_end_clean(); | |
269 | |
270 echo PHP_EOL.$lastStandard.' ('.$lastCount.' sniff'; | |
271 if ($lastCount > 1) { | |
272 echo 's'; | |
273 } | |
274 | |
275 echo ')'.PHP_EOL; | |
276 echo str_repeat('-', (strlen($lastStandard.$lastCount) + 10)); | |
277 echo PHP_EOL; | |
278 echo $sniffList; | |
279 | |
280 $lastStandard = $currentStandard; | |
281 $lastCount = 0; | |
282 | |
283 if ($currentStandard === null) { | |
284 break; | |
285 } | |
286 | |
287 ob_start(); | |
288 }//end if | |
289 | |
290 echo ' '.$sniff.PHP_EOL; | |
291 $lastCount++; | |
292 }//end foreach | |
293 | |
294 }//end explain() | |
295 | |
296 | |
297 /** | |
298 * Processes a single ruleset and returns a list of the sniffs it represents. | |
299 * | |
300 * Rules founds within the ruleset are processed immediately, but sniff classes | |
301 * are not registered by this method. | |
302 * | |
303 * @param string $rulesetPath The path to a ruleset XML file. | |
304 * @param int $depth How many nested processing steps we are in. This | |
305 * is only used for debug output. | |
306 * | |
307 * @return string[] | |
308 * @throws RuntimeException If the ruleset path is invalid. | |
309 */ | |
310 public function processRuleset($rulesetPath, $depth=0) | |
311 { | |
312 $rulesetPath = Util\Common::realpath($rulesetPath); | |
313 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
314 echo str_repeat("\t", $depth); | |
315 echo 'Processing ruleset '.Util\Common::stripBasepath($rulesetPath, $this->config->basepath).PHP_EOL; | |
316 } | |
317 | |
318 $ruleset = @simplexml_load_string(file_get_contents($rulesetPath)); | |
319 if ($ruleset === false) { | |
320 throw new RuntimeException("Ruleset $rulesetPath is not valid"); | |
321 } | |
322 | |
323 $ownSniffs = []; | |
324 $includedSniffs = []; | |
325 $excludedSniffs = []; | |
326 | |
327 $rulesetDir = dirname($rulesetPath); | |
328 $this->rulesetDirs[] = $rulesetDir; | |
329 | |
330 $sniffDir = $rulesetDir.DIRECTORY_SEPARATOR.'Sniffs'; | |
331 if (is_dir($sniffDir) === true) { | |
332 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
333 echo str_repeat("\t", $depth); | |
334 echo "\tAdding sniff files from ".Util\Common::stripBasepath($sniffDir, $this->config->basepath).' directory'.PHP_EOL; | |
335 } | |
336 | |
337 $ownSniffs = $this->expandSniffDirectory($sniffDir, $depth); | |
338 } | |
339 | |
340 // Included custom autoloaders. | |
341 foreach ($ruleset->{'autoload'} as $autoload) { | |
342 if ($this->shouldProcessElement($autoload) === false) { | |
343 continue; | |
344 } | |
345 | |
346 $autoloadPath = (string) $autoload; | |
347 if (is_file($autoloadPath) === false) { | |
348 $autoloadPath = Util\Common::realPath(dirname($rulesetPath).DIRECTORY_SEPARATOR.$autoloadPath); | |
349 } | |
350 | |
351 if ($autoloadPath === false) { | |
352 throw new RuntimeException('The specified autoload file "'.$autoload.'" does not exist'); | |
353 } | |
354 | |
355 include_once $autoloadPath; | |
356 | |
357 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
358 echo str_repeat("\t", $depth); | |
359 echo "\t=> included autoloader $autoloadPath".PHP_EOL; | |
360 } | |
361 }//end foreach | |
362 | |
363 // Process custom sniff config settings. | |
364 foreach ($ruleset->{'config'} as $config) { | |
365 if ($this->shouldProcessElement($config) === false) { | |
366 continue; | |
367 } | |
368 | |
369 Config::setConfigData((string) $config['name'], (string) $config['value'], true); | |
370 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
371 echo str_repeat("\t", $depth); | |
372 echo "\t=> set config value ".(string) $config['name'].': '.(string) $config['value'].PHP_EOL; | |
373 } | |
374 } | |
375 | |
376 foreach ($ruleset->rule as $rule) { | |
377 if (isset($rule['ref']) === false | |
378 || $this->shouldProcessElement($rule) === false | |
379 ) { | |
380 continue; | |
381 } | |
382 | |
383 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
384 echo str_repeat("\t", $depth); | |
385 echo "\tProcessing rule \"".$rule['ref'].'"'.PHP_EOL; | |
386 } | |
387 | |
388 $expandedSniffs = $this->expandRulesetReference((string) $rule['ref'], $rulesetDir, $depth); | |
389 $newSniffs = array_diff($expandedSniffs, $includedSniffs); | |
390 $includedSniffs = array_merge($includedSniffs, $expandedSniffs); | |
391 | |
392 $parts = explode('.', $rule['ref']); | |
393 if (count($parts) === 4 | |
394 && $parts[0] !== '' | |
395 && $parts[1] !== '' | |
396 && $parts[2] !== '' | |
397 ) { | |
398 $sniffCode = $parts[0].'.'.$parts[1].'.'.$parts[2]; | |
399 if (isset($this->ruleset[$sniffCode]['severity']) === true | |
400 && $this->ruleset[$sniffCode]['severity'] === 0 | |
401 ) { | |
402 // This sniff code has already been turned off, but now | |
403 // it is being explicitly included again, so turn it back on. | |
404 $this->ruleset[(string) $rule['ref']]['severity'] = 5; | |
405 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
406 echo str_repeat("\t", $depth); | |
407 echo "\t\t* disabling sniff exclusion for specific message code *".PHP_EOL; | |
408 echo str_repeat("\t", $depth); | |
409 echo "\t\t=> severity set to 5".PHP_EOL; | |
410 } | |
411 } else if (empty($newSniffs) === false) { | |
412 $newSniff = $newSniffs[0]; | |
413 if (in_array($newSniff, $ownSniffs) === false) { | |
414 // Including a sniff that hasn't been included higher up, but | |
415 // only including a single message from it. So turn off all messages in | |
416 // the sniff, except this one. | |
417 $this->ruleset[$sniffCode]['severity'] = 0; | |
418 $this->ruleset[(string) $rule['ref']]['severity'] = 5; | |
419 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
420 echo str_repeat("\t", $depth); | |
421 echo "\t\tExcluding sniff \"".$sniffCode.'" except for "'.$parts[3].'"'.PHP_EOL; | |
422 } | |
423 } | |
424 }//end if | |
425 }//end if | |
426 | |
427 if (isset($rule->exclude) === true) { | |
428 foreach ($rule->exclude as $exclude) { | |
429 if (isset($exclude['name']) === false) { | |
430 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
431 echo str_repeat("\t", $depth); | |
432 echo "\t\t* ignoring empty exclude rule *".PHP_EOL; | |
433 echo "\t\t\t=> ".$exclude->asXML().PHP_EOL; | |
434 } | |
435 | |
436 continue; | |
437 } | |
438 | |
439 if ($this->shouldProcessElement($exclude) === false) { | |
440 continue; | |
441 } | |
442 | |
443 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
444 echo str_repeat("\t", $depth); | |
445 echo "\t\tExcluding rule \"".$exclude['name'].'"'.PHP_EOL; | |
446 } | |
447 | |
448 // Check if a single code is being excluded, which is a shortcut | |
449 // for setting the severity of the message to 0. | |
450 $parts = explode('.', $exclude['name']); | |
451 if (count($parts) === 4) { | |
452 $this->ruleset[(string) $exclude['name']]['severity'] = 0; | |
453 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
454 echo str_repeat("\t", $depth); | |
455 echo "\t\t=> severity set to 0".PHP_EOL; | |
456 } | |
457 } else { | |
458 $excludedSniffs = array_merge( | |
459 $excludedSniffs, | |
460 $this->expandRulesetReference($exclude['name'], $rulesetDir, ($depth + 1)) | |
461 ); | |
462 } | |
463 }//end foreach | |
464 }//end if | |
465 | |
466 $this->processRule($rule, $newSniffs, $depth); | |
467 }//end foreach | |
468 | |
469 // Process custom command line arguments. | |
470 $cliArgs = []; | |
471 foreach ($ruleset->{'arg'} as $arg) { | |
472 if ($this->shouldProcessElement($arg) === false) { | |
473 continue; | |
474 } | |
475 | |
476 if (isset($arg['name']) === true) { | |
477 $argString = '--'.(string) $arg['name']; | |
478 if (isset($arg['value']) === true) { | |
479 $argString .= '='.(string) $arg['value']; | |
480 } | |
481 } else { | |
482 $argString = '-'.(string) $arg['value']; | |
483 } | |
484 | |
485 $cliArgs[] = $argString; | |
486 | |
487 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
488 echo str_repeat("\t", $depth); | |
489 echo "\t=> set command line value $argString".PHP_EOL; | |
490 } | |
491 }//end foreach | |
492 | |
493 // Set custom php ini values as CLI args. | |
494 foreach ($ruleset->{'ini'} as $arg) { | |
495 if ($this->shouldProcessElement($arg) === false) { | |
496 continue; | |
497 } | |
498 | |
499 if (isset($arg['name']) === false) { | |
500 continue; | |
501 } | |
502 | |
503 $name = (string) $arg['name']; | |
504 $argString = $name; | |
505 if (isset($arg['value']) === true) { | |
506 $value = (string) $arg['value']; | |
507 $argString .= "=$value"; | |
508 } else { | |
509 $value = 'true'; | |
510 } | |
511 | |
512 $cliArgs[] = '-d'; | |
513 $cliArgs[] = $argString; | |
514 | |
515 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
516 echo str_repeat("\t", $depth); | |
517 echo "\t=> set PHP ini value $name to $value".PHP_EOL; | |
518 } | |
519 }//end foreach | |
520 | |
521 if (empty($this->config->files) === true) { | |
522 // Process hard-coded file paths. | |
523 foreach ($ruleset->{'file'} as $file) { | |
524 $file = (string) $file; | |
525 $cliArgs[] = $file; | |
526 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
527 echo str_repeat("\t", $depth); | |
528 echo "\t=> added \"$file\" to the file list".PHP_EOL; | |
529 } | |
530 } | |
531 } | |
532 | |
533 if (empty($cliArgs) === false) { | |
534 // Change the directory so all relative paths are worked | |
535 // out based on the location of the ruleset instead of | |
536 // the location of the user. | |
537 $inPhar = Util\Common::isPharFile($rulesetDir); | |
538 if ($inPhar === false) { | |
539 $currentDir = getcwd(); | |
540 chdir($rulesetDir); | |
541 } | |
542 | |
543 $this->config->setCommandLineValues($cliArgs); | |
544 | |
545 if ($inPhar === false) { | |
546 chdir($currentDir); | |
547 } | |
548 } | |
549 | |
550 // Process custom ignore pattern rules. | |
551 foreach ($ruleset->{'exclude-pattern'} as $pattern) { | |
552 if ($this->shouldProcessElement($pattern) === false) { | |
553 continue; | |
554 } | |
555 | |
556 if (isset($pattern['type']) === false) { | |
557 $pattern['type'] = 'absolute'; | |
558 } | |
559 | |
560 $this->ignorePatterns[(string) $pattern] = (string) $pattern['type']; | |
561 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
562 echo str_repeat("\t", $depth); | |
563 echo "\t=> added global ".(string) $pattern['type'].' ignore pattern: '.(string) $pattern.PHP_EOL; | |
564 } | |
565 } | |
566 | |
567 $includedSniffs = array_unique(array_merge($ownSniffs, $includedSniffs)); | |
568 $excludedSniffs = array_unique($excludedSniffs); | |
569 | |
570 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
571 $included = count($includedSniffs); | |
572 $excluded = count($excludedSniffs); | |
573 echo str_repeat("\t", $depth); | |
574 echo "=> Ruleset processing complete; included $included sniffs and excluded $excluded".PHP_EOL; | |
575 } | |
576 | |
577 // Merge our own sniff list with our externally included | |
578 // sniff list, but filter out any excluded sniffs. | |
579 $files = []; | |
580 foreach ($includedSniffs as $sniff) { | |
581 if (in_array($sniff, $excludedSniffs) === true) { | |
582 continue; | |
583 } else { | |
584 $files[] = Util\Common::realpath($sniff); | |
585 } | |
586 } | |
587 | |
588 return $files; | |
589 | |
590 }//end processRuleset() | |
591 | |
592 | |
593 /** | |
594 * Expands a directory into a list of sniff files within. | |
595 * | |
596 * @param string $directory The path to a directory. | |
597 * @param int $depth How many nested processing steps we are in. This | |
598 * is only used for debug output. | |
599 * | |
600 * @return array | |
601 */ | |
602 private function expandSniffDirectory($directory, $depth=0) | |
603 { | |
604 $sniffs = []; | |
605 | |
606 $rdi = new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS); | |
607 $di = new \RecursiveIteratorIterator($rdi, 0, \RecursiveIteratorIterator::CATCH_GET_CHILD); | |
608 | |
609 $dirLen = strlen($directory); | |
610 | |
611 foreach ($di as $file) { | |
612 $filename = $file->getFilename(); | |
613 | |
614 // Skip hidden files. | |
615 if (substr($filename, 0, 1) === '.') { | |
616 continue; | |
617 } | |
618 | |
619 // We are only interested in PHP and sniff files. | |
620 $fileParts = explode('.', $filename); | |
621 if (array_pop($fileParts) !== 'php') { | |
622 continue; | |
623 } | |
624 | |
625 $basename = basename($filename, '.php'); | |
626 if (substr($basename, -5) !== 'Sniff') { | |
627 continue; | |
628 } | |
629 | |
630 $path = $file->getPathname(); | |
631 | |
632 // Skip files in hidden directories within the Sniffs directory of this | |
633 // standard. We use the offset with strpos() to allow hidden directories | |
634 // before, valid example: | |
635 // /home/foo/.composer/vendor/squiz/custom_tool/MyStandard/Sniffs/... | |
636 if (strpos($path, DIRECTORY_SEPARATOR.'.', $dirLen) !== false) { | |
637 continue; | |
638 } | |
639 | |
640 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
641 echo str_repeat("\t", $depth); | |
642 echo "\t\t=> ".Util\Common::stripBasepath($path, $this->config->basepath).PHP_EOL; | |
643 } | |
644 | |
645 $sniffs[] = $path; | |
646 }//end foreach | |
647 | |
648 return $sniffs; | |
649 | |
650 }//end expandSniffDirectory() | |
651 | |
652 | |
653 /** | |
654 * Expands a ruleset reference into a list of sniff files. | |
655 * | |
656 * @param string $ref The reference from the ruleset XML file. | |
657 * @param string $rulesetDir The directory of the ruleset XML file, used to | |
658 * evaluate relative paths. | |
659 * @param int $depth How many nested processing steps we are in. This | |
660 * is only used for debug output. | |
661 * | |
662 * @return array | |
663 * @throws RuntimeException If the reference is invalid. | |
664 */ | |
665 private function expandRulesetReference($ref, $rulesetDir, $depth=0) | |
666 { | |
667 // Ignore internal sniffs codes as they are used to only | |
668 // hide and change internal messages. | |
669 if (substr($ref, 0, 9) === 'Internal.') { | |
670 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
671 echo str_repeat("\t", $depth); | |
672 echo "\t\t* ignoring internal sniff code *".PHP_EOL; | |
673 } | |
674 | |
675 return []; | |
676 } | |
677 | |
678 // As sniffs can't begin with a full stop, assume references in | |
679 // this format are relative paths and attempt to convert them | |
680 // to absolute paths. If this fails, let the reference run through | |
681 // the normal checks and have it fail as normal. | |
682 if (substr($ref, 0, 1) === '.') { | |
683 $realpath = Util\Common::realpath($rulesetDir.'/'.$ref); | |
684 if ($realpath !== false) { | |
685 $ref = $realpath; | |
686 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
687 echo str_repeat("\t", $depth); | |
688 echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL; | |
689 } | |
690 } | |
691 } | |
692 | |
693 // As sniffs can't begin with a tilde, assume references in | |
694 // this format are relative to the user's home directory. | |
695 if (substr($ref, 0, 2) === '~/') { | |
696 $realpath = Util\Common::realpath($ref); | |
697 if ($realpath !== false) { | |
698 $ref = $realpath; | |
699 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
700 echo str_repeat("\t", $depth); | |
701 echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL; | |
702 } | |
703 } | |
704 } | |
705 | |
706 if (is_file($ref) === true) { | |
707 if (substr($ref, -9) === 'Sniff.php') { | |
708 // A single external sniff. | |
709 $this->rulesetDirs[] = dirname(dirname(dirname($ref))); | |
710 return [$ref]; | |
711 } | |
712 } else { | |
713 // See if this is a whole standard being referenced. | |
714 $path = Util\Standards::getInstalledStandardPath($ref); | |
715 if (Util\Common::isPharFile($path) === true && strpos($path, 'ruleset.xml') === false) { | |
716 // If the ruleset exists inside the phar file, use it. | |
717 if (file_exists($path.DIRECTORY_SEPARATOR.'ruleset.xml') === true) { | |
718 $path = $path.DIRECTORY_SEPARATOR.'ruleset.xml'; | |
719 } else { | |
720 $path = null; | |
721 } | |
722 } | |
723 | |
724 if ($path !== null) { | |
725 $ref = $path; | |
726 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
727 echo str_repeat("\t", $depth); | |
728 echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL; | |
729 } | |
730 } else if (is_dir($ref) === false) { | |
731 // Work out the sniff path. | |
732 $sepPos = strpos($ref, DIRECTORY_SEPARATOR); | |
733 if ($sepPos !== false) { | |
734 $stdName = substr($ref, 0, $sepPos); | |
735 $path = substr($ref, $sepPos); | |
736 } else { | |
737 $parts = explode('.', $ref); | |
738 $stdName = $parts[0]; | |
739 if (count($parts) === 1) { | |
740 // A whole standard? | |
741 $path = ''; | |
742 } else if (count($parts) === 2) { | |
743 // A directory of sniffs? | |
744 $path = DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$parts[1]; | |
745 } else { | |
746 // A single sniff? | |
747 $path = DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$parts[1].DIRECTORY_SEPARATOR.$parts[2].'Sniff.php'; | |
748 } | |
749 } | |
750 | |
751 $newRef = false; | |
752 $stdPath = Util\Standards::getInstalledStandardPath($stdName); | |
753 if ($stdPath !== null && $path !== '') { | |
754 if (Util\Common::isPharFile($stdPath) === true | |
755 && strpos($stdPath, 'ruleset.xml') === false | |
756 ) { | |
757 // Phar files can only return the directory, | |
758 // since ruleset can be omitted if building one standard. | |
759 $newRef = Util\Common::realpath($stdPath.$path); | |
760 } else { | |
761 $newRef = Util\Common::realpath(dirname($stdPath).$path); | |
762 } | |
763 } | |
764 | |
765 if ($newRef === false) { | |
766 // The sniff is not locally installed, so check if it is being | |
767 // referenced as a remote sniff outside the install. We do this | |
768 // by looking through all directories where we have found ruleset | |
769 // files before, looking for ones for this particular standard, | |
770 // and seeing if it is in there. | |
771 foreach ($this->rulesetDirs as $dir) { | |
772 if (strtolower(basename($dir)) !== strtolower($stdName)) { | |
773 continue; | |
774 } | |
775 | |
776 $newRef = Util\Common::realpath($dir.$path); | |
777 | |
778 if ($newRef !== false) { | |
779 $ref = $newRef; | |
780 } | |
781 } | |
782 } else { | |
783 $ref = $newRef; | |
784 } | |
785 | |
786 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
787 echo str_repeat("\t", $depth); | |
788 echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL; | |
789 } | |
790 }//end if | |
791 }//end if | |
792 | |
793 if (is_dir($ref) === true) { | |
794 if (is_file($ref.DIRECTORY_SEPARATOR.'ruleset.xml') === true) { | |
795 // We are referencing an external coding standard. | |
796 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
797 echo str_repeat("\t", $depth); | |
798 echo "\t\t* rule is referencing a standard using directory name; processing *".PHP_EOL; | |
799 } | |
800 | |
801 return $this->processRuleset($ref.DIRECTORY_SEPARATOR.'ruleset.xml', ($depth + 2)); | |
802 } else { | |
803 // We are referencing a whole directory of sniffs. | |
804 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
805 echo str_repeat("\t", $depth); | |
806 echo "\t\t* rule is referencing a directory of sniffs *".PHP_EOL; | |
807 echo str_repeat("\t", $depth); | |
808 echo "\t\tAdding sniff files from directory".PHP_EOL; | |
809 } | |
810 | |
811 return $this->expandSniffDirectory($ref, ($depth + 1)); | |
812 } | |
813 } else { | |
814 if (is_file($ref) === false) { | |
815 $error = "Referenced sniff \"$ref\" does not exist"; | |
816 throw new RuntimeException($error); | |
817 } | |
818 | |
819 if (substr($ref, -9) === 'Sniff.php') { | |
820 // A single sniff. | |
821 return [$ref]; | |
822 } else { | |
823 // Assume an external ruleset.xml file. | |
824 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
825 echo str_repeat("\t", $depth); | |
826 echo "\t\t* rule is referencing a standard using ruleset path; processing *".PHP_EOL; | |
827 } | |
828 | |
829 return $this->processRuleset($ref, ($depth + 2)); | |
830 } | |
831 }//end if | |
832 | |
833 }//end expandRulesetReference() | |
834 | |
835 | |
836 /** | |
837 * Processes a rule from a ruleset XML file, overriding built-in defaults. | |
838 * | |
839 * @param SimpleXMLElement $rule The rule object from a ruleset XML file. | |
840 * @param string[] $newSniffs An array of sniffs that got included by this rule. | |
841 * @param int $depth How many nested processing steps we are in. | |
842 * This is only used for debug output. | |
843 * | |
844 * @return void | |
845 * @throws RuntimeException If rule settings are invalid. | |
846 */ | |
847 private function processRule($rule, $newSniffs, $depth=0) | |
848 { | |
849 $ref = (string) $rule['ref']; | |
850 $todo = [$ref]; | |
851 | |
852 $parts = explode('.', $ref); | |
853 if (count($parts) <= 2) { | |
854 // We are processing a standard or a category of sniffs. | |
855 foreach ($newSniffs as $sniffFile) { | |
856 $parts = explode(DIRECTORY_SEPARATOR, $sniffFile); | |
857 $sniffName = array_pop($parts); | |
858 $sniffCategory = array_pop($parts); | |
859 array_pop($parts); | |
860 $sniffStandard = array_pop($parts); | |
861 $todo[] = $sniffStandard.'.'.$sniffCategory.'.'.substr($sniffName, 0, -9); | |
862 } | |
863 } | |
864 | |
865 foreach ($todo as $code) { | |
866 // Custom severity. | |
867 if (isset($rule->severity) === true | |
868 && $this->shouldProcessElement($rule->severity) === true | |
869 ) { | |
870 if (isset($this->ruleset[$code]) === false) { | |
871 $this->ruleset[$code] = []; | |
872 } | |
873 | |
874 $this->ruleset[$code]['severity'] = (int) $rule->severity; | |
875 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
876 echo str_repeat("\t", $depth); | |
877 echo "\t\t=> severity set to ".(int) $rule->severity; | |
878 if ($code !== $ref) { | |
879 echo " for $code"; | |
880 } | |
881 | |
882 echo PHP_EOL; | |
883 } | |
884 } | |
885 | |
886 // Custom message type. | |
887 if (isset($rule->type) === true | |
888 && $this->shouldProcessElement($rule->type) === true | |
889 ) { | |
890 if (isset($this->ruleset[$code]) === false) { | |
891 $this->ruleset[$code] = []; | |
892 } | |
893 | |
894 $type = strtolower((string) $rule->type); | |
895 if ($type !== 'error' && $type !== 'warning') { | |
896 throw new RuntimeException("Message type \"$type\" is invalid; must be \"error\" or \"warning\""); | |
897 } | |
898 | |
899 $this->ruleset[$code]['type'] = $type; | |
900 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
901 echo str_repeat("\t", $depth); | |
902 echo "\t\t=> message type set to ".(string) $rule->type; | |
903 if ($code !== $ref) { | |
904 echo " for $code"; | |
905 } | |
906 | |
907 echo PHP_EOL; | |
908 } | |
909 }//end if | |
910 | |
911 // Custom message. | |
912 if (isset($rule->message) === true | |
913 && $this->shouldProcessElement($rule->message) === true | |
914 ) { | |
915 if (isset($this->ruleset[$code]) === false) { | |
916 $this->ruleset[$code] = []; | |
917 } | |
918 | |
919 $this->ruleset[$code]['message'] = (string) $rule->message; | |
920 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
921 echo str_repeat("\t", $depth); | |
922 echo "\t\t=> message set to ".(string) $rule->message; | |
923 if ($code !== $ref) { | |
924 echo " for $code"; | |
925 } | |
926 | |
927 echo PHP_EOL; | |
928 } | |
929 } | |
930 | |
931 // Custom properties. | |
932 if (isset($rule->properties) === true | |
933 && $this->shouldProcessElement($rule->properties) === true | |
934 ) { | |
935 foreach ($rule->properties->property as $prop) { | |
936 if ($this->shouldProcessElement($prop) === false) { | |
937 continue; | |
938 } | |
939 | |
940 if (isset($this->ruleset[$code]) === false) { | |
941 $this->ruleset[$code] = [ | |
942 'properties' => [], | |
943 ]; | |
944 } else if (isset($this->ruleset[$code]['properties']) === false) { | |
945 $this->ruleset[$code]['properties'] = []; | |
946 } | |
947 | |
948 $name = (string) $prop['name']; | |
949 if (isset($prop['type']) === true | |
950 && (string) $prop['type'] === 'array' | |
951 ) { | |
952 $values = []; | |
953 if (isset($prop['extend']) === true | |
954 && (string) $prop['extend'] === 'true' | |
955 && isset($this->ruleset[$code]['properties'][$name]) === true | |
956 ) { | |
957 $values = $this->ruleset[$code]['properties'][$name]; | |
958 } | |
959 | |
960 if (isset($prop->element) === true) { | |
961 $printValue = ''; | |
962 foreach ($prop->element as $element) { | |
963 if ($this->shouldProcessElement($element) === false) { | |
964 continue; | |
965 } | |
966 | |
967 $value = (string) $element['value']; | |
968 if (isset($element['key']) === true) { | |
969 $key = (string) $element['key']; | |
970 $values[$key] = $value; | |
971 $printValue .= $key.'=>'.$value.','; | |
972 } else { | |
973 $values[] = $value; | |
974 $printValue .= $value.','; | |
975 } | |
976 } | |
977 | |
978 $printValue = rtrim($printValue, ','); | |
979 } else { | |
980 $value = (string) $prop['value']; | |
981 $printValue = $value; | |
982 foreach (explode(',', $value) as $val) { | |
983 list($k, $v) = explode('=>', $val.'=>'); | |
984 if ($v !== '') { | |
985 $values[trim($k)] = trim($v); | |
986 } else { | |
987 $values[] = trim($k); | |
988 } | |
989 } | |
990 }//end if | |
991 | |
992 $this->ruleset[$code]['properties'][$name] = $values; | |
993 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
994 echo str_repeat("\t", $depth); | |
995 echo "\t\t=> array property \"$name\" set to \"$printValue\""; | |
996 if ($code !== $ref) { | |
997 echo " for $code"; | |
998 } | |
999 | |
1000 echo PHP_EOL; | |
1001 } | |
1002 } else { | |
1003 $this->ruleset[$code]['properties'][$name] = (string) $prop['value']; | |
1004 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1005 echo str_repeat("\t", $depth); | |
1006 echo "\t\t=> property \"$name\" set to \"".(string) $prop['value'].'"'; | |
1007 if ($code !== $ref) { | |
1008 echo " for $code"; | |
1009 } | |
1010 | |
1011 echo PHP_EOL; | |
1012 } | |
1013 }//end if | |
1014 }//end foreach | |
1015 }//end if | |
1016 | |
1017 // Ignore patterns. | |
1018 foreach ($rule->{'exclude-pattern'} as $pattern) { | |
1019 if ($this->shouldProcessElement($pattern) === false) { | |
1020 continue; | |
1021 } | |
1022 | |
1023 if (isset($this->ignorePatterns[$code]) === false) { | |
1024 $this->ignorePatterns[$code] = []; | |
1025 } | |
1026 | |
1027 if (isset($pattern['type']) === false) { | |
1028 $pattern['type'] = 'absolute'; | |
1029 } | |
1030 | |
1031 $this->ignorePatterns[$code][(string) $pattern] = (string) $pattern['type']; | |
1032 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1033 echo str_repeat("\t", $depth); | |
1034 echo "\t\t=> added rule-specific ".(string) $pattern['type'].' ignore pattern'; | |
1035 if ($code !== $ref) { | |
1036 echo " for $code"; | |
1037 } | |
1038 | |
1039 echo ': '.(string) $pattern.PHP_EOL; | |
1040 } | |
1041 }//end foreach | |
1042 | |
1043 // Include patterns. | |
1044 foreach ($rule->{'include-pattern'} as $pattern) { | |
1045 if ($this->shouldProcessElement($pattern) === false) { | |
1046 continue; | |
1047 } | |
1048 | |
1049 if (isset($this->includePatterns[$code]) === false) { | |
1050 $this->includePatterns[$code] = []; | |
1051 } | |
1052 | |
1053 if (isset($pattern['type']) === false) { | |
1054 $pattern['type'] = 'absolute'; | |
1055 } | |
1056 | |
1057 $this->includePatterns[$code][(string) $pattern] = (string) $pattern['type']; | |
1058 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1059 echo str_repeat("\t", $depth); | |
1060 echo "\t\t=> added rule-specific ".(string) $pattern['type'].' include pattern'; | |
1061 if ($code !== $ref) { | |
1062 echo " for $code"; | |
1063 } | |
1064 | |
1065 echo ': '.(string) $pattern.PHP_EOL; | |
1066 } | |
1067 }//end foreach | |
1068 }//end foreach | |
1069 | |
1070 }//end processRule() | |
1071 | |
1072 | |
1073 /** | |
1074 * Determine if an element should be processed or ignored. | |
1075 * | |
1076 * @param SimpleXMLElement $element An object from a ruleset XML file. | |
1077 * | |
1078 * @return bool | |
1079 */ | |
1080 private function shouldProcessElement($element) | |
1081 { | |
1082 if (isset($element['phpcbf-only']) === false | |
1083 && isset($element['phpcs-only']) === false | |
1084 ) { | |
1085 // No exceptions are being made. | |
1086 return true; | |
1087 } | |
1088 | |
1089 if (PHP_CODESNIFFER_CBF === true | |
1090 && isset($element['phpcbf-only']) === true | |
1091 && (string) $element['phpcbf-only'] === 'true' | |
1092 ) { | |
1093 return true; | |
1094 } | |
1095 | |
1096 if (PHP_CODESNIFFER_CBF === false | |
1097 && isset($element['phpcs-only']) === true | |
1098 && (string) $element['phpcs-only'] === 'true' | |
1099 ) { | |
1100 return true; | |
1101 } | |
1102 | |
1103 return false; | |
1104 | |
1105 }//end shouldProcessElement() | |
1106 | |
1107 | |
1108 /** | |
1109 * Loads and stores sniffs objects used for sniffing files. | |
1110 * | |
1111 * @param array $files Paths to the sniff files to register. | |
1112 * @param array $restrictions The sniff class names to restrict the allowed | |
1113 * listeners to. | |
1114 * @param array $exclusions The sniff class names to exclude from the | |
1115 * listeners list. | |
1116 * | |
1117 * @return void | |
1118 */ | |
1119 public function registerSniffs($files, $restrictions, $exclusions) | |
1120 { | |
1121 $listeners = []; | |
1122 | |
1123 foreach ($files as $file) { | |
1124 // Work out where the position of /StandardName/Sniffs/... is | |
1125 // so we can determine what the class will be called. | |
1126 $sniffPos = strrpos($file, DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR); | |
1127 if ($sniffPos === false) { | |
1128 continue; | |
1129 } | |
1130 | |
1131 $slashPos = strrpos(substr($file, 0, $sniffPos), DIRECTORY_SEPARATOR); | |
1132 if ($slashPos === false) { | |
1133 continue; | |
1134 } | |
1135 | |
1136 $className = Autoload::loadFile($file); | |
1137 $compareName = Util\Common::cleanSniffClass($className); | |
1138 | |
1139 // If they have specified a list of sniffs to restrict to, check | |
1140 // to see if this sniff is allowed. | |
1141 if (empty($restrictions) === false | |
1142 && isset($restrictions[$compareName]) === false | |
1143 ) { | |
1144 continue; | |
1145 } | |
1146 | |
1147 // If they have specified a list of sniffs to exclude, check | |
1148 // to see if this sniff is allowed. | |
1149 if (empty($exclusions) === false | |
1150 && isset($exclusions[$compareName]) === true | |
1151 ) { | |
1152 continue; | |
1153 } | |
1154 | |
1155 // Skip abstract classes. | |
1156 $reflection = new \ReflectionClass($className); | |
1157 if ($reflection->isAbstract() === true) { | |
1158 continue; | |
1159 } | |
1160 | |
1161 $listeners[$className] = $className; | |
1162 | |
1163 if (PHP_CODESNIFFER_VERBOSITY > 2) { | |
1164 echo "Registered $className".PHP_EOL; | |
1165 } | |
1166 }//end foreach | |
1167 | |
1168 $this->sniffs = $listeners; | |
1169 | |
1170 }//end registerSniffs() | |
1171 | |
1172 | |
1173 /** | |
1174 * Populates the array of PHP_CodeSniffer_Sniff's for this file. | |
1175 * | |
1176 * @return void | |
1177 * @throws RuntimeException If sniff registration fails. | |
1178 */ | |
1179 public function populateTokenListeners() | |
1180 { | |
1181 // Construct a list of listeners indexed by token being listened for. | |
1182 $this->tokenListeners = []; | |
1183 | |
1184 foreach ($this->sniffs as $sniffClass => $sniffObject) { | |
1185 $this->sniffs[$sniffClass] = null; | |
1186 $this->sniffs[$sniffClass] = new $sniffClass(); | |
1187 | |
1188 $sniffCode = Util\Common::getSniffCode($sniffClass); | |
1189 $this->sniffCodes[$sniffCode] = $sniffClass; | |
1190 | |
1191 // Set custom properties. | |
1192 if (isset($this->ruleset[$sniffCode]['properties']) === true) { | |
1193 foreach ($this->ruleset[$sniffCode]['properties'] as $name => $value) { | |
1194 $this->setSniffProperty($sniffClass, $name, $value); | |
1195 } | |
1196 } | |
1197 | |
1198 $tokenizers = []; | |
1199 $vars = get_class_vars($sniffClass); | |
1200 if (isset($vars['supportedTokenizers']) === true) { | |
1201 foreach ($vars['supportedTokenizers'] as $tokenizer) { | |
1202 $tokenizers[$tokenizer] = $tokenizer; | |
1203 } | |
1204 } else { | |
1205 $tokenizers = ['PHP' => 'PHP']; | |
1206 } | |
1207 | |
1208 $tokens = $this->sniffs[$sniffClass]->register(); | |
1209 if (is_array($tokens) === false) { | |
1210 $msg = "Sniff $sniffClass register() method must return an array"; | |
1211 throw new RuntimeException($msg); | |
1212 } | |
1213 | |
1214 $ignorePatterns = []; | |
1215 $patterns = $this->getIgnorePatterns($sniffCode); | |
1216 foreach ($patterns as $pattern => $type) { | |
1217 $replacements = [ | |
1218 '\\,' => ',', | |
1219 '*' => '.*', | |
1220 ]; | |
1221 | |
1222 $ignorePatterns[] = strtr($pattern, $replacements); | |
1223 } | |
1224 | |
1225 $includePatterns = []; | |
1226 $patterns = $this->getIncludePatterns($sniffCode); | |
1227 foreach ($patterns as $pattern => $type) { | |
1228 $replacements = [ | |
1229 '\\,' => ',', | |
1230 '*' => '.*', | |
1231 ]; | |
1232 | |
1233 $includePatterns[] = strtr($pattern, $replacements); | |
1234 } | |
1235 | |
1236 foreach ($tokens as $token) { | |
1237 if (isset($this->tokenListeners[$token]) === false) { | |
1238 $this->tokenListeners[$token] = []; | |
1239 } | |
1240 | |
1241 if (isset($this->tokenListeners[$token][$sniffClass]) === false) { | |
1242 $this->tokenListeners[$token][$sniffClass] = [ | |
1243 'class' => $sniffClass, | |
1244 'source' => $sniffCode, | |
1245 'tokenizers' => $tokenizers, | |
1246 'ignore' => $ignorePatterns, | |
1247 'include' => $includePatterns, | |
1248 ]; | |
1249 } | |
1250 } | |
1251 }//end foreach | |
1252 | |
1253 }//end populateTokenListeners() | |
1254 | |
1255 | |
1256 /** | |
1257 * Set a single property for a sniff. | |
1258 * | |
1259 * @param string $sniffClass The class name of the sniff. | |
1260 * @param string $name The name of the property to change. | |
1261 * @param string $value The new value of the property. | |
1262 * | |
1263 * @return void | |
1264 */ | |
1265 public function setSniffProperty($sniffClass, $name, $value) | |
1266 { | |
1267 // Setting a property for a sniff we are not using. | |
1268 if (isset($this->sniffs[$sniffClass]) === false) { | |
1269 return; | |
1270 } | |
1271 | |
1272 $name = trim($name); | |
1273 if (is_string($value) === true) { | |
1274 $value = trim($value); | |
1275 } | |
1276 | |
1277 if ($value === '') { | |
1278 $value = null; | |
1279 } | |
1280 | |
1281 // Special case for booleans. | |
1282 if ($value === 'true') { | |
1283 $value = true; | |
1284 } else if ($value === 'false') { | |
1285 $value = false; | |
1286 } else if (substr($name, -2) === '[]') { | |
1287 $name = substr($name, 0, -2); | |
1288 $values = []; | |
1289 if ($value !== null) { | |
1290 foreach (explode(',', $value) as $val) { | |
1291 list($k, $v) = explode('=>', $val.'=>'); | |
1292 if ($v !== '') { | |
1293 $values[trim($k)] = trim($v); | |
1294 } else { | |
1295 $values[] = trim($k); | |
1296 } | |
1297 } | |
1298 } | |
1299 | |
1300 $value = $values; | |
1301 } | |
1302 | |
1303 $this->sniffs[$sniffClass]->$name = $value; | |
1304 | |
1305 }//end setSniffProperty() | |
1306 | |
1307 | |
1308 /** | |
1309 * Gets the array of ignore patterns. | |
1310 * | |
1311 * Optionally takes a listener to get ignore patterns specified | |
1312 * for that sniff only. | |
1313 * | |
1314 * @param string $listener The listener to get patterns for. If NULL, all | |
1315 * patterns are returned. | |
1316 * | |
1317 * @return array | |
1318 */ | |
1319 public function getIgnorePatterns($listener=null) | |
1320 { | |
1321 if ($listener === null) { | |
1322 return $this->ignorePatterns; | |
1323 } | |
1324 | |
1325 if (isset($this->ignorePatterns[$listener]) === true) { | |
1326 return $this->ignorePatterns[$listener]; | |
1327 } | |
1328 | |
1329 return []; | |
1330 | |
1331 }//end getIgnorePatterns() | |
1332 | |
1333 | |
1334 /** | |
1335 * Gets the array of include patterns. | |
1336 * | |
1337 * Optionally takes a listener to get include patterns specified | |
1338 * for that sniff only. | |
1339 * | |
1340 * @param string $listener The listener to get patterns for. If NULL, all | |
1341 * patterns are returned. | |
1342 * | |
1343 * @return array | |
1344 */ | |
1345 public function getIncludePatterns($listener=null) | |
1346 { | |
1347 if ($listener === null) { | |
1348 return $this->includePatterns; | |
1349 } | |
1350 | |
1351 if (isset($this->includePatterns[$listener]) === true) { | |
1352 return $this->includePatterns[$listener]; | |
1353 } | |
1354 | |
1355 return []; | |
1356 | |
1357 }//end getIncludePatterns() | |
1358 | |
1359 | |
1360 }//end class |