api de gestion de ticket, basé sur php-crud-api. Le but est de décorrélé les outils de gestion des données, afin
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SimpleRouter.php 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. <?php
  2. namespace Tqdev\PhpCrudApi\Middleware\Router;
  3. use Psr\Http\Message\ResponseInterface;
  4. use Psr\Http\Message\ServerRequestInterface;
  5. use Tqdev\PhpCrudApi\Cache\Cache;
  6. use Tqdev\PhpCrudApi\Controller\Responder;
  7. use Tqdev\PhpCrudApi\Middleware\Base\Middleware;
  8. use Tqdev\PhpCrudApi\Record\ErrorCode;
  9. use Tqdev\PhpCrudApi\Record\PathTree;
  10. use Tqdev\PhpCrudApi\RequestUtils;
  11. use Tqdev\PhpCrudApi\ResponseUtils;
  12. class SimpleRouter implements Router
  13. {
  14. private $basePath;
  15. private $responder;
  16. private $cache;
  17. private $ttl;
  18. private $debug;
  19. private $registration;
  20. private $routes;
  21. private $routeHandlers;
  22. private $middlewares;
  23. public function __construct(string $basePath, Responder $responder, Cache $cache, int $ttl, bool $debug)
  24. {
  25. $this->basePath = $this->detectBasePath($basePath);
  26. $this->responder = $responder;
  27. $this->cache = $cache;
  28. $this->ttl = $ttl;
  29. $this->debug = $debug;
  30. $this->registration = true;
  31. $this->routes = $this->loadPathTree();
  32. $this->routeHandlers = [];
  33. $this->middlewares = array();
  34. }
  35. private function detectBasePath(string $basePath): string
  36. {
  37. if ($basePath) {
  38. return $basePath;
  39. }
  40. if (isset($_SERVER['REQUEST_URI'])) {
  41. $fullPath = urldecode(explode('?', $_SERVER['REQUEST_URI'])[0]);
  42. if (isset($_SERVER['PATH_INFO'])) {
  43. $path = $_SERVER['PATH_INFO'];
  44. if (substr($fullPath, -1 * strlen($path)) == $path) {
  45. return substr($fullPath, 0, -1 * strlen($path));
  46. }
  47. }
  48. return $fullPath;
  49. }
  50. return '/';
  51. }
  52. private function loadPathTree(): PathTree
  53. {
  54. $data = $this->cache->get('PathTree');
  55. if ($data != '') {
  56. $tree = PathTree::fromJson(json_decode(gzuncompress($data)));
  57. $this->registration = false;
  58. } else {
  59. $tree = new PathTree();
  60. }
  61. return $tree;
  62. }
  63. public function register(string $method, string $path, array $handler)
  64. {
  65. $routeNumber = count($this->routeHandlers);
  66. $this->routeHandlers[$routeNumber] = $handler;
  67. if ($this->registration) {
  68. $path = trim($path, '/');
  69. $parts = array();
  70. if ($path) {
  71. $parts = explode('/', $path);
  72. }
  73. array_unshift($parts, $method);
  74. $this->routes->put($parts, $routeNumber);
  75. }
  76. }
  77. public function load(Middleware $middleware) /*: void*/
  78. {
  79. array_push($this->middlewares, $middleware);
  80. }
  81. public function route(ServerRequestInterface $request): ResponseInterface
  82. {
  83. if ($this->registration) {
  84. $data = gzcompress(json_encode($this->routes, JSON_UNESCAPED_UNICODE));
  85. $this->cache->set('PathTree', $data, $this->ttl);
  86. }
  87. return $this->handle($request);
  88. }
  89. private function getRouteNumbers(ServerRequestInterface $request): array
  90. {
  91. $method = strtoupper($request->getMethod());
  92. $path = array();
  93. $segment = $method;
  94. for ($i = 1; $segment; $i++) {
  95. array_push($path, $segment);
  96. $segment = RequestUtils::getPathSegment($request, $i);
  97. }
  98. return $this->routes->match($path);
  99. }
  100. private function removeBasePath(ServerRequestInterface $request): ServerRequestInterface
  101. {
  102. $path = $request->getUri()->getPath();
  103. $basePath = rtrim($this->basePath, '/');
  104. if (substr($path, 0, strlen($basePath)) == $basePath) {
  105. $path = substr($path, strlen($basePath));
  106. $request = $request->withUri($request->getUri()->withPath($path));
  107. }
  108. return $request;
  109. }
  110. public function getBasePath(): string
  111. {
  112. return $this->basePath;
  113. }
  114. public function handle(ServerRequestInterface $request): ResponseInterface
  115. {
  116. $request = $this->removeBasePath($request);
  117. if (count($this->middlewares)) {
  118. $handler = array_pop($this->middlewares);
  119. return $handler->process($request, $this);
  120. }
  121. $routeNumbers = $this->getRouteNumbers($request);
  122. if (count($routeNumbers) == 0) {
  123. return $this->responder->error(ErrorCode::ROUTE_NOT_FOUND, $request->getUri()->getPath());
  124. }
  125. try {
  126. $response = call_user_func($this->routeHandlers[$routeNumbers[0]], $request);
  127. } catch (\PDOException $e) {
  128. if (strpos(strtolower($e->getMessage()), 'duplicate') !== false) {
  129. $response = $this->responder->error(ErrorCode::DUPLICATE_KEY_EXCEPTION, '');
  130. } elseif (strpos(strtolower($e->getMessage()), 'default value') !== false) {
  131. $response = $this->responder->error(ErrorCode::DATA_INTEGRITY_VIOLATION, '');
  132. } elseif (strpos(strtolower($e->getMessage()), 'allow nulls') !== false) {
  133. $response = $this->responder->error(ErrorCode::DATA_INTEGRITY_VIOLATION, '');
  134. } elseif (strpos(strtolower($e->getMessage()), 'constraint') !== false) {
  135. $response = $this->responder->error(ErrorCode::DATA_INTEGRITY_VIOLATION, '');
  136. } else {
  137. $response = $this->responder->error(ErrorCode::ERROR_NOT_FOUND, '');
  138. }
  139. if ($this->debug) {
  140. $response = ResponseUtils::addExceptionHeaders($response, $e);
  141. }
  142. }
  143. return $response;
  144. }
  145. }