注解路由

Testing Is Documentation

tests/Router/RouterAnnotationTest.php注解路由 - 图1

QueryPHP 除了传统的自动匹配 MVC 路由之外,也支持自定义的注解路由。

Uses

  1. <?php
  2. use Leevel\Di\Container;
  3. use Leevel\Di\IContainer;
  4. use Leevel\Http\Request;
  5. use Leevel\Kernel\App;
  6. use Leevel\Router\Router;
  7. use Leevel\Router\RouterProvider;
  8. use Leevel\Router\Url;
  9. use Symfony\Component\HttpFoundation\Response;
  10. use Tests\Router\Middlewares\Demo1;
  11. use Tests\Router\Middlewares\Demo2;
  12. use Tests\Router\Middlewares\Demo3;
  13. use Tests\Router\Middlewares\DemoForAll;
  14. use Tests\Router\Middlewares\DemoForBasePath;

注解路由扫描

QueryPHP 系统会根据路由服务提供者信息,扫描系统的注解生成框架的注解路由,并且支持缓存到文件。

fixture 定义

路由服务提供者 Tests\Router\RouterProviderAnnotation

  1. namespace Tests\Router;
  2. class RouterProviderAnnotation extends RouterProvider
  3. {
  4. protected string $controllerDir = 'Router\\Controllers';
  5. protected array $middlewareGroups = [
  6. 'group1' => [
  7. 'demo1',
  8. 'demo2',
  9. ],
  10. 'group2' => [
  11. 'demo1',
  12. 'demo3:10,world',
  13. ],
  14. 'group3' => [
  15. 'demo1',
  16. 'demo2',
  17. 'demo3:10,world',
  18. ],
  19. ];
  20. protected array $middlewareAlias = [
  21. 'demo1' => Demo1::class,
  22. 'demo2' => Demo2::class,
  23. 'demo3' => Demo3::class,
  24. 'demo_for_base_path' => DemoForBasePath::class,
  25. 'demo_for_all' => DemoForAll::class,
  26. ];
  27. protected array $basePaths = [
  28. '*' => [
  29. 'middlewares' => 'demo_for_all',
  30. ],
  31. '/basePath/normalize' => [
  32. 'middlewares' => 'demo_for_base_path',
  33. ],
  34. ];
  35. protected array $groups = [
  36. 'pet' => [],
  37. 'store' => [],
  38. 'user' => [],
  39. '/api/v1' => [
  40. 'middlewares' => 'group1',
  41. ],
  42. '/api/v2' => [
  43. 'middlewares' => 'group2',
  44. ],
  45. '/api/v3' => [
  46. 'middlewares' => 'demo1,demo3:30,world',
  47. ],
  48. '/api/v3' => [
  49. 'middlewares' => ['demo1', 'group3'],
  50. ],
  51. '/api/v4' => [
  52. 'middlewares' => 'notFound',
  53. ],
  54. 'newPrefix/v1' => [],
  55. ];
  56. public function bootstrap(): void
  57. {
  58. parent::bootstrap();
  59. }
  60. public function getRouters(): array
  61. {
  62. return parent::getRouters();
  63. }
  64. }

