php-parse.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. #!/usr/bin/env php
  2. <?php
  3. require __DIR__ . '/../lib/bootstrap.php';
  4. ini_set('xdebug.max_nesting_level', 3000);
  5. // Disable XDebug var_dump() output truncation
  6. ini_set('xdebug.var_display_max_children', -1);
  7. ini_set('xdebug.var_display_max_data', -1);
  8. ini_set('xdebug.var_display_max_depth', -1);
  9. list($operations, $files, $attributes) = parseArgs($argv);
  10. /* Dump nodes by default */
  11. if (empty($operations)) {
  12. $operations[] = 'dump';
  13. }
  14. if (empty($files)) {
  15. showHelp("Must specify at least one file.");
  16. }
  17. $lexer = new PhpParser\Lexer\Emulative(array('usedAttributes' => array(
  18. 'startLine', 'endLine', 'startFilePos', 'endFilePos'
  19. )));
  20. $parser = new PhpParser\Parser($lexer);
  21. $dumper = new PhpParser\NodeDumper;
  22. $prettyPrinter = new PhpParser\PrettyPrinter\Standard;
  23. $serializer = new PhpParser\Serializer\XML;
  24. $traverser = new PhpParser\NodeTraverser();
  25. $traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
  26. foreach ($files as $file) {
  27. if (strpos($file, '<?php') === 0) {
  28. $code = $file;
  29. echo "====> Code $code\n";
  30. } else {
  31. if (!file_exists($file)) {
  32. die("File $file does not exist.\n");
  33. }
  34. $code = file_get_contents($file);
  35. echo "====> File $file:\n";
  36. }
  37. try {
  38. $stmts = $parser->parse($code);
  39. } catch (PhpParser\Error $e) {
  40. if ($attributes['with-column-info'] && $e->hasColumnInfo()) {
  41. $startLine = $e->getStartLine();
  42. $endLine = $e->getEndLine();
  43. $startColumn = $e->getStartColumn($code);
  44. $endColumn = $e->getEndColumn($code);
  45. $message .= $e->getRawMessage() . " from $startLine:$startColumn to $endLine:$endColumn";
  46. } else {
  47. $message = $e->getMessage();
  48. }
  49. die($message . "\n");
  50. }
  51. foreach ($operations as $operation) {
  52. if ('dump' === $operation) {
  53. echo "==> Node dump:\n";
  54. echo $dumper->dump($stmts), "\n";
  55. } elseif ('pretty-print' === $operation) {
  56. echo "==> Pretty print:\n";
  57. echo $prettyPrinter->prettyPrintFile($stmts), "\n";
  58. } elseif ('serialize-xml' === $operation) {
  59. echo "==> Serialized XML:\n";
  60. echo $serializer->serialize($stmts), "\n";
  61. } elseif ('var-dump' === $operation) {
  62. echo "==> var_dump():\n";
  63. var_dump($stmts);
  64. } elseif ('resolve-names' === $operation) {
  65. echo "==> Resolved names.\n";
  66. $stmts = $traverser->traverse($stmts);
  67. }
  68. }
  69. }
  70. function showHelp($error) {
  71. die($error . "\n\n" .
  72. <<<OUTPUT
  73. Usage: php php-parse.php [operations] file1.php [file2.php ...]
  74. or: php php-parse.php [operations] "<?php code"
  75. Turn PHP source code into an abstract syntax tree.
  76. Operations is a list of the following options (--dump by default):
  77. -d, --dump Dump nodes using NodeDumper
  78. -p, --pretty-print Pretty print file using PrettyPrinter\Standard
  79. --serialize-xml Serialize nodes using Serializer\XML
  80. --var-dump var_dump() nodes (for exact structure)
  81. -N, --resolve-names Resolve names using NodeVisitor\NameResolver
  82. -c, --with-column-info Show column-numbers for errors (if available)
  83. Example:
  84. php php-parse.php -d -p -N -d file.php
  85. Dumps nodes, pretty prints them, then resolves names and dumps them again.
  86. OUTPUT
  87. );
  88. }
  89. function parseArgs($args) {
  90. $operations = array();
  91. $files = array();
  92. $attributes = array(
  93. 'with-column-info' => false,
  94. );
  95. array_shift($args);
  96. $parseOptions = true;
  97. foreach ($args as $arg) {
  98. if (!$parseOptions) {
  99. $files[] = $arg;
  100. continue;
  101. }
  102. switch ($arg) {
  103. case '--dump':
  104. case '-d':
  105. $operations[] = 'dump';
  106. break;
  107. case '--pretty-print':
  108. case '-p':
  109. $operations[] = 'pretty-print';
  110. break;
  111. case '--serialize-xml':
  112. $operations[] = 'serialize-xml';
  113. break;
  114. case '--var-dump':
  115. $operations[] = 'var-dump';
  116. break;
  117. case '--resolve-names':
  118. case '-N';
  119. $operations[] = 'resolve-names';
  120. break;
  121. case '--with-column-info':
  122. case '-c';
  123. $attributes['with-column-info'] = true;
  124. break;
  125. case '--':
  126. $parseOptions = false;
  127. break;
  128. default:
  129. if ($arg[0] === '-') {
  130. showHelp("Invalid operation $arg.");
  131. } else {
  132. $files[] = $arg;
  133. }
  134. }
  135. }
  136. return array($operations, $files, $attributes);
  137. }