Phan\CLI::__construct PHP Method

__construct() public method

Create and read command line arguments, configuring \Phan\Config as a side effect.
public __construct ( )
    public function __construct()
    {
        global $argv;
        // Parse command line args
        // still available: g,n,t,u,v,w
        $opts = getopt("f:m:o:c:k:aeqbr:pid:s:3:y:l:xj:zh::", ['backward-compatibility-checks', 'dead-code-detection', 'directory:', 'dump-ast', 'dump-signatures-file:', 'exclude-directory-list:', 'exclude-file:', 'file-list-only:', 'file-list:', 'help', 'ignore-undeclared', 'minimum-severity:', 'output-mode:', 'output:', 'parent-constructor-required:', 'progress-bar', 'project-root-directory:', 'quick', 'state-file:', 'processes:', 'config-file:', 'signature-compatibility', 'markdown-issue-messages']);
        // Determine the root directory of the project from which
        // we root all relative paths passed in as args
        Config::get()->setProjectRootDirectory($opts['d'] ?? $opts['project-root-directory'] ?? getcwd());
        // Before reading the config, check for an override on
        // the location of the config file path.
        if (isset($opts['k'])) {
            $this->config_file = $opts['k'];
        } else {
            if (isset($opts['config-file'])) {
                $this->config_file = $opts['config-file'];
            }
        }
        // Now that we have a root directory, attempt to read a
        // configuration file `.phan/config.php` if it exists
        $this->maybeReadConfigFile();
        $this->output = new ConsoleOutput();
        $factory = new PrinterFactory();
        $printer_type = 'text';
        $minimum_severity = Config::get()->minimum_severity;
        $mask = -1;
        foreach ($opts ?? [] as $key => $value) {
            switch ($key) {
                case 'h':
                case 'help':
                    $this->usage();
                    break;
                case 'r':
                case 'file-list-only':
                    // Mark it so that we don't load files through
                    // other mechanisms.
                    $this->file_list_only = true;
                    // Empty out the file list
                    $this->file_list = [];
                    // Intentionally fall through to load the
                    // file list
                // Intentionally fall through to load the
                // file list
                case 'f':
                case 'file-list':
                    $file_list = is_array($value) ? $value : [$value];
                    foreach ($file_list as $file_name) {
                        $file_path = Config::projectPath($file_name);
                        if (is_file($file_path) && is_readable($file_path)) {
                            $this->file_list = array_merge($this->file_list, file(Config::projectPath($file_name), FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES));
                        } else {
                            error_log("Unable to read file {$file_path}");
                        }
                    }
                    break;
                case 'l':
                case 'directory':
                    if (!$this->file_list_only) {
                        $directory_list = is_array($value) ? $value : [$value];
                        foreach ($directory_list as $directory_name) {
                            $this->file_list = array_merge($this->file_list, $this->directoryNameToFileList($directory_name));
                        }
                    }
                    break;
                case 'k':
                case 'config-file':
                    break;
                case 'm':
                case 'output-mode':
                    if (!in_array($value, $factory->getTypes(), true)) {
                        $this->usage(sprintf('Unknown output mode "%s". Known values are [%s]', $value, implode(',', $factory->getTypes())));
                    }
                    $printer_type = $value;
                    break;
                case 'c':
                case 'parent-constructor-required':
                    Config::get()->parent_constructor_required = explode(',', $value);
                    break;
                case 'q':
                case 'quick':
                    Config::get()->quick_mode = true;
                    break;
                case 'b':
                case 'backward-compatibility-checks':
                    Config::get()->backward_compatibility_checks = true;
                    break;
                case 'p':
                case 'progress-bar':
                    Config::get()->progress_bar = true;
                    break;
                case 'a':
                case 'dump-ast':
                    Config::get()->dump_ast = true;
                    break;
                case 'dump-signatures-file':
                    Config::get()->dump_signatures_file = $value;
                    break;
                case 'o':
                case 'output':
                    $this->output = new StreamOutput(fopen($value, 'w'));
                    break;
                case 'i':
                case 'ignore-undeclared':
                    $mask ^= Issue::CATEGORY_UNDEFINED;
                    break;
                case '3':
                case 'exclude-directory-list':
                    Config::get()->exclude_analysis_directory_list = explode(',', $value);
                    break;
                case 'exclude-file':
                    Config::get()->exclude_file_list = array_merge(Config::get()->exclude_file_list, is_array($value) ? $value : [$value]);
                    break;
                case 's':
                case 'state-file':
                    // TODO: re-enable eventually
                    // Config::get()->stored_state_file_path = $value;
                    break;
                case 'j':
                case 'processes':
                    Config::get()->processes = (int) $value;
                    break;
                case 'z':
                case 'signature-compatibility':
                    Config::get()->analyze_signature_compatibility = (bool) $value;
                    break;
                case 'y':
                case 'minimum-severity':
                    $minimum_severity = (int) $value;
                    break;
                case 'd':
                case 'project-root-directory':
                    // We handle this flag before parsing options so
                    // that we can get the project root directory to
                    // base other config flags values on
                    break;
                case 'x':
                case 'dead-code-detection':
                    Config::get()->dead_code_detection = true;
                    break;
                case 'markdown-issue-messages':
                    Config::get()->markdown_issue_messages = true;
                    break;
                default:
                    $this->usage("Unknown option '-{$key}'");
                    break;
            }
        }
        $printer = $factory->getPrinter($printer_type, $this->output);
        $filter = new ChainedIssueFilter([new FileIssueFilter(new Phan()), new MinimumSeverityFilter($minimum_severity), new CategoryIssueFilter($mask)]);
        $collector = new BufferingCollector($filter);
        Phan::setPrinter($printer);
        Phan::setIssueCollector($collector);
        $pruneargv = array();
        foreach ($opts ?? [] as $opt => $value) {
            foreach ($argv as $key => $chunk) {
                $regex = '/^' . (isset($opt[1]) ? '--' : '-') . $opt . '/';
                if (($chunk == $value || is_array($value) && in_array($chunk, $value)) && $argv[$key - 1][0] == '-' || preg_match($regex, $chunk)) {
                    array_push($pruneargv, $key);
                }
            }
        }
        while ($key = array_pop($pruneargv)) {
            unset($argv[$key]);
        }
        foreach ($argv as $arg) {
            if ($arg[0] == '-') {
                $this->usage("Unknown option '{$arg}'");
            }
        }
        if (!$this->file_list_only) {
            // Merge in any remaining args on the CLI
            $this->file_list = array_merge($this->file_list, array_slice($argv, 1));
            // Merge in any files given in the config
            $this->file_list = array_merge($this->file_list, Config::get()->file_list);
            // Merge in any directories given in the config
            foreach (Config::get()->directory_list as $directory_name) {
                $this->file_list = array_merge($this->file_list, $this->directoryNameToFileList($directory_name));
            }
            // Don't scan anything twice
            $this->file_list = array_unique($this->file_list);
        }
        // Exclude any files that should be excluded from
        // parsing and analysis (not read at all)
        if (count(Config::get()->exclude_file_list) > 0) {
            $exclude_file_set = [];
            foreach (Config::get()->exclude_file_list as $file) {
                $exclude_file_set[$file] = true;
            }
            $this->file_list = array_filter($this->file_list, function (string $file) use($exclude_file_set) : bool {
                return empty($exclude_file_set[$file]);
            });
        }
        // We can't run dead code detection on multiple cores because
        // we need to update reference lists in a globally accessible
        // way during analysis. With our parallelization mechanism, there
        // is no shared state between processes, making it impossible to
        // have a complete set of reference lists.
        assert(Config::get()->processes === 1 || !Config::get()->dead_code_detection, "We cannot run dead code detection on more than one core.");
    }