路由注解缓存结果 tests/Router/Apps/AppForAnnotation/data.json

  1. {
  2. "base_paths": {
  3. "*": {
  4. "middlewares": {
  5. "handle": [
  6. "Tests\\Router\\Middlewares\\DemoForAll@handle"
  7. ],
  8. "terminate": [
  9. "Tests\\Router\\Middlewares\\DemoForAll@terminate"
  10. ]
  11. }
  12. },
  13. "\/^\\\/basePath\\\/normalize\\\/$\/": {
  14. "middlewares": {
  15. "handle": [
  16. "Tests\\Router\\Middlewares\\DemoForBasePath@handle"
  17. ]
  18. }
  19. },
  20. "\/^\\\/api\\\/v1(\\S*)\\\/$\/": {
  21. "middlewares": {
  22. "handle": [
  23. "Tests\\Router\\Middlewares\\Demo2@handle"
  24. ],
  25. "terminate": [
  26. "Tests\\Router\\Middlewares\\Demo1@terminate",
  27. "Tests\\Router\\Middlewares\\Demo2@terminate"
  28. ]
  29. }
  30. },
  31. "\/^\\\/api\\\/v2(\\S*)\\\/$\/": {
  32. "middlewares": {
  33. "handle": [
  34. "Tests\\Router\\Middlewares\\Demo3@handle:10,world"
  35. ],
  36. "terminate": [
  37. "Tests\\Router\\Middlewares\\Demo1@terminate"
  38. ]
  39. }
  40. },
  41. "\/^\\\/api\\\/v3(\\S*)\\\/$\/": {
  42. "middlewares": {
  43. "handle": [
  44. "Tests\\Router\\Middlewares\\Demo2@handle",
  45. "Tests\\Router\\Middlewares\\Demo3@handle:10,world"
  46. ],
  47. "terminate": [
  48. "Tests\\Router\\Middlewares\\Demo1@terminate",
  49. "Tests\\Router\\Middlewares\\Demo2@terminate"
  50. ]
  51. }
  52. }
  53. },
  54. "groups": [
  55. "\/pet",
  56. "\/store",
  57. "\/user",
  58. "\/api\/v1",
  59. "\/api\/v2",
  60. "\/api\/v3",
  61. "\/api\/v4",
  62. "\/newPrefix\/v1"
  63. ],
  64. "routers": {
  65. "get": {
  66. "static": {
  67. "\/basePath\/normalize\/": {
  68. "bind": "\\Tests\\Router\\Controllers\\Annotation\\BasePath@normalize"
  69. },
  70. "\/bindNotFound\/test\/": {
  71. "bind": "\\Tests\\Router\\Controllers\\Annotation\\BindNotFound@notFound"
  72. },
  73. "\/bindNotFound\/test2\/": {
  74. "bind": "\\Tests\\Router\\Controllers\\Annotation\\BindNotFound"
  75. },
  76. "\/bindNotFound\/test3\/": {
  77. "bind": "\\Tests\\Router\\Controllers\\Annotation\\BindMethodNotFound"
  78. },
  79. "\/bindNotSet\/test\/": {
  80. "bind": null
  81. },
  82. "\/bindNotSet\/test2\/": {
  83. "bind": null
  84. },
  85. "\/domain\/test\/": {
  86. "domain": "queryphp.com",
  87. "bind": "\\Tests\\Router\\Apps\\AppForAnnotation\\Controllers\\Domain@fooNotMatchedDomain"
  88. },
  89. "\/domain\/test2\/": {
  90. "domain": "queryphp.com",
  91. "bind": "\\Tests\\Router\\Apps\\AppForAnnotation\\Controllers\\Domain@barMatchedDomain"
  92. },
  93. "\/domain\/test3\/": {
  94. "domain": "{subdomain:[A-Za-z]+}-vip.{domain}.queryphp.com",
  95. "bind": "\\Tests\\Router\\Apps\\AppForAnnotation\\Controllers\\Domain@barMatchedDomainWithVar",
  96. "domain_regex": "\/^([A-Za-z]+)\\-vip\\.(\\S+)\\.queryphp\\.com$\/",
  97. "domain_var": [
  98. "subdomain",
  99. "domain"
  100. ]
  101. },
  102. "\/domain\/test4\/": {
  103. "domain": "api.queryphp.com",
  104. "bind": "\\Tests\\Router\\Apps\\AppForAnnotation\\Controllers\\Domain@barMatchedDomainWithoutExtend"
  105. },
  106. "\/extendVar\/test\/": {
  107. "attributes": {
  108. "args1": "hello",
  109. "args2": "world"
  110. },
  111. "bind": "\\Tests\\Router\\Apps\\AppForAnnotation\\Controllers\\ExtendVar@withExtendVar"
  112. },
  113. "\/middleware\/test\/": {
  114. "middlewares": {
  115. "handle": [
  116. "Tests\\Router\\Middlewares\\Demo2@handle"
  117. ],
  118. "terminate": [
  119. "Tests\\Router\\Middlewares\\Demo1@terminate",
  120. "Tests\\Router\\Middlewares\\Demo2@terminate"
  121. ]
  122. },
  123. "bind": "\\Tests\\Router\\Apps\\AppForAnnotation\\Controllers\\Middleware@foo"
  124. },
  125. "\/middleware\/test2\/": {
  126. "middlewares": {
  127. "handle": [
  128. "Tests\\Router\\Middlewares\\Demo2@handle",
  129. "Tests\\Router\\Middlewares\\Demo3@handle:10,world"
  130. ],
  131. "terminate": [
  132. "Tests\\Router\\Middlewares\\Demo1@terminate",
  133. "Tests\\Router\\Middlewares\\Demo2@terminate"
  134. ]
  135. },
  136. "bind": "\\Tests\\Router\\Apps\\AppForAnnotation\\Controllers\\Middleware@bar"
  137. },
  138. "\/middleware\/test3\/": {
  139. "middlewares": {
  140. "handle": [
  141. "Tests\\Router\\Middlewares\\Demo2@handle",
  142. "Tests\\Router\\Middlewares\\Demo3@handle:10,world",
  143. "Tests\\Router\\Middlewares\\DemoForBasePath@handle"
  144. ],
  145. "terminate": [
  146. "Tests\\Router\\Middlewares\\Demo1@terminate",
  147. "Tests\\Router\\Middlewares\\Demo2@terminate"
  148. ]
  149. },
  150. "bind": "\\Tests\\Router\\Apps\\AppForAnnotation\\Controllers\\Middleware@hello"
  151. },
  152. "\/middleware\/test4\/": {
  153. "middlewares": {
  154. "handle": [],
  155. "terminate": [
  156. "Tests\\Router\\Middlewares\\Demo1@terminate"
  157. ]
  158. },
  159. "bind": "\\Tests\\Router\\Apps\\AppForAnnotation\\Controllers\\Middleware@world"
  160. },
  161. "\/port\/test\/": {
  162. "port": 9527,
  163. "bind": "\\Tests\\Router\\Apps\\AppForAnnotation\\Controllers\\Port@fooNotMatchedPort"
  164. },
  165. "\/port\/test2\/": {
  166. "port": 9527,
  167. "bind": "\\Tests\\Router\\Apps\\AppForAnnotation\\Controllers\\Port@barMatchedPort"
  168. },
  169. "\/scheme\/test\/": {
  170. "scheme": "https",
  171. "bind": "\\Tests\\Router\\Apps\\AppForAnnotation\\Controllers\\Scheme@fooNotMatchedScheme"
  172. },
  173. "\/scheme\/test2\/": {
  174. "scheme": "http",
  175. "bind": "\\Tests\\Router\\Apps\\AppForAnnotation\\Controllers\\Scheme@barMatchedScheme"
  176. }
  177. },
  178. "a": {
  179. "\/api\/v1": {
  180. "\/api\/v1\/petLeevel\/{petId:[A-Za-z]+}\/": {
  181. "bind": "\\Tests\\Router\\Controllers\\Annotation\\PetLeevel",
  182. "var": [
  183. "petId"
  184. ]
  185. },
  186. "regex": [
  187. "~^(?|\/api\/v1\/petLeevel\/([A-Za-z]+)\/)$~x"
  188. ],
  189. "map": [
  190. {
  191. "2": "\/api\/v1\/petLeevel\/{petId:[A-Za-z]+}\/"
  192. }
  193. ]
  194. },
  195. "_": {
  196. "\/api\/notInGroup\/petLeevel\/{petId:[A-Za-z]+}\/": {
  197. "bind": "\\Tests\\Router\\Apps\\AppForAnnotation\\Controllers\\Pet@petLeevelNotInGroup",
  198. "var": [
  199. "petId"
  200. ]
  201. },
  202. "regex": [
  203. "~^(?|\/api\/notInGroup\/petLeevel\/([A-Za-z]+)\/)$~x"
  204. ],
  205. "map": [
  206. {
  207. "2": "\/api\/notInGroup\/petLeevel\/{petId:[A-Za-z]+}\/"
  208. }
  209. ]
  210. }
  211. },
  212. "n": {
  213. "\/newPrefix\/v1": {
  214. "\/newPrefix\/v1\/petLeevel\/{petId:[A-Za-z]+}\/": {
  215. "bind": "\\Tests\\Router\\Controllers\\Annotation\\NewPrefix",
  216. "var": [
  217. "petId"
  218. ]
  219. },
  220. "regex": [
  221. "~^(?|\/newPrefix\/v1\/petLeevel\/([A-Za-z]+)\/)$~x"
  222. ],
  223. "map": [
  224. {
  225. "2": "\/newPrefix\/v1\/petLeevel\/{petId:[A-Za-z]+}\/"
  226. }
  227. ]
  228. }
  229. }
  230. }
  231. }
  232. }
  1. public function testBaseRouterData(): void
  2. {
  3. $container = $this->createContainer();
  4. $container->singleton('router', $router = $this->createRouter($container));
  5. $provider = new RouterProviderAnnotation($container);
  6. $this->assertNull($provider->register());
  7. $this->assertNull($provider->bootstrap());
  8. $data = file_get_contents(__DIR__.'/Apps/AppForAnnotation/data.json');
  9. $this->assertSame(
  10. $data,
  11. $this->varJson(
  12. [
  13. 'base_paths' => $router->getBasePaths(),
  14. 'groups' => $router->getGroups(),
  15. 'routers' => $router->getRouters(),
  16. ]
  17. )
  18. );
  19. }

