DispatcherFactory.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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\RpcServer\Router;
  12. use FastRoute\DataGenerator\GroupCountBased as DataGenerator;
  13. use FastRoute\Dispatcher;
  14. use FastRoute\Dispatcher\GroupCountBased;
  15. use FastRoute\RouteParser\Std;
  16. use Hyperf\Di\Annotation\AnnotationCollector;
  17. use Hyperf\Di\Exception\ConflictAnnotationException;
  18. use Hyperf\Di\ReflectionManager;
  19. use Hyperf\HttpServer\Annotation\Middleware;
  20. use Hyperf\HttpServer\Annotation\Middlewares;
  21. use Hyperf\HttpServer\MiddlewareManager;
  22. use Hyperf\Rpc\Contract\PathGeneratorInterface;
  23. use Hyperf\RpcServer\Annotation\RpcService;
  24. use Hyperf\RpcServer\Event\AfterPathRegister;
  25. use Hyperf\Stringable\Str;
  26. use Psr\EventDispatcher\EventDispatcherInterface;
  27. use ReflectionMethod;
  28. class DispatcherFactory
  29. {
  30. protected array $routes = [BASE_PATH . '/config/services.php'];
  31. /**
  32. * @var RouteCollector[]
  33. */
  34. private array $routers = [];
  35. /**
  36. * @var Dispatcher[]
  37. */
  38. private array $dispatchers = [];
  39. public function __construct(private EventDispatcherInterface $eventDispatcher, private PathGeneratorInterface $pathGenerator)
  40. {
  41. $this->initAnnotationRoute(AnnotationCollector::list());
  42. $this->initConfigRoute();
  43. }
  44. public function getDispatcher(string $serverName): Dispatcher
  45. {
  46. if (isset($this->dispatchers[$serverName])) {
  47. return $this->dispatchers[$serverName];
  48. }
  49. $router = $this->getRouter($serverName);
  50. return $this->dispatchers[$serverName] = new GroupCountBased($router->getData());
  51. }
  52. public function initConfigRoute()
  53. {
  54. Router::init($this);
  55. foreach ($this->routes as $route) {
  56. if (file_exists($route)) {
  57. require_once $route;
  58. }
  59. }
  60. }
  61. public function getRouter(string $serverName): RouteCollector
  62. {
  63. if (isset($this->routers[$serverName])) {
  64. return $this->routers[$serverName];
  65. }
  66. $parser = new Std();
  67. $generator = new DataGenerator();
  68. return $this->routers[$serverName] = new RouteCollector($parser, $generator);
  69. }
  70. private function initAnnotationRoute(array $collector): void
  71. {
  72. foreach ($collector as $className => $metadata) {
  73. if (isset($metadata['_c'][RpcService::class])) {
  74. $middlewares = $this->handleMiddleware($metadata['_c']);
  75. $this->handleRpcService($className, $metadata['_c'][RpcService::class], $metadata['_m'] ?? [], $middlewares);
  76. }
  77. }
  78. }
  79. /**
  80. * Register route according to RpcService annotation.
  81. */
  82. private function handleRpcService(
  83. string $className,
  84. RpcService $annotation,
  85. array $methodMetadata,
  86. array $middlewares = []
  87. ): void {
  88. $prefix = $annotation->name ?: $className;
  89. $router = $this->getRouter($annotation->server);
  90. $publicMethods = ReflectionManager::reflectClass($className)->getMethods(ReflectionMethod::IS_PUBLIC);
  91. foreach ($publicMethods as $reflectionMethod) {
  92. $methodName = $reflectionMethod->getName();
  93. if (Str::startsWith($methodName, '__')) {
  94. continue;
  95. }
  96. $path = $this->pathGenerator->generate($prefix, $methodName);
  97. $router->addRoute($path, [
  98. $className,
  99. $methodName,
  100. ]);
  101. $methodMiddlewares = $middlewares;
  102. // Handle method level middlewares.
  103. if (isset($methodMetadata[$methodName])) {
  104. $methodMiddlewares = array_merge($this->handleMiddleware($methodMetadata[$methodName]), $middlewares);
  105. }
  106. // TODO: Remove array_unique from v3.0.
  107. $methodMiddlewares = array_unique($methodMiddlewares);
  108. // Register middlewares.
  109. MiddlewareManager::addMiddlewares($annotation->server, $path, 'POST', $methodMiddlewares);
  110. // Trigger the AfterPathRegister event.
  111. $this->eventDispatcher->dispatch(new AfterPathRegister($path, $className, $methodName, $annotation));
  112. }
  113. }
  114. private function handleMiddleware(array $metadata): array
  115. {
  116. $hasMiddlewares = isset($metadata[Middlewares::class]);
  117. $hasMiddleware = isset($metadata[Middleware::class]);
  118. if (! $hasMiddlewares && ! $hasMiddleware) {
  119. return [];
  120. }
  121. if ($hasMiddlewares && $hasMiddleware) {
  122. throw new ConflictAnnotationException('Could not use @Middlewares and @Middleware annotation at the same times at same level.');
  123. }
  124. if ($hasMiddlewares) {
  125. // @Middlewares
  126. /** @var Middlewares $middlewares */
  127. $middlewares = $metadata[Middlewares::class];
  128. $result = [];
  129. foreach ($middlewares->middlewares as $middleware) {
  130. $result[] = $middleware->middleware;
  131. }
  132. return $result;
  133. }
  134. // @Middleware
  135. /** @var Middleware $middleware */
  136. $middleware = $metadata[Middleware::class];
  137. return [$middleware->middleware];
  138. }
  139. }