|
|
#!/usr/bin/env php <?php
foreach ([__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 truncation ini_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, <<<OUTPUT Usage: 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 page
Example: 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]; }
|