基本使用

fixture 定义

路由定义

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Pet::petLeevel
  2. /**
  3. * @OA\Get(
  4. * path="/api/v1/petLeevel/{petId:[A-Za-z]+}/",
  5. * tags={"pet"},
  6. * summary="Just test the router",
  7. * operationId="petLeevel",
  8. * @OA\Parameter(
  9. * name="petId",
  10. * in="path",
  11. * description="ID of pet to return",
  12. * required=true,
  13. * @OA\Schema(
  14. * type="integer",
  15. * format="int64"
  16. * )
  17. * ),
  18. * @OA\Response(
  19. * response=405,
  20. * description="Invalid input"
  21. * ),
  22. * security={
  23. * {"petstore_auth": {"write:pets", "read:pets"}}
  24. * },
  25. * requestBody={"$ref": "#/components/requestBodies/Pet"},
  26. * leevelBind="\Tests\Router\Controllers\Annotation\PetLeevel"
  27. * )
  28. */
  29. public function petLeevel();

控制器

  1. namespace Tests\Router\Controllers\Annotation;
  2. use Leevel\Di\IContainer;
  3. class PetLeevel
  4. {
  5. public function handle(IContainer $container): string
  6. {
  7. return 'hello plus for petLeevel, attributes petId is '.
  8. $container
  9. ->make('request')
  10. ->attributes
  11. ->get('petId');
  12. }
  13. }
  1. public function testMatchedPetLeevel(): void
  2. {
  3. $pathInfo = '/api/v1/petLeevel/hello';
  4. $attributes = [];
  5. $method = 'GET';
  6. $controllerDir = 'Controllers';
  7. $container = $this->createContainer();
  8. $request = $this->createRequest($pathInfo, $attributes, $method);
  9. $container->singleton('router', $router = $this->createRouter($container));
  10. $container->instance('request', $request);
  11. $container->instance(IContainer::class, $container);
  12. $provider = new RouterProviderAnnotation($container);
  13. $router->setControllerDir($controllerDir);
  14. $provider->register();
  15. $provider->bootstrap();
  16. if (isset($GLOBALS['demo_middlewares'])) {
  17. unset($GLOBALS['demo_middlewares']);
  18. }
  19. $response = $router->dispatch($request);
  20. $this->assertInstanceof(Response::class, $response);
  21. $this->assertSame('hello plus for petLeevel, attributes petId is hello', $response->getContent());
  22. $data = <<<'eot'
  23. [
  24. "DemoForAll::handle",
  25. "Demo2::handle"
  26. ]
  27. eot;
  28. $this->assertSame(
  29. $data,
  30. $this->varJson(
  31. $GLOBALS['demo_middlewares']
  32. )
  33. );
  34. $router->throughMiddleware($request, [
  35. $response,
  36. ]);
  37. $data = <<<'eot'
  38. [
  39. "DemoForAll::handle",
  40. "Demo2::handle",
  41. "DemoForAll::terminate",
  42. "Demo1::terminate",
  43. "Demo2::terminate"
  44. ]
  45. eot;
  46. $this->assertSame(
  47. $data,
  48. $this->varJson(
  49. $GLOBALS['demo_middlewares'],
  50. 1
  51. )
  52. );
  53. unset($GLOBALS['demo_middlewares']);
  54. }

