/** * Constructs a doc generator. * * @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run. * * @see generate() */ public function __construct(Ruleset $ruleset) { $this->ruleset = $ruleset; $standardFiles = array(); foreach ($ruleset->sniffs as $className => $sniffClass) { $file = Autoload::getLoadedFileName($className); $docFile = str_replace(DIRECTORY_SEPARATOR . 'Sniffs' . DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR . 'Docs' . DIRECTORY_SEPARATOR, $file); $docFile = str_replace('Sniff.php', 'Standard.xml', $docFile); if (is_file($docFile) === true) { $this->docFiles[] = $docFile; } } }
/** * Loads existing cache data for the run, if any. * * @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run. * @param \PHP_CodeSniffer\Config $config The config data for the run. * * @return void */ public static function load(Ruleset $ruleset, Config $config) { // Look at every loaded sniff class so far and use their file contents // to generate a hash for the code used during the run. // At this point, the loaded class list contains the core PHPCS code // and all sniffs that have been loaded as part of the run. if (PHP_CODESNIFFER_VERBOSITY > 1) { echo PHP_EOL . "\tGenerating loaded file list for code hash" . PHP_EOL; } $codeHash = ''; $classes = array_keys(Autoload::getLoadedClasses()); sort($classes); $installDir = dirname(__DIR__); $installDirLen = strlen($installDir); $standardDir = $installDir . DIRECTORY_SEPARATOR . 'Standards'; $standardDirLen = strlen($standardDir); foreach ($classes as $file) { if (substr($file, 0, $standardDirLen) !== $standardDir) { if (substr($file, 0, $installDirLen) === $installDir) { // We are only interested in sniffs here. continue; } if (PHP_CODESNIFFER_VERBOSITY > 1) { echo "\t\t=> external file: {$file}" . PHP_EOL; } } else { if (PHP_CODESNIFFER_VERBOSITY > 1) { echo "\t\t=> internal sniff: {$file}" . PHP_EOL; } } $codeHash .= md5_file($file); } // Add the content of the used rulesets to the hash so that sniff setting // changes in the ruleset invalidate the cache. $rulesets = $ruleset->paths; sort($rulesets); foreach ($rulesets as $file) { if (substr($file, 0, $standardDirLen) !== $standardDir) { if (PHP_CODESNIFFER_VERBOSITY > 1) { echo "\t\t=> external ruleset: {$file}" . PHP_EOL; } } else { if (PHP_CODESNIFFER_VERBOSITY > 1) { echo "\t\t=> internal ruleset: {$file}" . PHP_EOL; } } $codeHash .= md5_file($file); } // Go through the core PHPCS code and add those files to the file // hash. This ensures that core PHPCS changes will also invalidate the cache. // Note that we ignore sniffs here, and any files that don't affect // the outcome of the run. $di = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($installDir), 0, \RecursiveIteratorIterator::CATCH_GET_CHILD); $di = new \RecursiveDirectoryIterator($installDir); $filter = new \RecursiveCallbackFilterIterator($di, function ($file, $key, $iterator) { // Skip hidden files. $filename = $file->getFilename(); if (substr($filename, 0, 1) === '.') { return false; } $filePath = Common::realpath($file->getPathname()); if ($filePath === false) { return false; } if (is_dir($filePath) === true && ($filename === 'Standards' || $filename === 'Exceptions' || $filename === 'Reports' || $filename === 'Generators')) { return false; } return true; }); $iterator = new \RecursiveIteratorIterator($filter); foreach ($iterator as $file) { if (PHP_CODESNIFFER_VERBOSITY > 1) { echo "\t\t=> core file: {$file}" . PHP_EOL; } $codeHash .= md5_file($file); } $codeHash = md5($codeHash); // Along with the code hash, use various settings that can affect // the results of a run to create a new hash. This hash will be used // in the cache file name. $rulesetHash = md5(var_export($ruleset->ignorePatterns, true) . var_export($ruleset->includePatterns, true)); $configData = array('tabWidth' => $config->tabWidth, 'encoding' => $config->encoding, 'recordErrors' => $config->recordErrors, 'codeHash' => $codeHash, 'rulesetHash' => $rulesetHash); $configString = implode(',', $configData); $cacheHash = substr(sha1($configString), 0, 12); if (PHP_CODESNIFFER_VERBOSITY > 1) { echo "\tGenerating cache key data" . PHP_EOL; echo "\t\t=> tabWidth: " . $configData['tabWidth'] . PHP_EOL; echo "\t\t=> encoding: " . $configData['encoding'] . PHP_EOL; echo "\t\t=> recordErrors: " . (int) $configData['recordErrors'] . PHP_EOL; echo "\t\t=> codeHash: " . $configData['codeHash'] . PHP_EOL; echo "\t\t=> rulesetHash: " . $configData['rulesetHash'] . PHP_EOL; echo "\t\t=> cacheHash: {$cacheHash}" . PHP_EOL; } if ($config->cacheFile !== null) { $cacheFile = $config->cacheFile; } else { // Determine the common paths for all files being checked. // We can use this to locate an existing cache file, or to // determine where to create a new one. if (PHP_CODESNIFFER_VERBOSITY > 1) { echo "\tChecking possible cache file paths" . PHP_EOL; } $paths = array(); foreach ($config->files as $file) { $file = Common::realpath($file); while ($file !== DIRECTORY_SEPARATOR) { if (isset($paths[$file]) === false) { $paths[$file] = 1; } else { $paths[$file]++; } $lastFile = $file; $file = dirname($file); if ($file === $lastFile) { // Just in case something went wrong, // we don't want to end up in an infinite loop. break; } } } ksort($paths); $paths = array_reverse($paths); $numFiles = count($config->files); $tmpDir = sys_get_temp_dir(); $cacheFile = null; foreach ($paths as $file => $count) { if ($count !== $numFiles) { unset($paths[$file]); continue; } $fileHash = substr(sha1($file), 0, 12); $testFile = $tmpDir . DIRECTORY_SEPARATOR . "phpcs.{$fileHash}.{$cacheHash}.cache"; if ($cacheFile === null) { // This will be our default location if we can't find // an existing file. $cacheFile = $testFile; } if (PHP_CODESNIFFER_VERBOSITY > 1) { echo "\t\t=> {$testFile}" . PHP_EOL; echo "\t\t\t * based on shared location: {$file} *" . PHP_EOL; } if (file_exists($testFile) === true) { $cacheFile = $testFile; break; } } //end foreach if ($cacheFile === null) { // Unlikely, but just in case $paths is empty for some reason. $cacheFile = $tmpDir . DIRECTORY_SEPARATOR . "phpcs.{$cacheHash}.cache"; } } //end if self::$path = $cacheFile; if (PHP_CODESNIFFER_VERBOSITY > 1) { echo "\t=> Using cache file: " . self::$path . PHP_EOL; } if (file_exists(self::$path) === true) { self::$cache = json_decode(file_get_contents(self::$path), true); // Verify the contents of the cache file. if (self::$cache['config'] !== $configData) { self::$cache = array(); if (PHP_CODESNIFFER_VERBOSITY > 1) { echo "\t* cache was invalid and has been cleared *" . PHP_EOL; } } } else { if (PHP_CODESNIFFER_VERBOSITY > 1) { echo "\t* cache file does not exist *" . PHP_EOL; } } self::$cache['config'] = $configData; }
/** * Loads and stores sniffs objects used for sniffing files. * * @param array $files Paths to the sniff files to register. * @param array $restrictions The sniff class names to restrict the allowed * listeners to. * @param array $exclusions The sniff class names to exclude from the * listeners list. * * @return void */ public function registerSniffs($files, $restrictions, $exclusions) { $listeners = array(); foreach ($files as $file) { // Work out where the position of /StandardName/Sniffs/... is // so we can determine what the class will be called. $sniffPos = strrpos($file, DIRECTORY_SEPARATOR . 'Sniffs' . DIRECTORY_SEPARATOR); if ($sniffPos === false) { continue; } $slashPos = strrpos(substr($file, 0, $sniffPos), DIRECTORY_SEPARATOR); if ($slashPos === false) { continue; } $className = Autoload::loadFile($file); // If they have specified a list of sniffs to restrict to, check // to see if this sniff is allowed. if (empty($restrictions) === false && isset($restrictions[strtolower($className)]) === false) { continue; } // If they have specified a list of sniffs to exclude, check // to see if this sniff is allowed. if (empty($exclusions) === false && isset($exclusions[strtolower($className)]) === true) { continue; } // Skip abstract classes. $reflection = new \ReflectionClass($className); if ($reflection->isAbstract() === true) { continue; } $listeners[$className] = $className; if (PHP_CODESNIFFER_VERBOSITY > 2) { echo "Registered {$className}" . PHP_EOL; } } //end foreach $this->sniffs = $listeners; }
/** * Initialise the reporter. * * All reports specified in the config will be created and their * output file (or a temp file if none is specified) initialised by * clearing the current contents. * * @param \PHP_CodeSniffer\Config $config The config data for the run. * * @return void * @throws RuntimeException If a report is not available. */ public function __construct(Config $config) { $this->config = $config; foreach ($config->reports as $type => $output) { $type = ucfirst($type); if ($output === null) { $output = $config->reportFile; } if (strpos($type, '.') !== false) { // This is a path to a custom report class. $filename = realpath($type); if ($filename === false) { echo "ERROR: Custom report \"{$type}\" not found" . PHP_EOL; exit(3); } $reportClassName = Autoload::loadFile($filename); } else { $reportClassName = 'PHP_CodeSniffer\\Reports\\' . $type; } $reportClass = new $reportClassName(); if (false === $reportClass instanceof Report) { throw new RuntimeException('Class "' . $reportClassName . '" must implement the "PHP_CodeSniffer\\Report" interface.'); } $this->reports[$type] = array('output' => $output, 'class' => $reportClass); if ($output === null) { // Using a temp file. $this->tmpFiles[$type] = tempnam(sys_get_temp_dir(), 'phpcs'); file_put_contents($this->tmpFiles[$type], ''); } else { file_put_contents($output, ''); } } //end foreach }
/** * Get the class name of the filter being used for the run. * * @return string */ private function getFilterClass() { $filterType = $this->config->filter; if ($filterType === null) { $filterClass = '\\PHP_CodeSniffer\\Filters\\Filter'; } else { if (strpos($filterType, '.') !== false) { // This is a path to a custom filter class. $filename = realpath($filterType); if ($filename === false) { echo "ERROR: Custom filter \"{$filterType}\" not found" . PHP_EOL; exit(3); } $filterClass = \PHP_CodeSniffer\Autoload::loadFile($filename); } else { $filterClass = '\\PHP_CodeSniffer\\Filters\\' . $filterType; } } return $filterClass; }