Chris@17: Chris@17: * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) Chris@17: * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence Chris@17: */ Chris@17: Chris@17: namespace PHP_CodeSniffer\Files; Chris@17: Chris@17: use PHP_CodeSniffer\Ruleset; Chris@17: use PHP_CodeSniffer\Config; Chris@17: use PHP_CodeSniffer\Util\Cache; Chris@17: Chris@17: class LocalFile extends File Chris@17: { Chris@17: Chris@17: Chris@17: /** Chris@17: * Creates a LocalFile object and sets the content. Chris@17: * Chris@17: * @param string $path The absolute path to the file. Chris@17: * @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run. Chris@17: * @param \PHP_CodeSniffer\Config $config The config data for the run. Chris@17: * Chris@17: * @return void Chris@17: */ Chris@17: public function __construct($path, Ruleset $ruleset, Config $config) Chris@17: { Chris@17: $this->path = trim($path); Chris@17: if (is_readable($this->path) === false) { Chris@17: parent::__construct($this->path, $ruleset, $config); Chris@17: $error = 'Error opening file; file no longer exists or you do not have access to read the file'; Chris@17: $this->addMessage(true, $error, 1, 1, 'Internal.LocalFile', [], 5, false); Chris@17: $this->ignored = true; Chris@17: return; Chris@17: } Chris@17: Chris@17: // Before we go and spend time tokenizing this file, just check Chris@17: // to see if there is a tag up top to indicate that the whole Chris@17: // file should be ignored. It must be on one of the first two lines. Chris@17: if ($config->annotations === true) { Chris@17: $handle = fopen($this->path, 'r'); Chris@17: if ($handle !== false) { Chris@17: $firstContent = fgets($handle); Chris@17: $firstContent .= fgets($handle); Chris@17: fclose($handle); Chris@17: Chris@17: if (strpos($firstContent, '@codingStandardsIgnoreFile') !== false Chris@17: || stripos($firstContent, 'phpcs:ignorefile') !== false Chris@17: ) { Chris@17: // We are ignoring the whole file. Chris@17: $this->ignored = true; Chris@17: return; Chris@17: } Chris@17: } Chris@17: } Chris@17: Chris@17: $this->reloadContent(); Chris@17: Chris@18: parent::__construct($this->path, $ruleset, $config); Chris@17: Chris@17: }//end __construct() Chris@17: Chris@17: Chris@17: /** Chris@17: * Loads the latest version of the file's content from the file system. Chris@17: * Chris@17: * @return void Chris@17: */ Chris@17: public function reloadContent() Chris@17: { Chris@17: $this->setContent(file_get_contents($this->path)); Chris@17: Chris@17: }//end reloadContent() Chris@17: Chris@17: Chris@17: /** Chris@17: * Processes the file. Chris@17: * Chris@17: * @return void Chris@17: */ Chris@17: public function process() Chris@17: { Chris@17: if ($this->ignored === true) { Chris@17: return; Chris@17: } Chris@17: Chris@17: if ($this->configCache['cache'] === false) { Chris@18: parent::process(); Chris@18: return; Chris@17: } Chris@17: Chris@17: $hash = md5_file($this->path); Chris@17: $cache = Cache::get($this->path); Chris@17: if ($cache !== false && $cache['hash'] === $hash) { Chris@17: // We can't filter metrics, so just load all of them. Chris@17: $this->metrics = $cache['metrics']; Chris@17: Chris@17: if ($this->configCache['recordErrors'] === true) { Chris@17: // Replay the cached errors and warnings to filter out the ones Chris@17: // we don't need for this specific run. Chris@17: $this->configCache['cache'] = false; Chris@17: $this->replayErrors($cache['errors'], $cache['warnings']); Chris@17: $this->configCache['cache'] = true; Chris@17: } else { Chris@17: $this->errorCount = $cache['errorCount']; Chris@17: $this->warningCount = $cache['warningCount']; Chris@17: $this->fixableCount = $cache['fixableCount']; Chris@17: } Chris@17: Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 0 Chris@17: || (PHP_CODESNIFFER_CBF === true && empty($this->config->files) === false) Chris@17: ) { Chris@17: echo "[loaded from cache]... "; Chris@17: } Chris@17: Chris@17: $this->numTokens = $cache['numTokens']; Chris@17: $this->fromCache = true; Chris@17: return; Chris@17: }//end if Chris@17: Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: echo PHP_EOL; Chris@17: } Chris@17: Chris@17: parent::process(); Chris@17: Chris@17: $cache = [ Chris@17: 'hash' => $hash, Chris@17: 'errors' => $this->errors, Chris@17: 'warnings' => $this->warnings, Chris@17: 'metrics' => $this->metrics, Chris@17: 'errorCount' => $this->errorCount, Chris@17: 'warningCount' => $this->warningCount, Chris@17: 'fixableCount' => $this->fixableCount, Chris@17: 'numTokens' => $this->numTokens, Chris@17: ]; Chris@17: Chris@17: Cache::set($this->path, $cache); Chris@17: Chris@17: // During caching, we don't filter out errors in any way, so Chris@17: // we need to do that manually now by replaying them. Chris@17: if ($this->configCache['recordErrors'] === true) { Chris@17: $this->configCache['cache'] = false; Chris@17: $this->replayErrors($this->errors, $this->warnings); Chris@17: $this->configCache['cache'] = true; Chris@17: } Chris@17: Chris@17: }//end process() Chris@17: Chris@17: Chris@17: /** Chris@17: * Clears and replays error and warnings for the file. Chris@17: * Chris@17: * Replaying errors and warnings allows for filtering rules to be changed Chris@17: * and then errors and warnings to be reapplied with the new rules. This is Chris@17: * particularly useful while caching. Chris@17: * Chris@17: * @param array $errors The list of errors to replay. Chris@17: * @param array $warnings The list of warnings to replay. Chris@17: * Chris@17: * @return void Chris@17: */ Chris@17: private function replayErrors($errors, $warnings) Chris@17: { Chris@17: $this->errors = []; Chris@17: $this->warnings = []; Chris@17: $this->errorCount = 0; Chris@17: $this->warningCount = 0; Chris@17: $this->fixableCount = 0; Chris@17: Chris@17: foreach ($errors as $line => $lineErrors) { Chris@17: foreach ($lineErrors as $column => $colErrors) { Chris@17: foreach ($colErrors as $error) { Chris@17: $this->activeListener = $error['listener']; Chris@17: $this->addMessage( Chris@17: true, Chris@17: $error['message'], Chris@17: $line, Chris@17: $column, Chris@17: $error['source'], Chris@17: [], Chris@17: $error['severity'], Chris@17: $error['fixable'] Chris@17: ); Chris@17: } Chris@17: } Chris@17: } Chris@17: Chris@17: foreach ($warnings as $line => $lineErrors) { Chris@17: foreach ($lineErrors as $column => $colErrors) { Chris@17: foreach ($colErrors as $error) { Chris@17: $this->activeListener = $error['listener']; Chris@17: $this->addMessage( Chris@17: false, Chris@17: $error['message'], Chris@17: $line, Chris@17: $column, Chris@17: $error['source'], Chris@17: [], Chris@17: $error['severity'], Chris@17: $error['fixable'] Chris@17: ); Chris@17: } Chris@17: } Chris@17: } Chris@17: Chris@17: }//end replayErrors() Chris@17: Chris@17: Chris@17: }//end class