基础路径匹配

fixture 定义

路由定义

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\BasePath::foo
  2. /**
  3. * @OA\Get(
  4. * path="/basePath/normalize/",
  5. * tags={"pet"},
  6. * summary="Just test the router",
  7. * operationId="petLeevel",
  8. * @OA\Parameter(
  9. * name="petId",
  10. * in="path",
  11. * description="ID of pet to return",
  12. * required=true,
  13. * @OA\Schema(
  14. * type="integer",
  15. * format="int64"
  16. * )
  17. * ),
  18. * @OA\Response(
  19. * response=405,
  20. * description="Invalid input"
  21. * ),
  22. * security={
  23. * {"petstore_auth": {"write:pets", "read:pets"}}
  24. * },
  25. * requestBody={"$ref": "#/components/requestBodies/Pet"},
  26. * leevelBind="Tests\Router\Controllers\Annotation\BasePath@normalize"
  27. * )
  28. */
  29. public function foo();

控制器

  1. namespace Tests\Router\Controllers\Annotation;
  2. class BasePath
  3. {
  4. public function normalize(): string
  5. {
  6. return 'hello plus for basePath normalize';
  7. }
  8. }
  1. public function testMatchedBasePathNormalize(): void
  2. {
  3. $pathInfo = '/basePath/normalize';
  4. $attributes = [];
  5. $method = 'GET';
  6. $controllerDir = 'Controllers';
  7. $container = $this->createContainer();
  8. $request = $this->createRequest($pathInfo, $attributes, $method);
  9. $container->singleton('router', $router = $this->createRouter($container));
  10. $container->instance('request', $request);
  11. $container->instance(IContainer::class, $container);
  12. $provider = new RouterProviderAnnotation($container);
  13. $provider->register();
  14. $provider->bootstrap();
  15. if (isset($GLOBALS['demo_middlewares'])) {
  16. unset($GLOBALS['demo_middlewares']);
  17. }
  18. $response = $router->dispatch($request);
  19. $this->assertInstanceof(Response::class, $response);
  20. $this->assertSame('hello plus for basePath normalize', $response->getContent());
  21. $data = <<<'eot'
  22. [
  23. "DemoForAll::handle",
  24. "DemoForBasePath::handle"
  25. ]
  26. eot;
  27. $this->assertSame(
  28. $data,
  29. $this->varJson(
  30. $GLOBALS['demo_middlewares']
  31. )
  32. );
  33. $router->throughMiddleware($request, [
  34. $response,
  35. ]);
  36. $data = <<<'eot'
  37. [
  38. "DemoForAll::handle",
  39. "DemoForBasePath::handle",
  40. "DemoForAll::terminate"
  41. ]
  42. eot;
  43. $this->assertSame(
  44. $data,
  45. $this->varJson(
  46. $GLOBALS['demo_middlewares'],
  47. 1
  48. )
  49. );
  50. unset($GLOBALS['demo_middlewares']);
  51. }

