public function testParser() { if (!PhutilXHPASTBinary::isAvailable()) { try { PhutilXHPASTBinary::build(); } catch (Exception $ex) { $this->assertSkipped(pht('%s is not built or not up to date.', 'xhpast')); } } $dir = dirname(__FILE__) . '/data/'; foreach (Filesystem::listDirectory($dir) as $file) { if (preg_match('/\\.test$/', $file)) { $this->executeParserTest($file, Filesystem::readFile($dir . $file)); } } }
public function scan() { $output = $this->output; if (!\PhutilXHPASTBinary::isAvailable()) { $output->writeln(\PhutilXHPASTBinary::getBuildInstructions(), $output::OUTPUT_NORMAL); exit; } $base_path = $this->basePath; $php_files = $this->allFiles($base_path); $total_files = count($php_files); $this->output->writeln("{$total_files} files to scan"); /** @type \Generator $split */ $split = $this->chunk($php_files, ceil($total_files / $this->workers)); foreach ($split as $chunk) { $this->forkProcess($chunk); } foreach ($this->processes as $pID) { pcntl_waitpid($pID, $status); } }
public function getHighlightFuture($language, $source) { if ($language === null) { $language = PhutilLanguageGuesser::guessLanguage($source); } $have_pygments = !empty($this->config['pygments.enabled']); if ($language == 'php' && PhutilXHPASTBinary::isAvailable()) { return id(new PhutilXHPASTSyntaxHighlighter())->getHighlightFuture($source); } if ($language == 'console') { return id(new PhutilConsoleSyntaxHighlighter())->getHighlightFuture($source); } if ($language == 'diviner' || $language == 'remarkup') { return id(new PhutilDivinerSyntaxHighlighter())->getHighlightFuture($source); } if ($language == 'rainbow') { return id(new PhutilRainbowSyntaxHighlighter())->getHighlightFuture($source); } if ($language == 'php') { return id(new PhutilLexerSyntaxHighlighter())->setConfig('lexer', new PhutilPHPFragmentLexer())->setConfig('language', 'php')->getHighlightFuture($source); } if ($language == 'py') { return id(new PhutilLexerSyntaxHighlighter())->setConfig('lexer', new PhutilPythonFragmentLexer())->setConfig('language', 'py')->getHighlightFuture($source); } if ($language == 'json') { return id(new PhutilLexerSyntaxHighlighter())->setConfig('lexer', new PhutilJSONFragmentLexer())->getHighlightFuture($source); } if ($language == 'invisible') { return id(new PhutilInvisibleSyntaxHighlighter())->getHighlightFuture($source); } // Don't invoke Pygments for plain text, since it's expensive and has // no effect. if ($language !== 'text' && $language !== 'txt') { if ($have_pygments) { return id(new PhutilPygmentsSyntaxHighlighter())->setConfig('language', $language)->getHighlightFuture($source); } } return id(new PhutilDefaultSyntaxHighlighter())->getHighlightFuture($source); }
/** * Analyze the library, generating the file and symbol maps. * * @return void */ private function analyzeLibrary() { // Identify all the ".php" source files in the library. $this->log(pht('Finding source files...')); $source_map = $this->loadSourceFileMap(); $this->log(pht('Found %s files.', new PhutilNumber(count($source_map)))); // Load the symbol cache with existing parsed symbols. This allows us // to remap libraries quickly by analyzing only changed files. $this->log(pht('Loading symbol cache...')); $symbol_cache = $this->loadSymbolCache(); // If the XHPAST binary is not up-to-date, build it now. Otherwise, // `phutil_symbols.php` will attempt to build the binary and will fail // miserably because it will be trying to build the same file multiple // times in parallel. if (!PhutilXHPASTBinary::isAvailable()) { PhutilXHPASTBinary::build(); } // Build out the symbol analysis for all the files in the library. For // each file, check if it's in cache. If we miss in the cache, do a fresh // analysis. $symbol_map = array(); $futures = array(); foreach ($source_map as $file => $hash) { if (!empty($symbol_cache[$hash])) { $symbol_map[$file] = $symbol_cache[$hash]; continue; } $futures[$file] = $this->buildSymbolAnalysisFuture($file); } $this->log(pht('Found %s files in cache.', new PhutilNumber(count($symbol_map)))); // Run the analyzer on any files which need analysis. if ($futures) { $limit = $this->subprocessLimit; $count = number_format(count($futures)); $this->log(pht('Analyzing %d files with %d subprocesses...', $count, $limit)); $progress = new PhutilConsoleProgressBar(); if ($this->quiet) { $progress->setQuiet(true); } $progress->setTotal(count($futures)); $futures = id(new FutureIterator($futures))->limit($limit); foreach ($futures as $file => $future) { $result = $future->resolveJSON(); if (empty($result['error'])) { $symbol_map[$file] = $result; } else { $progress->done(false); throw new XHPASTSyntaxErrorException($result['line'], $file . ': ' . $result['error']); } $progress->update(1); } $progress->done(); } $this->fileSymbolMap = $symbol_map; // We're done building the cache, so write it out immediately. Note that // we've only retained entries for files we found, so this implicitly cleans // out old cache entries. $this->writeSymbolCache($symbol_map, $source_map); // Our map is up to date, so either show it on stdout or write it to disk. $this->log(pht('Building library map...')); $this->librarySymbolMap = $this->buildLibraryMap($symbol_map); }