output ?: $this->getDefaultOutput($settings); $phpExecutable = PhpExecutable::getPhpExecutable($settings->phpExecutable); $olderThanPhp54 = $phpExecutable->getVersionId() < 50400; // From PHP version 5.4 are tokens translated by default $translateTokens = $phpExecutable->isIsHhvmType() || $olderThanPhp54; $output->writeHeader($phpExecutable->getVersionId(), $settings->parallelJobs, $phpExecutable->getHhvmVersion()); $files = $this->getFilesFromPaths($settings->paths, $settings->extensions, $settings->excluded); if (empty($files)) { throw new Exception('No file found to check.'); } $output->setTotalFileCount(count($files)); $parallelLint = new ParallelLint($phpExecutable, $settings->parallelJobs); $parallelLint->setAspTagsEnabled($settings->aspTags); $parallelLint->setShortTagEnabled($settings->shortTag); $parallelLint->setShowDeprecated($settings->showDeprecated); $parallelLint->setSyntaxErrorCallback($this->createSyntaxErrorCallback($settings)); $parallelLint->setProcessCallback(function ($status, $file) use ($output) { if ($status === ParallelLint::STATUS_OK) { $output->ok(); } else if ($status === ParallelLint::STATUS_SKIP) { $output->skip(); } else if ($status === ParallelLint::STATUS_ERROR) { $output->error(); } else { $output->fail(); } }); $result = $parallelLint->lint($files); if ($settings->blame) { $this->gitBlame($result, $settings); } $output->writeResult($result, new ErrorFormatter($settings->colors, $translateTokens), $settings->ignoreFails); return $result; } /** * @param Output $output */ public function setOutput(Output $output) { $this->output = $output; } /** * @param Settings $settings * @return Output */ protected function getDefaultOutput(Settings $settings) { $writer = new ConsoleWriter; switch ($settings->format) { case Settings::FORMAT_JSON: return new JsonOutput($writer); case Settings::FORMAT_GITLAB: return new GitLabOutput($writer); case Settings::FORMAT_CHECKSTYLE: return new CheckstyleOutput($writer); } if ($settings->colors === Settings::DISABLED) { $output = new TextOutput($writer); } else { $output = new TextOutputColored($writer, $settings->colors); } $output->showProgress = $settings->showProgress; return $output; } /** * @param Result $result * @param Settings $settings * @throws Exception */ protected function gitBlame(Result $result, Settings $settings) { if (!GitBlameProcess::gitExists($settings->gitExecutable)) { return; } foreach ($result->getErrors() as $error) { if ($error instanceof SyntaxError) { $process = new GitBlameProcess($settings->gitExecutable, $error->getFilePath(), $error->getLine()); $process->waitForFinish(); if ($process->isSuccess()) { $blame = new Blame; $blame->name = $process->getAuthor(); $blame->email = $process->getAuthorEmail(); $blame->datetime = $process->getAuthorTime(); $blame->commitHash = $process->getCommitHash(); $blame->summary = $process->getSummary(); $error->setBlame($blame); } } } } /** * @param array $paths * @param array $extensions * @param array $excluded * @return array * @throws NotExistsPathException */ protected function getFilesFromPaths(array $paths, array $extensions, array $excluded = array()) { $extensions = array_map('preg_quote', $extensions, array_fill(0, count($extensions), '`')); $regex = count($extensions) ? '`\.(?:' . implode('|', $extensions) . ')$`iD' : '`.+`iD'; $files = array(); foreach ($paths as $path) { if (is_file($path)) { $files[] = $path; } else if (is_dir($path)) { $iterator = new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS); if (!empty($excluded)) { $iterator = new RecursiveDirectoryFilterIterator($iterator, $excluded); } $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD ); $iterator = new \RegexIterator($iterator, $regex); /** @var \SplFileInfo[] $iterator */ foreach ($iterator as $directoryFile) { $files[] = (string) $directoryFile; } } else { throw new NotExistsPathException($path); } } $files = array_unique($files); return $files; } protected function createSyntaxErrorCallback(Settings $settings) { if ($settings->syntaxErrorCallbackFile === null) { return null; } $fullFilePath = realpath($settings->syntaxErrorCallbackFile); if ($fullFilePath === false) { throw new NotExistsPathException($settings->syntaxErrorCallbackFile); } require_once $fullFilePath; $expectedClassName = basename($fullFilePath, '.php'); if (!class_exists($expectedClassName)) { throw new NotExistsClassException($expectedClassName, $settings->syntaxErrorCallbackFile); } $callbackInstance = new $expectedClassName; if (!($callbackInstance instanceof SyntaxErrorCallback)) { throw new NotImplementCallbackException($expectedClassName); } return $callbackInstance; } } class RecursiveDirectoryFilterIterator extends \RecursiveFilterIterator { /** @var \RecursiveDirectoryIterator */ private $iterator; /** @var array */ private $excluded = array(); /** * @param \RecursiveDirectoryIterator $iterator * @param array $excluded */ public function __construct(\RecursiveDirectoryIterator $iterator, array $excluded) { parent::__construct($iterator); $this->iterator = $iterator; $this->excluded = array_map(array($this, 'getPathname'), $excluded); } /** * (PHP 5 >= 5.1.0)
* Check whether the current element of the iterator is acceptable * * @link http://php.net/manual/en/filteriterator.accept.php * @return bool true if the current element is acceptable, otherwise false. */ #[ReturnTypeWillChange] public function accept() { $current = $this->current()->getPathname(); $current = $this->normalizeDirectorySeparator($current); if ('.' . DIRECTORY_SEPARATOR !== $current[0] . $current[1]) { $current = '.' . DIRECTORY_SEPARATOR . $current; } return !in_array($current, $this->excluded); } /** * (PHP 5 >= 5.1.0)
* Check whether the inner iterator's current element has children * * @link http://php.net/manual/en/recursivefilteriterator.haschildren.php * @return bool true if the inner iterator has children, otherwise false */ #[ReturnTypeWillChange] public function hasChildren() { return $this->iterator->hasChildren(); } /** * (PHP 5 >= 5.1.0)
* Return the inner iterator's children contained in a RecursiveFilterIterator * * @link http://php.net/manual/en/recursivefilteriterator.getchildren.php * @return \RecursiveFilterIterator containing the inner iterator's children. */ #[ReturnTypeWillChange] public function getChildren() { return new self($this->iterator->getChildren(), $this->excluded); } /** * @param string $file * @return string */ private function getPathname($file) { $file = $this->normalizeDirectorySeparator($file); if ('.' . DIRECTORY_SEPARATOR !== $file[0] . $file[1]) { $file = '.' . DIRECTORY_SEPARATOR . $file; } $directoryFile = new \SplFileInfo($file); return $directoryFile->getPathname(); } /** * @param string $file * @return string */ private function normalizeDirectorySeparator($file) { return str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $file); } }