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.

LuRoute.php 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. <?php
  2. namespace Luticate\Utils\Controller;
  3. use FastRoute\Dispatcher;
  4. use Luticate\Utils\Business\LuBusinessException;
  5. use Luticate\Utils\Business\LuMethodDocParser;
  6. use Luticate\Utils\Dbo\LuDboConstraintException;
  7. use Luticate\Utils\Dbo\LuDboDeserializeException;
  8. use FastRoute\DataGenerator\GroupCountBased as DataGeneratorGroupCountBased;
  9. use FastRoute\Dispatcher\GroupCountBased as DispatcherGroupCountBased;
  10. use FastRoute\RouteCollector;
  11. use FastRoute\RouteParser\Std;
  12. use Luticate\Utils\Dbo\LuMiddlewareDbo;
  13. use Luticate\Utils\Dbo\LuRouteDbo;
  14. use Luticate\Utils\Middleware\LuAbstractMiddleware;
  15. class LuRoute {
  16. const REG_UINT = "[0-9]+";
  17. const REG_INT = "\\-?" . self::REG_UINT;
  18. const REG_BOOL = "true|false";
  19. /**
  20. * @var DispatcherGroupCountBased
  21. */
  22. private $_dispatcher = null;
  23. /**
  24. * @var LuMiddlewareDbo[]
  25. */
  26. private $_middlewares = [];
  27. /**
  28. * @var LuRouteDbo[]
  29. */
  30. private $routes = array();
  31. public function __construct()
  32. {
  33. }
  34. public function getRoutes()
  35. {
  36. return $this->routes;
  37. }
  38. /**
  39. * @param $middleware mixed
  40. */
  41. public function addMiddleware($middleware)
  42. {
  43. if (is_string($middleware)) {
  44. $this->_middlewares[] = new LuMiddlewareDbo($middleware);
  45. }
  46. else if (is_array($middleware)) {
  47. $this->_middlewares[] = LuMiddlewareDbo::jsonDeserialize($middleware);
  48. }
  49. else {
  50. $this->_middlewares[] = $middleware;
  51. }
  52. }
  53. public function setup()
  54. {
  55. $routeCollector = new RouteCollector(new Std(), new DataGeneratorGroupCountBased());
  56. $router = $this;
  57. foreach ($this->getRoutes() as $route) {
  58. $routeCollector->addRoute($route->getMethod(), $route->getUrl(), function($parameters) use($route, $router)
  59. {
  60. return $router->execute($route, $parameters);
  61. });
  62. }
  63. $this->_dispatcher = new DispatcherGroupCountBased($routeCollector->getData());
  64. }
  65. public function execute(LuRouteDbo $route, $parameters)
  66. {
  67. $middlewares = [];
  68. /**
  69. * @var $middlewareDbo LuMiddlewareDbo
  70. */
  71. foreach (array_merge($this->_middlewares, $route->getMiddlewares()) as $middlewareDbo) {
  72. $middlewareName = $middlewareDbo->getMiddleware();
  73. /**
  74. * @var $middleware LuAbstractMiddleware
  75. */
  76. $middleware = new $middlewareName();
  77. $parameters["_middleware"] = $middlewareDbo->getParameters();
  78. foreach ($route->getParameters() as $key => $value) {
  79. $parameters["_middleware"][$key] = $value;
  80. }
  81. $parameters = $middleware->onBefore($route, $parameters);
  82. $middlewares[] = [
  83. 'middleware' => $middleware,
  84. 'dbo' => $middlewareDbo
  85. ];
  86. }
  87. unset($parameters["_middleware"]);
  88. $reflect = new \ReflectionMethod($route->getController(), $route->getAction());
  89. $params = $reflect->getParameters();
  90. $docs = new LuMethodDocParser($reflect->getDocComment());
  91. $docs->parse();
  92. $args = array();
  93. foreach ($params as $param) {
  94. try {
  95. if ($param->isOptional()) {
  96. $value = null;
  97. if (array_key_exists($param->getName(), $parameters)) {
  98. $value = $this->getParam($param, $parameters[$param->getName()]);
  99. }
  100. else {
  101. $value = $param->getDefaultValue();
  102. }
  103. }
  104. else {
  105. if (array_key_exists($param->getName(), $parameters)) {
  106. $value = $this->getParam($param, $parameters[$param->getName()]);
  107. }
  108. else {
  109. throw new LuDboDeserializeException("Missing parameter '" . $param->getName() . "'", 400);
  110. }
  111. }
  112. if (array_key_exists($param->getName(), $docs->getParams())) {
  113. $doc = $docs->getParams()[$param->getName()];
  114. if (is_null($value) && $doc->isNotNull()) {
  115. throw new LuDboConstraintException("Parameter '" . $param->getName() . "' can not be null", 400);
  116. }
  117. if (!is_null($value)) {
  118. $value->checkConstraints($doc->getConstraints());
  119. }
  120. }
  121. $args[$param->getName()] = $value;
  122. }
  123. catch (LuDboConstraintException $e)
  124. {
  125. $paramName = $param->getName();
  126. throw new LuBusinessException("Invalid value for '${paramName}': " . $e->getMessage(), 400, $e);
  127. }
  128. catch (LuDboDeserializeException $e)
  129. {
  130. $paramName = $param->getName();
  131. throw new LuBusinessException("Failed to deserialize '${paramName}': " . $e->getMessage(), 400, $e);
  132. }
  133. }
  134. $controller = $route->getController();
  135. $controllerInstance = new $controller();
  136. $result = call_user_func_array(array($controllerInstance, $route->getAction()), $args);
  137. foreach ($middlewares as $middlewareArray) {
  138. $middleware = $middlewareArray['middleware'];
  139. $middlewareDbo = $middlewareArray['dbo'];
  140. $parameters["_middleware"] = $middlewareDbo->getParameters();
  141. foreach ($route->getParameters() as $key => $value) {
  142. $parameters["_middleware"][$key] = $value;
  143. }
  144. $result = $middleware->onAfter($route, $parameters, $result);
  145. }
  146. return $result;
  147. }
  148. public function dispatch(string $httpMethod, string $url, $parameters)
  149. {
  150. $routeInfo = $this->_dispatcher->dispatch($httpMethod, $url);
  151. if ($routeInfo[0] == Dispatcher::NOT_FOUND) {
  152. throw new LuBusinessException("Route not found", 404);
  153. }
  154. else if ($routeInfo[0] == Dispatcher::METHOD_NOT_ALLOWED) {
  155. throw new LuBusinessException("Method not allowed", 405);
  156. }
  157. else {
  158. $handler = $routeInfo[1];
  159. $urlVars = $routeInfo[2];
  160. $parameters = array_merge($parameters, $urlVars);
  161. return $handler($parameters);
  162. }
  163. }
  164. private function getClassName(\ReflectionParameter $param) {
  165. preg_match('/\[\s\<\w+?>\s([\w]+)/s', $param->__toString(), $matches);
  166. return isset($matches[1]) ? $matches[1] : null;
  167. }
  168. private function getParam(\ReflectionParameter $param, $value)
  169. {
  170. $typedValue = null;
  171. $className = $this->getClassName($param);
  172. if (is_null($className)) {
  173. $typedValue = $value;
  174. }
  175. else {
  176. $class = $param->getClass();
  177. try
  178. {
  179. $json = json_decode($value, true);
  180. $typedValue = call_user_func_array(array($class->getName(), "jsonDeserialize"), array($json));
  181. }
  182. catch (\Exception $e)
  183. {
  184. if ($e instanceof LuDboDeserializeException || $e instanceof LuDboConstraintException) {
  185. throw $e;
  186. }
  187. throw new LuBusinessException("Unable to parse JSON value for '" . $param->getName() . "'", 400, $e);
  188. }
  189. }
  190. return $typedValue;
  191. }
  192. public function addRoute2(LuRouteDbo $route) {
  193. $controller = $route->getController();
  194. if (strpos($controller, "\\") === false) {
  195. $controller = "App\\Controller\\" . $controller . "Controller";
  196. }
  197. $route->setController($controller);
  198. $this->routes[] = $route;
  199. }
  200. public function addRoute(string $httpMethod, string $url, string $controller, string $method, $parameters = array(), $middlewares = array())
  201. {
  202. $middlewaresDbo = [];
  203. foreach ($middlewares as $middleware) {
  204. if (is_string($middleware)) {
  205. $middlewaresDbo[] = new LuMiddlewareDbo($middleware);
  206. }
  207. else if (is_array($middleware)) {
  208. $middlewaresDbo[] = LuMiddlewareDbo::jsonDeserialize($middleware);
  209. }
  210. else {
  211. $middlewaresDbo[] = $middleware;
  212. }
  213. }
  214. $route = new LuRouteDbo();
  215. $route->setUrl($url);
  216. $route->setController($controller);
  217. $route->setAction($method);
  218. $route->setMiddlewares($middlewaresDbo);
  219. $route->setParameters($parameters);
  220. $route->setMethod($httpMethod);
  221. $this->addRoute2($route);
  222. }
  223. public function get(string $url, string $controller, string $method, $parameters = array(), $middleware = array())
  224. {
  225. $this->addRoute("GET", $url, $controller, $method, $parameters, $middleware);
  226. }
  227. public function post(string $url, string $controller, string $method, $parameters = array(), $middleware = array())
  228. {
  229. $this->addRoute("POST", $url, $controller, $method, $parameters, $middleware);
  230. }
  231. public function put(string $url, string $controller, string $method, $parameters = array(), $middleware = array())
  232. {
  233. $this->addRoute("PUT", $url, $controller, $method, $parameters, $middleware);
  234. }
  235. public function delete(string $url, string $controller, string $method, $parameters = array(), $middleware = array())
  236. {
  237. $this->addRoute("DELETE", $url, $controller, $method, $parameters, $middleware);
  238. }
  239. }