| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 | #!/usr/bin/env php<?phpforeach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) {    if (file_exists($file)) {        require $file;        break;    }}ini_set('xdebug.max_nesting_level', 3000);// Disable Xdebug var_dump() output truncationini_set('xdebug.var_display_max_children', -1);ini_set('xdebug.var_display_max_data', -1);ini_set('xdebug.var_display_max_depth', -1);list($operations, $files, $attributes) = parseArgs($argv);/* Dump nodes by default */if (empty($operations)) {    $operations[] = 'dump';}if (empty($files)) {    showHelp("Must specify at least one file.");}$lexer = new PhpParser\Lexer\Emulative(['usedAttributes' => [    'startLine', 'endLine', 'startFilePos', 'endFilePos', 'comments']]);$parser = (new PhpParser\ParserFactory)->create(    PhpParser\ParserFactory::PREFER_PHP7,    $lexer);$dumper = new PhpParser\NodeDumper([    'dumpComments' => true,    'dumpPositions' => $attributes['with-positions'],]);$prettyPrinter = new PhpParser\PrettyPrinter\Standard;$traverser = new PhpParser\NodeTraverser();$traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);foreach ($files as $file) {    if (strpos($file, '<?php') === 0) {        $code = $file;        fwrite(STDERR, "====> Code $code\n");    } else {        if (!file_exists($file)) {            fwrite(STDERR, "File $file does not exist.\n");            exit(1);        }        $code = file_get_contents($file);        fwrite(STDERR, "====> File $file:\n");    }    if ($attributes['with-recovery']) {        $errorHandler = new PhpParser\ErrorHandler\Collecting;        $stmts = $parser->parse($code, $errorHandler);        foreach ($errorHandler->getErrors() as $error) {            $message = formatErrorMessage($error, $code, $attributes['with-column-info']);            fwrite(STDERR, $message . "\n");        }        if (null === $stmts) {            continue;        }    } else {        try {            $stmts = $parser->parse($code);        } catch (PhpParser\Error $error) {            $message = formatErrorMessage($error, $code, $attributes['with-column-info']);            fwrite(STDERR, $message . "\n");            exit(1);        }    }    foreach ($operations as $operation) {        if ('dump' === $operation) {            fwrite(STDERR, "==> Node dump:\n");            echo $dumper->dump($stmts, $code), "\n";        } elseif ('pretty-print' === $operation) {            fwrite(STDERR, "==> Pretty print:\n");            echo $prettyPrinter->prettyPrintFile($stmts), "\n";        } elseif ('json-dump' === $operation) {            fwrite(STDERR, "==> JSON dump:\n");            echo json_encode($stmts, JSON_PRETTY_PRINT), "\n";        } elseif ('var-dump' === $operation) {            fwrite(STDERR, "==> var_dump():\n");            var_dump($stmts);        } elseif ('resolve-names' === $operation) {            fwrite(STDERR, "==> Resolved names.\n");            $stmts = $traverser->traverse($stmts);        }    }}function formatErrorMessage(PhpParser\Error $e, $code, $withColumnInfo) {    if ($withColumnInfo && $e->hasColumnInfo()) {        return $e->getMessageWithColumnInfo($code);    } else {        return $e->getMessage();    }}function showHelp($error = '') {    if ($error) {        fwrite(STDERR, $error . "\n\n");    }    fwrite($error ? STDERR : STDOUT, <<<OUTPUTUsage: php-parse [operations] file1.php [file2.php ...]   or: php-parse [operations] "<?php code"Turn PHP source code into an abstract syntax tree.Operations is a list of the following options (--dump by default):    -d, --dump              Dump nodes using NodeDumper    -p, --pretty-print      Pretty print file using PrettyPrinter\Standard    -j, --json-dump         Print json_encode() result        --var-dump          var_dump() nodes (for exact structure)    -N, --resolve-names     Resolve names using NodeVisitor\NameResolver    -c, --with-column-info  Show column-numbers for errors (if available)    -P, --with-positions    Show positions in node dumps    -r, --with-recovery     Use parsing with error recovery    -h, --help              Display this pageExample:    php-parse -d -p -N -d file.php    Dumps nodes, pretty prints them, then resolves names and dumps them again.OUTPUT    );    exit($error ? 1 : 0);}function parseArgs($args) {    $operations = [];    $files = [];    $attributes = [        'with-column-info' => false,        'with-positions' => false,        'with-recovery' => false,    ];    array_shift($args);    $parseOptions = true;    foreach ($args as $arg) {        if (!$parseOptions) {            $files[] = $arg;            continue;        }        switch ($arg) {            case '--dump':            case '-d':                $operations[] = 'dump';                break;            case '--pretty-print':            case '-p':                $operations[] = 'pretty-print';                break;            case '--json-dump':            case '-j':                $operations[] = 'json-dump';                break;            case '--var-dump':                $operations[] = 'var-dump';                break;            case '--resolve-names':            case '-N';                $operations[] = 'resolve-names';                break;            case '--with-column-info':            case '-c';                $attributes['with-column-info'] = true;                break;            case '--with-positions':            case '-P':                $attributes['with-positions'] = true;                break;            case '--with-recovery':            case '-r':                $attributes['with-recovery'] = true;                break;            case '--help':            case '-h';                showHelp();                break;            case '--':                $parseOptions = false;                break;            default:                if ($arg[0] === '-') {                    showHelp("Invalid operation $arg.");                } else {                    $files[] = $arg;                }        }    }    return [$operations, $files, $attributes];}
 |