leevelScheme 协议匹配

fixture 定义

路由定义

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Scheme::barMatchedScheme
  2. /**
  3. * @OA\Get(
  4. * path="/scheme/test2",
  5. * tags={"pet"},
  6. * summary="Just test the router",
  7. * operationId="petLeevel",
  8. * @OA\Parameter(
  9. * name="petId",
  10. * in="path",
  11. * description="ID of pet to return",
  12. * required=true,
  13. * @OA\Schema(
  14. * type="integer",
  15. * format="int64"
  16. * )
  17. * ),
  18. * @OA\Response(
  19. * response=405,
  20. * description="Invalid input"
  21. * ),
  22. * security={
  23. * {"petstore_auth": {"write:pets", "read:pets"}}
  24. * },
  25. * requestBody={"$ref": "#/components/requestBodies/Pet"},
  26. * leevelScheme="http"
  27. * )
  28. */
  29. public function barMatchedScheme();

控制器

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Scheme::barMatchedScheme
  2. public function barMatchedScheme()
  3. {
  4. return 'barMatchedScheme';
  5. }
  1. public function testMatchedAndSchemeMatched(): void
  2. {
  3. $pathInfo = '/scheme/test2';
  4. $attributes = [];
  5. $method = 'GET';
  6. $controllerDir = 'Controllers';
  7. $container = $this->createContainer();
  8. $request = $this->createRequest($pathInfo, $attributes, $method);
  9. $container->singleton('router', $router = $this->createRouter($container));
  10. $container->instance('request', $request);
  11. $container->instance(IContainer::class, $container);
  12. $provider = new RouterProviderAnnotation($container);
  13. $router->setControllerDir($controllerDir);
  14. $provider->register();
  15. $provider->bootstrap();
  16. $result = $router->dispatch($request);
  17. $this->assertSame('barMatchedScheme', $result->getContent());
  18. }

leevelDomain 域名匹配

fixture 定义

路由定义

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Domain::barMatchedDomain
  2. /**
  3. * @OA\Get(
  4. * path="/domain/test2",
  5. * tags={"pet"},
  6. * summary="Just test the router",
  7. * operationId="petLeevel",
  8. * @OA\Parameter(
  9. * name="petId",
  10. * in="path",
  11. * description="ID of pet to return",
  12. * required=true,
  13. * @OA\Schema(
  14. * type="integer",
  15. * format="int64"
  16. * )
  17. * ),
  18. * @OA\Response(
  19. * response=405,
  20. * description="Invalid input"
  21. * ),
  22. * security={
  23. * {"petstore_auth": {"write:pets", "read:pets"}}
  24. * },
  25. * requestBody={"$ref": "#/components/requestBodies/Pet"},
  26. * leevelDomain="queryphp.com"
  27. * )
  28. */
  29. public function barMatchedDomain();

控制器

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Domain::barMatchedDomain
  2. public function barMatchedDomain()
  3. {
  4. return 'barMatchedDomain';
  5. }
  1. public function testMatchedAndDomainMatched(): void
  2. {
  3. $pathInfo = '/domain/test2';
  4. $attributes = [];
  5. $method = 'GET';
  6. $controllerDir = 'Controllers';
  7. $container = $this->createContainer();
  8. $request = new Request([], [], $attributes, [], [], ['HTTP_HOST' => 'queryphp.com']);
  9. $request->setPathInfo($pathInfo);
  10. $request->setMethod($method);
  11. $container->singleton('router', $router = $this->createRouter($container));
  12. $container->instance('request', $request);
  13. $container->instance(IContainer::class, $container);
  14. $provider = new RouterProviderAnnotation($container);
  15. $router->setControllerDir($controllerDir);
  16. $provider->register();
  17. $provider->bootstrap();
  18. $result = $router->dispatch($request);
  19. $this->assertSame('barMatchedDomain', $result->getContent());
  20. }

leevelDomain 域名匹配支持变量

fixture 定义

路由定义

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Domain::barMatchedDomainWithVar
  2. /**
  3. * @OA\Get(
  4. * path="/domain/test3",
  5. * tags={"pet"},
  6. * summary="Just test the router",
  7. * operationId="petLeevel",
  8. * @OA\Parameter(
  9. * name="petId",
  10. * in="path",
  11. * description="ID of pet to return",
  12. * required=true,
  13. * @OA\Schema(
  14. * type="integer",
  15. * format="int64"
  16. * )
  17. * ),
  18. * @OA\Response(
  19. * response=405,
  20. * description="Invalid input"
  21. * ),
  22. * security={
  23. * {"petstore_auth": {"write:pets", "read:pets"}}
  24. * },
  25. * requestBody={"$ref": "#/components/requestBodies/Pet"},
  26. * leevelDomain="{subdomain:[A-Za-z]+}-vip.{domain}.queryphp.com"
  27. * )
  28. */
  29. public function barMatchedDomainWithVar(Request $request);

