PathMatch.php 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. <?php
  2. declare(strict_types=1);
  3. namespace Phper666\JWTAuth;
  4. use FastRoute\RouteParser\Std;
  5. /**
  6. * 此类是根据nikic/fastroute包提供的路由匹配而修改的
  7. * 里面修改了一些路由匹配逻辑,可以支持hyperf路由正则等配置
  8. * 可以直接在no_check_route里面配置正则路由忽略掉不需要检查的路由
  9. */
  10. class PathMatch
  11. {
  12. protected function getApproxChunkSize(): int
  13. {
  14. return 10;
  15. }
  16. protected function computeChunkSize($count)
  17. {
  18. $numParts = max(1, round($count / $this->getApproxChunkSize()));
  19. return (int) ceil($count / $numParts);
  20. }
  21. protected function processChunk($regexToRoutesMap)
  22. {
  23. $routeMap = [];
  24. $regexes = [];
  25. $numGroups = 0;
  26. foreach ($regexToRoutesMap as $regex => $variables) {
  27. $numVariables = count($variables);
  28. $numGroups = max($numGroups, $numVariables);
  29. $regexes[] = $regex . str_repeat('()', $numGroups - $numVariables);
  30. $routeMap[$numGroups + 1] = $variables;
  31. ++$numGroups;
  32. }
  33. $regex = '~^(?|' . implode('|', $regexes) . ')$~';
  34. return ['regex' => $regex, 'routeMap' => $routeMap];
  35. }
  36. protected function buildRegexForRoute(array $parseRouteData): array
  37. {
  38. $regex = '';
  39. $variables = [];
  40. foreach ($parseRouteData as $part) {
  41. if (is_string($part)) {
  42. $regex .= preg_quote($part, '~');
  43. continue;
  44. }
  45. [$varName, $regexPart] = $part;
  46. $variables[$varName] = $varName;
  47. $regex .= '(' . $regexPart . ')';
  48. }
  49. return [$regex, $variables];
  50. }
  51. public function matchRoute(array $noCheckRouteData, string $method, string $path): bool
  52. {
  53. // 为空直接返回路由不匹配
  54. if (empty($noCheckRouteData)) {
  55. return false;
  56. }
  57. $std = new Std();
  58. foreach ($noCheckRouteData as $route) {
  59. $noCheckMethod = $route[0] ?? null;
  60. $noCheckPath = $route[1] ?? null;
  61. if ($noCheckMethod == null || $noCheckPath == null) {
  62. // TODO 抛出异常
  63. }
  64. $noCheckMethod = strtoupper($noCheckMethod);
  65. $method = strtoupper($method);
  66. // 直接返回路由已经被匹配到
  67. if (($noCheckMethod == $method || $noCheckMethod == "**")) {
  68. if ($noCheckPath == "/**") {
  69. return true;
  70. }
  71. if ($noCheckPath == $path) {
  72. return true;
  73. }
  74. if ($noCheckPath != $path) {
  75. // parse route
  76. $parseRouteData = $std->parse($noCheckPath);
  77. foreach ($parseRouteData as $routeData) {
  78. [$regex, $variables] = $this->buildRegexForRoute($routeData);
  79. $regexToRoutesMap[$regex] = $variables;
  80. $chunkSize = $this->computeChunkSize(count($regexToRoutesMap));
  81. $chunks = array_chunk($regexToRoutesMap, $chunkSize, true);
  82. $buildRegexForRouteData = array_map([$this, 'processChunk'], $chunks);
  83. foreach ($buildRegexForRouteData as $data) {
  84. if (!preg_match($data['regex'], $path)) {
  85. continue;
  86. }
  87. return true;
  88. }
  89. }
  90. }
  91. }
  92. }
  93. return false;
  94. }
  95. }