Ast.php 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of Hyperf.
  5. *
  6. * @link https://www.hyperf.io
  7. * @document https://hyperf.wiki
  8. * @contact group@hyperf.io
  9. * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  10. */
  11. namespace Hyperf\Di\Aop;
  12. use Hyperf\Support\Composer;
  13. use PhpParser\Node\Stmt\ClassLike;
  14. use PhpParser\Node\Stmt\Namespace_;
  15. use PhpParser\NodeTraverser;
  16. use PhpParser\Parser;
  17. use PhpParser\ParserFactory;
  18. use PhpParser\PrettyPrinter\Standard;
  19. use PhpParser\PrettyPrinterAbstract;
  20. class Ast
  21. {
  22. private Parser $astParser;
  23. private PrettyPrinterAbstract $printer;
  24. public function __construct()
  25. {
  26. $parserFactory = new ParserFactory();
  27. $this->astParser = $parserFactory->create(ParserFactory::ONLY_PHP7);
  28. $this->printer = new Standard();
  29. }
  30. public function parse(string $code): ?array
  31. {
  32. return $this->astParser->parse($code);
  33. }
  34. public function proxy(string $className)
  35. {
  36. $code = $this->getCodeByClassName($className);
  37. $stmts = $this->astParser->parse($code);
  38. $traverser = new NodeTraverser();
  39. $visitorMetadata = new VisitorMetadata($className);
  40. // User could modify or replace the node visitors by Hyperf\Di\Aop\AstVisitorRegistry.
  41. $queue = clone AstVisitorRegistry::getQueue();
  42. foreach ($queue as $string) {
  43. $visitor = new $string($visitorMetadata);
  44. $traverser->addVisitor($visitor);
  45. }
  46. $modifiedStmts = $traverser->traverse($stmts);
  47. return $this->printer->prettyPrintFile($modifiedStmts);
  48. }
  49. public function parseClassByStmts(array $stmts): string
  50. {
  51. $namespace = $className = '';
  52. foreach ($stmts as $stmt) {
  53. if ($stmt instanceof Namespace_ && $stmt->name) {
  54. $namespace = $stmt->name->toString();
  55. foreach ($stmt->stmts as $node) {
  56. if (($node instanceof ClassLike) && $node->name) {
  57. $className = $node->name->toString();
  58. break;
  59. }
  60. }
  61. }
  62. }
  63. return ($namespace && $className) ? $namespace . '\\' . $className : '';
  64. }
  65. private function getCodeByClassName(string $className): string
  66. {
  67. $file = Composer::getLoader()->findFile($className);
  68. if (! $file) {
  69. return '';
  70. }
  71. return file_get_contents($file);
  72. }
  73. }