Phan\Phan::analyzeFileList PHP Méthode

analyzeFileList() public static méthode

Analyze the given set of files and emit any issues found to STDOUT.
See also: Phan\CodeBase
public static analyzeFileList ( CodeBase $code_base, array $file_path_list ) : boolean
$code_base CodeBase A code base needs to be passed in because we require it to be initialized before any classes or files are loaded.
$file_path_list array A list of files to scan
Résultat boolean We emit messages to the configured printer and return true if issues were found.
    public static function analyzeFileList(CodeBase $code_base, array $file_path_list) : bool
    {
        $file_count = count($file_path_list);
        // We'll construct a set of files that we'll
        // want to run an analysis on
        $analyze_file_path_list = [];
        if (Config::get()->consistent_hashing_file_order) {
            // Parse the files in lexicographic order.
            // If there are duplicate class/function definitions,
            // this ensures they are added to the maps in the same order.
            sort($file_path_list, SORT_STRING);
        }
        // This first pass parses code and populates the
        // global state we'll need for doing a second
        // analysis after.
        foreach ($file_path_list as $i => $file_path) {
            CLI::progress('parse', ($i + 1) / $file_count);
            // Kick out anything we read from the former version
            // of this file
            $code_base->flushDependenciesForFile($file_path);
            // If the file is gone, no need to continue
            if (($real = realpath($file_path)) === false || !file_exists($real)) {
                continue;
            }
            try {
                // Parse the file
                Analysis::parseFile($code_base, $file_path);
                // Save this to the set of files to analyze
                $analyze_file_path_list[] = $file_path;
            } catch (\Throwable $throwable) {
                error_log($file_path . ' ' . $throwable->getMessage() . "\n");
            }
        }
        // Don't continue on to analysis if the user has
        // chosen to just dump the AST
        if (Config::get()->dump_ast) {
            exit(EXIT_SUCCESS);
        }
        if (is_string(Config::get()->dump_signatures_file)) {
            exit(self::dumpSignaturesToFile($code_base, Config::get()->dump_signatures_file));
        }
        // With parsing complete, we need to tell the code base to
        // start hydrating any requested elements on their way out.
        // Hydration expands class types, imports parent methods,
        // properties, etc., and does stuff like that.
        //
        // This is an optimization that saves us a significant
        // amount of time on very large code bases. Instead of
        // hydrating all classes, we only hydrate the things we
        // actually need. When running as multiple processes this
        // lets us only need to do hydrate a subset of classes.
        $code_base->setShouldHydrateRequestedElements(true);
        // Take a pass over all functions verifying
        // various states now that we have the whole
        // state in memory
        Analysis::analyzeClasses($code_base);
        // Take a pass over all functions verifying
        // various states now that we have the whole
        // state in memory
        Analysis::analyzeFunctions($code_base);
        // Filter out any files that are to be excluded from
        // analysis
        $analyze_file_path_list = array_filter($analyze_file_path_list, function ($file_path) {
            return !self::isExcludedAnalysisFile($file_path);
        });
        // Get the count of all files we're going to analyze
        $file_count = count($analyze_file_path_list);
        // Prevent an ugly failure if we have no files to
        // analyze.
        if (0 == $file_count) {
            return false;
        }
        // Get a map from process_id to the set of files that
        // the given process should analyze in a stable order
        $process_file_list_map = (new Ordering($code_base))->orderForProcessCount(Config::get()->processes, $analyze_file_path_list);
        // This worker takes a file and analyzes it
        $analysis_worker = function ($i, $file_path) use($file_count, $code_base) {
            CLI::progress('analyze', ($i + 1) / $file_count);
            Analysis::analyzeFile($code_base, $file_path);
        };
        // Determine how many processes we're running on. This may be
        // less than the provided number if the files are bunched up
        // excessively.
        $process_count = count($process_file_list_map);
        assert($process_count > 0 && $process_count <= Config::get()->processes, "The process count must be between 1 and the given number of processes. After mapping files to cores, {$process_count} process were set to be used.");
        // Check to see if we're running as multiple processes
        // or not
        if ($process_count > 1) {
            // Run analysis one file at a time, splitting the set of
            // files up among a given number of child processes.
            $pool = new ForkPool($process_file_list_map, function () {
                // Remove any issues that were collected prior to forking
                // to prevent duplicate issues in the output.
                self::getIssueCollector()->reset();
            }, $analysis_worker, function () {
                // Return the collected issues to be serialized.
                return self::getIssueCollector()->getCollectedIssues();
            });
            // Wait for all tasks to complete and collect the results.
            self::collectSerializedResults($pool->wait());
        } else {
            // Get the task data from the 0th processor
            $analyze_file_path_list = array_values($process_file_list_map)[0];
            // If we're not running as multiple processes, just iterate
            // over the file list and analyze them
            foreach ($analyze_file_path_list as $i => $file_path) {
                $analysis_worker($i, $file_path);
            }
            // Scan through all globally accessible elements
            // in the code base and emit errors for dead
            // code.
            Analysis::analyzeDeadCode($code_base);
        }
        // Get a count of the number of issues that were found
        $is_issue_found = 0 !== count(self::$issueCollector->getCollectedIssues());
        // Collect all issues, blocking
        self::display();
        return $is_issue_found;
    }