控制器

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Domain::barMatchedDomainWithVar
  2. public function barMatchedDomainWithVar(Request $request)
  3. {
  4. return 'barMatchedDomainWithVar and attributes are '.
  5. json_encode($request->attributes->all());
  6. }
  1. public function testMatchedAndDomainWithVarMatched(): void
  2. {
  3. $pathInfo = '/domain/test3';
  4. $attributes = [];
  5. $method = 'GET';
  6. $controllerDir = 'Controllers';
  7. $container = $this->createContainer();
  8. $request = new Request([], [], $attributes, [], [], ['HTTP_HOST' => 'foo-vip.bar.queryphp.com']);
  9. $request->setPathInfo($pathInfo);
  10. $request->setMethod($method);
  11. $container->instance(Request::class, $request);
  12. $container->singleton('router', $router = $this->createRouter($container));
  13. $container->instance('request', $request);
  14. $container->instance(IContainer::class, $container);
  15. $provider = new RouterProviderAnnotation($container);
  16. $router->setControllerDir($controllerDir);
  17. $provider->register();
  18. $provider->bootstrap();
  19. $result = $router->dispatch($request);
  20. $this->assertSame('barMatchedDomainWithVar and attributes are {"subdomain":"foo","domain":"bar"}', $result->getContent());
  21. }

leevelPort 端口匹配

fixture 定义

路由定义

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Port::barMatchedPort
  2. /**
  3. * @OA\Get(
  4. * path="/port/test2",
  5. * tags={"pet"},
  6. * summary="Just test the router",
  7. * operationId="petLeevel",
  8. * @OA\Parameter(
  9. * name="petId",
  10. * in="path",
  11. * description="ID of pet to return",
  12. * required=true,
  13. * @OA\Schema(
  14. * type="integer",
  15. * format="int64"
  16. * )
  17. * ),
  18. * @OA\Response(
  19. * response=405,
  20. * description="Invalid input"
  21. * ),
  22. * security={
  23. * {"petstore_auth": {"write:pets", "read:pets"}}
  24. * },
  25. * requestBody={"$ref": "#/components/requestBodies/Pet"},
  26. * leevelPort=9527
  27. * )
  28. */
  29. public function barMatchedPort();

控制器

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Port::barMatchedPort
  2. public function barMatchedPort()
  3. {
  4. return 'barMatchedPort';
  5. }
  1. public function testMatchedAndPortMatched(): void
  2. {
  3. $pathInfo = '/port/test2';
  4. $attributes = [];
  5. $method = 'GET';
  6. $controllerDir = 'Controllers';
  7. $container = $this->createContainer();
  8. $request = new Request([], [], $attributes, [], [], ['SERVER_PORT' => '9527']);
  9. $request->setPathInfo($pathInfo);
  10. $request->setMethod($method);
  11. $container->singleton('router', $router = $this->createRouter($container));
  12. $container->instance('request', $request);
  13. $container->instance(IContainer::class, $container);
  14. $provider = new RouterProviderAnnotation($container);
  15. $router->setControllerDir($controllerDir);
  16. $provider->register();
  17. $provider->bootstrap();
  18. $result = $router->dispatch($request);
  19. $this->assertSame('barMatchedPort', $result->getContent());
  20. }

leevelAttributes 扩展变量匹配

fixture 定义

路由定义

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\ExtendVar::withExtendVar
  2. /**
  3. * @OA\Get(
  4. * path="/extendVar/test",
  5. * tags={"pet"},
  6. * summary="Just test the router",
  7. * operationId="petLeevel",
  8. * @OA\Parameter(
  9. * name="petId",
  10. * in="path",
  11. * description="ID of pet to return",
  12. * required=true,
  13. * @OA\Schema(
  14. * type="integer",
  15. * format="int64"
  16. * )
  17. * ),
  18. * @OA\Response(
  19. * response=405,
  20. * description="Invalid input"
  21. * ),
  22. * security={
  23. * {"petstore_auth": {"write:pets", "read:pets"}}
  24. * },
  25. * requestBody={"$ref": "#/components/requestBodies/Pet"},
  26. * leevelAttributes={"args1": "hello", "args2": "world"}
  27. * )
  28. */
  29. public function withExtendVar(Request $request);

