#!/usr/bin/env php
<?php
foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $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';
}
showHelp("Must specify at least one file.");
}
$lexer = new PhpParserLexerEmulative(['usedAttributes' => [
'startLine', 'endLine', 'startFilePos', 'endFilePos', 'comments'
]]);
$parser = (new PhpParserParserFactory)->create(
PhpParserParserFactory::PREFER_PHP7,
$lexer
);
$dumper = new PhpParserNodeDumper([
'dumpComments' => true,
'dumpPositions' => $attributes['with-positions'],
]);
$prettyPrinter = new PhpParserPrettyPrinterStandard;
$traverser = new PhpParserNodeTraverser();
$traverser->addVisitor(new PhpParserNodeVisitorNameResolver);
foreach ($files as $file) {
if (strpos($file,
'<?php') ===
0) {
$code = $file;
fwrite(STDERR,
"====> Code $coden");
} else {
fwrite(STDERR,
"File $file does not exist.n");
}
fwrite(STDERR,
"====> File $file:n");
}
if ($attributes['with-recovery']) {
$errorHandler = new PhpParserErrorHandlerCollecting;
$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 (PhpParserError $error) {
$message = formatErrorMessage($error, $code, $attributes['with-column-info']);
fwrite(STDERR,
$message .
"n");
}
}
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");
} elseif ('resolve-names' === $operation) {
fwrite(STDERR,
"==> Resolved names.n");
$stmts = $traverser->traverse($stmts);
}
}
}
function formatErrorMessage(PhpParserError $e, $code, $withColumnInfo) {
if ($withColumnInfo && $e->hasColumnInfo()) {
return $e->getMessageWithColumnInfo($code);
} else {
return $e->getMessage();
}
}
function showHelp($error = '') {
if ($error) {
fwrite(STDERR,
$error .
"nn");
}
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
-j, --json-dump
print json_encode
() result
--var-dump
var_dump() nodes
(for exact structure
)
-N, --resolve-names Resolve names using NodeVisitorNameResolver
-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
);
}
function parseArgs($args) {
$operations = [];
$files = [];
$attributes = [
'with-column-info' => false,
'with-positions' => false,
'with-recovery' => false,
];
$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];
}