Usage Example

Exemple #1
0
 /**
  * This reads all files in `tests/files/src`, runs
  * the analyzer on each and compares the output
  * to the files's counterpart in
  * `tests/files/expected`
  *
  * @param string[] $test_file_list
  * @param string $expected_file_path
  * @dataProvider getTestFiles
  */
 public function testFiles($test_file_list, $expected_file_path)
 {
     $expected_output = '';
     if (is_file($expected_file_path)) {
         // Read the expected output
         $expected_output = trim(file_get_contents($expected_file_path));
     }
     $stream = new BufferedOutput();
     $printer = new PlainTextPrinter();
     $printer->configureOutput($stream);
     Phan::setPrinter($printer);
     Phan::setIssueCollector(new BufferingCollector());
     Phan::analyzeFileList($this->code_base, $test_file_list);
     $output = $stream->fetch();
     // Uncomment to save the output back to the expected
     // output. This should be done for error message
     // text changes and only if you promise to be careful.
     /*
     $saved_output = $output;
     $test_file_elements= explode('/', $test_file_list[0]);
     $test_file_name = array_pop($test_file_elements);
     $saved_output = preg_replace('/[^ :\n]*\/' . $test_file_name . '/', '%s', $saved_output);
     $saved_output = preg_replace('/closure_[^\(]*\(/', 'closure_%s(', $saved_output);
     if (!empty($saved_output) && strlen($saved_output) > 0) {
         $saved_output .= "\n";
     }
     file_put_contents($expected_file_path, $saved_output);
     $expected_output =
         trim(file_get_contents($expected_file_path));
     */
     $wanted_re = preg_replace('/\\r\\n/', "\n", $expected_output);
     // do preg_quote, but miss out any %r delimited sections
     $temp = "";
     $r = "%r";
     $startOffset = 0;
     $length = strlen($wanted_re);
     while ($startOffset < $length) {
         $start = strpos($wanted_re, $r, $startOffset);
         if ($start !== false) {
             // we have found a start tag
             $end = strpos($wanted_re, $r, $start + 2);
             if ($end === false) {
                 // unbalanced tag, ignore it.
                 $end = $start = $length;
             }
         } else {
             // no more %r sections
             $start = $end = $length;
         }
         // quote a non re portion of the string
         $temp = $temp . preg_quote(substr($wanted_re, $startOffset, $start - $startOffset), '/');
         // add the re unquoted.
         if ($end > $start) {
             $temp = $temp . '(' . substr($wanted_re, $start + 2, $end - $start - 2) . ')';
         }
         $startOffset = $end + 2;
     }
     $wanted_re = $temp;
     $wanted_re = str_replace(['%binary_string_optional%'], 'string', $wanted_re);
     $wanted_re = str_replace(['%unicode_string_optional%'], 'string', $wanted_re);
     $wanted_re = str_replace(['%unicode\\|string%', '%string\\|unicode%'], 'string', $wanted_re);
     $wanted_re = str_replace(['%u\\|b%', '%b\\|u%'], '', $wanted_re);
     // Stick to basics
     $wanted_re = str_replace('%e', '\\' . DIRECTORY_SEPARATOR, $wanted_re);
     $wanted_re = str_replace('%s', '[^\\r\\n]+', $wanted_re);
     $wanted_re = str_replace('%S', '[^\\r\\n]*', $wanted_re);
     $wanted_re = str_replace('%a', '.+', $wanted_re);
     $wanted_re = str_replace('%A', '.*', $wanted_re);
     $wanted_re = str_replace('%w', '\\s*', $wanted_re);
     $wanted_re = str_replace('%i', '[+-]?\\d+', $wanted_re);
     $wanted_re = str_replace('%d', '\\d+', $wanted_re);
     $wanted_re = str_replace('%x', '[0-9a-fA-F]+', $wanted_re);
     $wanted_re = str_replace('%f', '[+-]?\\.?\\d+\\.?\\d*(?:[Ee][+-]?\\d+)?', $wanted_re);
     $wanted_re = str_replace('%c', '.', $wanted_re);
     // %f allows two points "-.0.0" but that is the best *simple* expression
     $this->assertRegExp("/^{$wanted_re}\$/", $output, "Unexpected output in {$test_file_list[0]}");
 }
All Usage Examples Of Phan\Phan::analyzeFileList