控制器

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\ExtendVar::withExtendVar
  2. public function withExtendVar(Request $request)
  3. {
  4. return 'withExtendVar and attributes are '.
  5. json_encode($request->attributes->all());
  6. }
  1. public function testMatchedWithExtendVar(): void
  2. {
  3. $pathInfo = '/extendVar/test';
  4. $attributes = [];
  5. $method = 'GET';
  6. $controllerDir = 'Controllers';
  7. $container = $this->createContainer();
  8. $request = new Request([], [], $attributes);
  9. $request->setPathInfo($pathInfo);
  10. $request->setMethod($method);
  11. $container->singleton('router', $router = $this->createRouter($container));
  12. $container->instance(Request::class, $request);
  13. $container->instance(IContainer::class, $container);
  14. $provider = new RouterProviderAnnotation($container);
  15. $router->setControllerDir($controllerDir);
  16. $provider->register();
  17. $provider->bootstrap();
  18. $result = $router->dispatch($request);
  19. $this->assertSame('withExtendVar and attributes are {"args1":"hello","args2":"world"}', $result->getContent());
  20. }

leevelMiddlewares 中间件匹配

fixture 定义

路由定义

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Middleware::foo
  2. /**
  3. * @OA\Get(
  4. * path="/middleware/test",
  5. * tags={"pet"},
  6. * summary="Just test the router",
  7. * operationId="petLeevel",
  8. * @OA\Parameter(
  9. * name="petId",
  10. * in="path",
  11. * description="ID of pet to return",
  12. * required=true,
  13. * @OA\Schema(
  14. * type="integer",
  15. * format="int64"
  16. * )
  17. * ),
  18. * @OA\Response(
  19. * response=405,
  20. * description="Invalid input"
  21. * ),
  22. * security={
  23. * {"petstore_auth": {"write:pets", "read:pets"}}
  24. * },
  25. * requestBody={"$ref": "#/components/requestBodies/Pet"},
  26. * leevelMiddlewares="group1"
  27. * )
  28. */
  29. public function foo();

控制器

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Middleware::foo
  2. public function foo()
  3. {
  4. return 'Middleware matched';
  5. }
  1. public function testMiddleware(): void
  2. {
  3. $pathInfo = '/middleware/test';
  4. $attributes = [];
  5. $method = 'GET';
  6. $controllerDir = 'Controllers';
  7. $container = $this->createContainer();
  8. $request = new Request([], [], $attributes);
  9. $request->setPathInfo($pathInfo);
  10. $request->setMethod($method);
  11. $container->singleton('router', $router = $this->createRouter($container));
  12. $container->instance(Request::class, $request);
  13. $container->instance(IContainer::class, $container);
  14. $provider = new RouterProviderAnnotation($container);
  15. $router->setControllerDir($controllerDir);
  16. $provider->register();
  17. $provider->bootstrap();
  18. if (isset($GLOBALS['demo_middlewares'])) {
  19. unset($GLOBALS['demo_middlewares']);
  20. }
  21. $result = $router->dispatch($request);
  22. $data = <<<'eot'
  23. [
  24. "DemoForAll::handle",
  25. "Demo2::handle"
  26. ]
  27. eot;
  28. $this->assertSame(
  29. $data,
  30. $this->varJson(
  31. $GLOBALS['demo_middlewares']
  32. )
  33. );
  34. $router->throughMiddleware($request, [
  35. $result,
  36. ]);
  37. $data = <<<'eot'
  38. [
  39. "DemoForAll::handle",
  40. "Demo2::handle",
  41. "DemoForAll::terminate",
  42. "Demo1::terminate",
  43. "Demo2::terminate"
  44. ]
  45. eot;
  46. $this->assertSame(
  47. $data,
  48. $this->varJson(
  49. $GLOBALS['demo_middlewares'],
  50. 1
  51. )
  52. );
  53. $this->assertSame('Middleware matched', $result->getContent());
  54. unset($GLOBALS['demo_middlewares']);
  55. }

leevelMiddlewares 中间件匹配支持数组

fixture 定义

路由定义

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Middleware::bar
  2. /**
  3. * @OA\Get(
  4. * path="/middleware/test2",
  5. * tags={"pet"},
  6. * summary="Just test the router",
  7. * operationId="petLeevel",
  8. * @OA\Parameter(
  9. * name="petId",
  10. * in="path",
  11. * description="ID of pet to return",
  12. * required=true,
  13. * @OA\Schema(
  14. * type="integer",
  15. * format="int64"
  16. * )
  17. * ),
  18. * @OA\Response(
  19. * response=405,
  20. * description="Invalid input"
  21. * ),
  22. * security={
  23. * {"petstore_auth": {"write:pets", "read:pets"}}
  24. * },
  25. * requestBody={"$ref": "#/components/requestBodies/Pet"},
  26. * leevelMiddlewares={"group1", "group2"}
  27. * )
  28. */
  29. public function bar();

