/** * Expands a ruleset reference into a list of sniff files. * * @param string $ref The reference from the ruleset XML file. * @param string $rulesetDir The directory of the ruleset XML file, used to * evaluate relative paths. * @param int $depth How many nested processing steps we are in. This * is only used for debug output. * * @return array * @throws RuntimeException If the reference is invalid. */ private function expandRulesetReference($ref, $rulesetDir, $depth = 0) { // Ignore internal sniffs codes as they are used to only // hide and change internal messages. if (substr($ref, 0, 9) === 'Internal.') { if (PHP_CODESNIFFER_VERBOSITY > 1) { echo str_repeat("\t", $depth); echo "\t\t* ignoring internal sniff code *" . PHP_EOL; } return array(); } // As sniffs can't begin with a full stop, assume references in // this format are relative paths and attempt to convert them // to absolute paths. If this fails, let the reference run through // the normal checks and have it fail as normal. if (substr($ref, 0, 1) === '.') { $realpath = Util\Common::realpath($rulesetDir . '/' . $ref); if ($realpath !== false) { $ref = $realpath; if (PHP_CODESNIFFER_VERBOSITY > 1) { echo str_repeat("\t", $depth); echo "\t\t=> " . Util\Common::stripBasepath($ref, $this->config->basepath) . PHP_EOL; } } } // As sniffs can't begin with a tilde, assume references in // this format are relative to the user's home directory. if (substr($ref, 0, 2) === '~/') { $realpath = Util\Common::realpath($ref); if ($realpath !== false) { $ref = $realpath; if (PHP_CODESNIFFER_VERBOSITY > 1) { echo str_repeat("\t", $depth); echo "\t\t=> " . Util\Common::stripBasepath($ref, $this->config->basepath) . PHP_EOL; } } } if (is_file($ref) === true) { if (substr($ref, -9) === 'Sniff.php') { // A single external sniff. $this->rulesetDirs[] = dirname(dirname(dirname($ref))); return array($ref); } } else { // See if this is a whole standard being referenced. $path = Util\Standards::getInstalledStandardPath($ref); if (Util\Common::isPharFile($path) === true && strpos($path, 'ruleset.xml') === false) { // If the ruleset exists inside the phar file, use it. if (file_exists($path . DIRECTORY_SEPARATOR . 'ruleset.xml') === true) { $path = $path . DIRECTORY_SEPARATOR . 'ruleset.xml'; } else { $path = null; } } if ($path !== null) { $ref = $path; if (PHP_CODESNIFFER_VERBOSITY > 1) { echo str_repeat("\t", $depth); echo "\t\t=> " . Util\Common::stripBasepath($ref, $this->config->basepath) . PHP_EOL; } } else { if (is_dir($ref) === false) { // Work out the sniff path. $sepPos = strpos($ref, DIRECTORY_SEPARATOR); if ($sepPos !== false) { $stdName = substr($ref, 0, $sepPos); $path = substr($ref, $sepPos); } else { $parts = explode('.', $ref); $stdName = $parts[0]; if (count($parts) === 1) { // A whole standard? $path = ''; } else { if (count($parts) === 2) { // A directory of sniffs? $path = DIRECTORY_SEPARATOR . 'Sniffs' . DIRECTORY_SEPARATOR . $parts[1]; } else { // A single sniff? $path = DIRECTORY_SEPARATOR . 'Sniffs' . DIRECTORY_SEPARATOR . $parts[1] . DIRECTORY_SEPARATOR . $parts[2] . 'Sniff.php'; } } } $newRef = false; $stdPath = Util\Standards::getInstalledStandardPath($stdName); if ($stdPath !== null && $path !== '') { if (Util\Common::isPharFile($stdPath) === true && strpos($stdPath, 'ruleset.xml') === false) { // Phar files can only return the directory, // since ruleset can be omitted if building one standard. $newRef = Util\Common::realpath($stdPath . $path); } else { $newRef = Util\Common::realpath(dirname($stdPath) . $path); } } if ($newRef === false) { // The sniff is not locally installed, so check if it is being // referenced as a remote sniff outside the install. We do this // by looking through all directories where we have found ruleset // files before, looking for ones for this particular standard, // and seeing if it is in there. foreach ($this->rulesetDirs as $dir) { if (strtolower(basename($dir)) !== strtolower($stdName)) { continue; } $newRef = Util\Common::realpath($dir . $path); if ($newRef !== false) { $ref = $newRef; } } } else { $ref = $newRef; } if (PHP_CODESNIFFER_VERBOSITY > 1) { echo str_repeat("\t", $depth); echo "\t\t=> " . Util\Common::stripBasepath($ref, $this->config->basepath) . PHP_EOL; } } } //end if } //end if if (is_dir($ref) === true) { if (is_file($ref . DIRECTORY_SEPARATOR . 'ruleset.xml') === true) { // We are referencing an external coding standard. if (PHP_CODESNIFFER_VERBOSITY > 1) { echo str_repeat("\t", $depth); echo "\t\t* rule is referencing a standard using directory name; processing *" . PHP_EOL; } return $this->processRuleset($ref . DIRECTORY_SEPARATOR . 'ruleset.xml', $depth + 2); } else { // We are referencing a whole directory of sniffs. if (PHP_CODESNIFFER_VERBOSITY > 1) { echo str_repeat("\t", $depth); echo "\t\t* rule is referencing a directory of sniffs *" . PHP_EOL; echo str_repeat("\t", $depth); echo "\t\tAdding sniff files from directory" . PHP_EOL; } return $this->expandSniffDirectory($ref, $depth + 1); } } else { if (is_file($ref) === false) { $error = "Referenced sniff \"{$ref}\" does not exist"; throw new RuntimeException($error); } if (substr($ref, -9) === 'Sniff.php') { // A single sniff. return array($ref); } else { // Assume an external ruleset.xml file. if (PHP_CODESNIFFER_VERBOSITY > 1) { echo str_repeat("\t", $depth); echo "\t\t* rule is referencing a standard using ruleset path; processing *" . PHP_EOL; } return $this->processRuleset($ref, $depth + 2); } } //end if }