| 148 |
lars |
1 |
#!/usr/bin/env php
|
|
|
2 |
<?php
|
|
|
3 |
|
|
|
4 |
foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) {
|
|
|
5 |
if (file_exists($file)) {
|
|
|
6 |
require $file;
|
|
|
7 |
break;
|
|
|
8 |
}
|
|
|
9 |
}
|
|
|
10 |
|
|
|
11 |
ini_set('xdebug.max_nesting_level', 3000);
|
|
|
12 |
|
|
|
13 |
// Disable Xdebug var_dump() output truncation
|
|
|
14 |
ini_set('xdebug.var_display_max_children', -1);
|
|
|
15 |
ini_set('xdebug.var_display_max_data', -1);
|
|
|
16 |
ini_set('xdebug.var_display_max_depth', -1);
|
|
|
17 |
|
|
|
18 |
list($operations, $files, $attributes) = parseArgs($argv);
|
|
|
19 |
|
|
|
20 |
/* Dump nodes by default */
|
|
|
21 |
if (empty($operations)) {
|
|
|
22 |
$operations[] = 'dump';
|
|
|
23 |
}
|
|
|
24 |
|
|
|
25 |
if (empty($files)) {
|
|
|
26 |
showHelp("Must specify at least one file.");
|
|
|
27 |
}
|
|
|
28 |
|
|
|
29 |
$lexer = new PhpParser\Lexer\Emulative(['usedAttributes' => [
|
|
|
30 |
'startLine', 'endLine', 'startFilePos', 'endFilePos', 'comments'
|
|
|
31 |
]]);
|
|
|
32 |
$parser = (new PhpParser\ParserFactory)->create(
|
|
|
33 |
PhpParser\ParserFactory::PREFER_PHP7,
|
|
|
34 |
$lexer
|
|
|
35 |
);
|
|
|
36 |
$dumper = new PhpParser\NodeDumper([
|
|
|
37 |
'dumpComments' => true,
|
|
|
38 |
'dumpPositions' => $attributes['with-positions'],
|
|
|
39 |
]);
|
|
|
40 |
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
|
|
41 |
|
|
|
42 |
$traverser = new PhpParser\NodeTraverser();
|
|
|
43 |
$traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
|
|
|
44 |
|
|
|
45 |
foreach ($files as $file) {
|
|
|
46 |
if (strpos($file, '<?php') === 0) {
|
|
|
47 |
$code = $file;
|
|
|
48 |
fwrite(STDERR, "====> Code $code\n");
|
|
|
49 |
} else {
|
|
|
50 |
if (!file_exists($file)) {
|
|
|
51 |
fwrite(STDERR, "File $file does not exist.\n");
|
|
|
52 |
exit(1);
|
|
|
53 |
}
|
|
|
54 |
|
|
|
55 |
$code = file_get_contents($file);
|
|
|
56 |
fwrite(STDERR, "====> File $file:\n");
|
|
|
57 |
}
|
|
|
58 |
|
|
|
59 |
if ($attributes['with-recovery']) {
|
|
|
60 |
$errorHandler = new PhpParser\ErrorHandler\Collecting;
|
|
|
61 |
$stmts = $parser->parse($code, $errorHandler);
|
|
|
62 |
foreach ($errorHandler->getErrors() as $error) {
|
|
|
63 |
$message = formatErrorMessage($error, $code, $attributes['with-column-info']);
|
|
|
64 |
fwrite(STDERR, $message . "\n");
|
|
|
65 |
}
|
|
|
66 |
if (null === $stmts) {
|
|
|
67 |
continue;
|
|
|
68 |
}
|
|
|
69 |
} else {
|
|
|
70 |
try {
|
|
|
71 |
$stmts = $parser->parse($code);
|
|
|
72 |
} catch (PhpParser\Error $error) {
|
|
|
73 |
$message = formatErrorMessage($error, $code, $attributes['with-column-info']);
|
|
|
74 |
fwrite(STDERR, $message . "\n");
|
|
|
75 |
exit(1);
|
|
|
76 |
}
|
|
|
77 |
}
|
|
|
78 |
|
|
|
79 |
foreach ($operations as $operation) {
|
|
|
80 |
if ('dump' === $operation) {
|
|
|
81 |
fwrite(STDERR, "==> Node dump:\n");
|
|
|
82 |
echo $dumper->dump($stmts, $code), "\n";
|
|
|
83 |
} elseif ('pretty-print' === $operation) {
|
|
|
84 |
fwrite(STDERR, "==> Pretty print:\n");
|
|
|
85 |
echo $prettyPrinter->prettyPrintFile($stmts), "\n";
|
|
|
86 |
} elseif ('json-dump' === $operation) {
|
|
|
87 |
fwrite(STDERR, "==> JSON dump:\n");
|
|
|
88 |
echo json_encode($stmts, JSON_PRETTY_PRINT), "\n";
|
|
|
89 |
} elseif ('var-dump' === $operation) {
|
|
|
90 |
fwrite(STDERR, "==> var_dump():\n");
|
|
|
91 |
var_dump($stmts);
|
|
|
92 |
} elseif ('resolve-names' === $operation) {
|
|
|
93 |
fwrite(STDERR, "==> Resolved names.\n");
|
|
|
94 |
$stmts = $traverser->traverse($stmts);
|
|
|
95 |
}
|
|
|
96 |
}
|
|
|
97 |
}
|
|
|
98 |
|
|
|
99 |
function formatErrorMessage(PhpParser\Error $e, $code, $withColumnInfo) {
|
|
|
100 |
if ($withColumnInfo && $e->hasColumnInfo()) {
|
|
|
101 |
return $e->getMessageWithColumnInfo($code);
|
|
|
102 |
} else {
|
|
|
103 |
return $e->getMessage();
|
|
|
104 |
}
|
|
|
105 |
}
|
|
|
106 |
|
|
|
107 |
function showHelp($error = '') {
|
|
|
108 |
if ($error) {
|
|
|
109 |
fwrite(STDERR, $error . "\n\n");
|
|
|
110 |
}
|
|
|
111 |
fwrite($error ? STDERR : STDOUT, <<<OUTPUT
|
|
|
112 |
Usage: php-parse [operations] file1.php [file2.php ...]
|
|
|
113 |
or: php-parse [operations] "<?php code"
|
|
|
114 |
Turn PHP source code into an abstract syntax tree.
|
|
|
115 |
|
|
|
116 |
Operations is a list of the following options (--dump by default):
|
|
|
117 |
|
|
|
118 |
-d, --dump Dump nodes using NodeDumper
|
|
|
119 |
-p, --pretty-print Pretty print file using PrettyPrinter\Standard
|
|
|
120 |
-j, --json-dump Print json_encode() result
|
|
|
121 |
--var-dump var_dump() nodes (for exact structure)
|
|
|
122 |
-N, --resolve-names Resolve names using NodeVisitor\NameResolver
|
|
|
123 |
-c, --with-column-info Show column-numbers for errors (if available)
|
|
|
124 |
-P, --with-positions Show positions in node dumps
|
|
|
125 |
-r, --with-recovery Use parsing with error recovery
|
|
|
126 |
-h, --help Display this page
|
|
|
127 |
|
|
|
128 |
Example:
|
|
|
129 |
php-parse -d -p -N -d file.php
|
|
|
130 |
|
|
|
131 |
Dumps nodes, pretty prints them, then resolves names and dumps them again.
|
|
|
132 |
|
|
|
133 |
|
|
|
134 |
OUTPUT
|
|
|
135 |
);
|
|
|
136 |
exit($error ? 1 : 0);
|
|
|
137 |
}
|
|
|
138 |
|
|
|
139 |
function parseArgs($args) {
|
|
|
140 |
$operations = [];
|
|
|
141 |
$files = [];
|
|
|
142 |
$attributes = [
|
|
|
143 |
'with-column-info' => false,
|
|
|
144 |
'with-positions' => false,
|
|
|
145 |
'with-recovery' => false,
|
|
|
146 |
];
|
|
|
147 |
|
|
|
148 |
array_shift($args);
|
|
|
149 |
$parseOptions = true;
|
|
|
150 |
foreach ($args as $arg) {
|
|
|
151 |
if (!$parseOptions) {
|
|
|
152 |
$files[] = $arg;
|
|
|
153 |
continue;
|
|
|
154 |
}
|
|
|
155 |
|
|
|
156 |
switch ($arg) {
|
|
|
157 |
case '--dump':
|
|
|
158 |
case '-d':
|
|
|
159 |
$operations[] = 'dump';
|
|
|
160 |
break;
|
|
|
161 |
case '--pretty-print':
|
|
|
162 |
case '-p':
|
|
|
163 |
$operations[] = 'pretty-print';
|
|
|
164 |
break;
|
|
|
165 |
case '--json-dump':
|
|
|
166 |
case '-j':
|
|
|
167 |
$operations[] = 'json-dump';
|
|
|
168 |
break;
|
|
|
169 |
case '--var-dump':
|
|
|
170 |
$operations[] = 'var-dump';
|
|
|
171 |
break;
|
|
|
172 |
case '--resolve-names':
|
|
|
173 |
case '-N';
|
|
|
174 |
$operations[] = 'resolve-names';
|
|
|
175 |
break;
|
|
|
176 |
case '--with-column-info':
|
|
|
177 |
case '-c';
|
|
|
178 |
$attributes['with-column-info'] = true;
|
|
|
179 |
break;
|
|
|
180 |
case '--with-positions':
|
|
|
181 |
case '-P':
|
|
|
182 |
$attributes['with-positions'] = true;
|
|
|
183 |
break;
|
|
|
184 |
case '--with-recovery':
|
|
|
185 |
case '-r':
|
|
|
186 |
$attributes['with-recovery'] = true;
|
|
|
187 |
break;
|
|
|
188 |
case '--help':
|
|
|
189 |
case '-h';
|
|
|
190 |
showHelp();
|
|
|
191 |
break;
|
|
|
192 |
case '--':
|
|
|
193 |
$parseOptions = false;
|
|
|
194 |
break;
|
|
|
195 |
default:
|
|
|
196 |
if ($arg[0] === '-') {
|
|
|
197 |
showHelp("Invalid operation $arg.");
|
|
|
198 |
} else {
|
|
|
199 |
$files[] = $arg;
|
|
|
200 |
}
|
|
|
201 |
}
|
|
|
202 |
}
|
|
|
203 |
|
|
|
204 |
return [$operations, $files, $attributes];
|
|
|
205 |
}
|