控制器

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Middleware::bar
  2. public function bar()
  3. {
  4. return 'Middleware matched 2';
  5. }
  1. public function testMiddleware2(): void
  2. {
  3. $pathInfo = '/middleware/test2';
  4. $attributes = [];
  5. $method = 'GET';
  6. $controllerDir = 'Controllers';
  7. $container = $this->createContainer();
  8. $request = new Request([], [], $attributes);
  9. $request->setPathInfo($pathInfo);
  10. $request->setMethod($method);
  11. $container->singleton('router', $router = $this->createRouter($container));
  12. $container->instance(Request::class, $request);
  13. $container->instance(IContainer::class, $container);
  14. $provider = new RouterProviderAnnotation($container);
  15. $router->setControllerDir($controllerDir);
  16. $provider->register();
  17. $provider->bootstrap();
  18. if (isset($GLOBALS['demo_middlewares'])) {
  19. unset($GLOBALS['demo_middlewares']);
  20. }
  21. $result = $router->dispatch($request);
  22. $data = <<<'eot'
  23. [
  24. "DemoForAll::handle",
  25. "Demo2::handle",
  26. "Demo3::handle(arg1:10,arg2:world)"
  27. ]
  28. eot;
  29. $this->assertSame(
  30. $data,
  31. $this->varJson(
  32. $GLOBALS['demo_middlewares']
  33. )
  34. );
  35. $router->throughMiddleware($request, [
  36. $result,
  37. ]);
  38. $data = <<<'eot'
  39. [
  40. "DemoForAll::handle",
  41. "Demo2::handle",
  42. "Demo3::handle(arg1:10,arg2:world)",
  43. "DemoForAll::terminate",
  44. "Demo1::terminate",
  45. "Demo2::terminate"
  46. ]
  47. eot;
  48. $this->assertSame(
  49. $data,
  50. $this->varJson(
  51. $GLOBALS['demo_middlewares'],
  52. 1
  53. )
  54. );
  55. $this->assertSame('Middleware matched 2', $result->getContent());
  56. unset($GLOBALS['demo_middlewares']);
  57. }

leevelMiddlewares 中间件匹配支持类名

fixture 定义

路由定义

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Middleware::world
  2. /**
  3. * @OA\Get(
  4. * path="/middleware/test4",
  5. * tags={"pet"},
  6. * summary="Just test the router",
  7. * operationId="petLeevel",
  8. * @OA\Parameter(
  9. * name="petId",
  10. * in="path",
  11. * description="ID of pet to return",
  12. * required=true,
  13. * @OA\Schema(
  14. * type="integer",
  15. * format="int64"
  16. * )
  17. * ),
  18. * @OA\Response(
  19. * response=405,
  20. * description="Invalid input"
  21. * ),
  22. * security={
  23. * {"petstore_auth": {"write:pets", "read:pets"}}
  24. * },
  25. * requestBody={"$ref": "#/components/requestBodies/Pet"},
  26. * leevelMiddlewares={"Tests\Router\Middlewares\Demo1"}
  27. * )
  28. */
  29. public function world();

控制器

  1. # Tests\Router\Apps\AppForAnnotation\Controllers\Middleware::world
  2. public function world()
  3. {
  4. return 'Middleware matched 4';
  5. }
  1. public function testMiddleware4(): void
  2. {
  3. $pathInfo = '/middleware/test4';
  4. $attributes = [];
  5. $method = 'GET';
  6. $controllerDir = 'Controllers';
  7. $container = $this->createContainer();
  8. $request = new Request([], [], $attributes);
  9. $request->setPathInfo($pathInfo);
  10. $request->setMethod($method);
  11. $container->singleton('router', $router = $this->createRouter($container));
  12. $container->instance(Request::class, $request);
  13. $container->instance(IContainer::class, $container);
  14. $provider = new RouterProviderAnnotation($container);
  15. $router->setControllerDir($controllerDir);
  16. $provider->register();
  17. $provider->bootstrap();
  18. if (isset($GLOBALS['demo_middlewares'])) {
  19. unset($GLOBALS['demo_middlewares']);
  20. }
  21. $result = $router->dispatch($request);
  22. $data = <<<'eot'
  23. [
  24. "DemoForAll::handle"
  25. ]
  26. eot;
  27. $this->assertSame(
  28. $data,
  29. $this->varJson(
  30. $GLOBALS['demo_middlewares']
  31. )
  32. );
  33. $router->throughMiddleware($request, [
  34. $result,
  35. ]);
  36. $data = <<<'eot'
  37. [
  38. "DemoForAll::handle",
  39. "DemoForAll::terminate",
  40. "Demo1::terminate"
  41. ]
  42. eot;
  43. $this->assertSame(
  44. $data,
  45. $this->varJson(
  46. $GLOBALS['demo_middlewares'],
  47. 1
  48. )
  49. );
  50. $this->assertSame('Middleware matched 4', $result->getContent());
  51. unset($GLOBALS['demo_middlewares']);
  52. }