router.class.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <?php
  2. namespace Juju\App;
  3. require_once('path.class.php');
  4. $dir = dirname(__DIR__);
  5. require_once(realpath("{$dir}/events.trait.php"));
  6. $dir = "{$dir}/Http/";
  7. require_once(realpath("{$dir}/request.class.php"));
  8. require_once(realpath("{$dir}/response.class.php"));
  9. use \Juju\{Http\Request, Http\Response, Events};
  10. class Router {
  11. use Events;
  12. private $_paths = [];
  13. private $_routers = [];
  14. private $_base = '/';
  15. private $responses = [];
  16. private $_handled = false;
  17. public function __construct(string $base = null, array $paths = null){
  18. if($paths != null){
  19. $this->paths($paths);
  20. }
  21. if($base != null){
  22. $this->base($base);
  23. }
  24. }
  25. public function __get($name){
  26. switch($name){
  27. case 'base':
  28. return $this->_base;
  29. break;
  30. case 'handled':
  31. return $this->_handled;
  32. break;
  33. case 'routes':
  34. $routes = [];
  35. $base = rtrim($this->_base, '/');
  36. foreach($this->_paths as $path){
  37. if(!isset($routes[$base.$path->path])){
  38. $routes[$base.$path->path] = 0;
  39. }
  40. $routes[$base.$path->path] += count($path);
  41. }
  42. foreach($this->_routers as $router){
  43. foreach($router->routes as $route => $count){
  44. if(!isset($routes[$base.$route])){
  45. $routes[$base.$route] = 0;
  46. }
  47. $routes[$base.$route] += $count;
  48. }
  49. }
  50. return $routes;
  51. break;
  52. }
  53. }
  54. public function __clone(){
  55. // No cloning
  56. }
  57. public function __destruct(){
  58. $this->_paths = [];
  59. }
  60. public function __toString(){
  61. return "[Router]";
  62. }
  63. public function base(string $base){
  64. $this->_base = $base;
  65. return $this;
  66. }
  67. public static function url(string $url){
  68. return preg_replace('/(\/+)/','/',$url);
  69. }
  70. public function prefix(string $prefix, callable $fn){
  71. $found = false;
  72. foreach($this->_routers as $k => $router){
  73. if($router->base == $prefix){
  74. $found = true;
  75. $fn($router);
  76. break;
  77. }
  78. }
  79. if(!$found){
  80. $router= new Router($prefix);
  81. $this->_routers[] = $router;
  82. $fn($router);
  83. }
  84. return $this;
  85. }
  86. public function path(string $path, callable $fn){
  87. $obj = false;
  88. foreach($this->_paths as $k => $p){
  89. if($p->path == $path){
  90. $obj = $p;
  91. }
  92. }
  93. if(!$obj){
  94. $obj = new Path($path);
  95. array_push($this->_paths, $obj);
  96. }
  97. $obj->handle($fn);
  98. return $this;
  99. }
  100. public function get(string $path, callable $fn){
  101. return $this->path($path, function($req, $res, $args) use($fn){
  102. if($req->verb === 'GET'){
  103. return $fn($req, $res, $args);
  104. }
  105. });
  106. }
  107. public function post(string $path, callable $fn){
  108. return $this->path($path, function($req, $res, $args) use($fn){
  109. if($req->verb === 'POST'){
  110. return $fn($req, $res, $args);
  111. }
  112. });
  113. }
  114. public function put(string $path, callable $fn){
  115. return $this->path($path, function($req, $res, $args) use($fn){
  116. if($req->verb === 'PUT'){
  117. return $fn($req, $res, $args);
  118. }
  119. });
  120. }
  121. public function delete(string $path, callable $fn){
  122. return $this->path($path, function($req, $res, $args) use($fn){
  123. if($req->verb === 'DELETE'){
  124. return $fn($req, $res, $args);
  125. }
  126. });
  127. }
  128. public function patch(string $path, callable $fn){
  129. return $this->path($path, function($req, $res, $args) use($fn){
  130. if($req->verb === 'PATCH'){
  131. return $fn($req, $res, $args);
  132. }
  133. });
  134. }
  135. public function paths(array $paths){
  136. foreach($paths as $path => $fn){
  137. $this->path($path, $fn);
  138. }
  139. return $this;
  140. }
  141. public function gets(array $paths){
  142. foreach($paths as $path => $fn){
  143. $this->get($path, $fn);
  144. }
  145. return $this;
  146. }
  147. public function posts(array $paths){
  148. foreach($paths as $path => $fn){
  149. $this->post($path, $fn);
  150. }
  151. return $this;
  152. }
  153. public function puts(array $paths){
  154. foreach($paths as $path => $fn){
  155. $this->put($path, $fn);
  156. }
  157. return $this;
  158. }
  159. public function deletes(array $paths){
  160. foreach($paths as $path => $fn){
  161. $this->delete($path, $fn);
  162. }
  163. return $this;
  164. }
  165. public function patches(array $paths){
  166. foreach($paths as $path => $fn){
  167. $this->patch($path, $fn);
  168. }
  169. return $this;
  170. }
  171. public function clear(){
  172. $this->_paths = [];
  173. return $this;
  174. }
  175. public function handle(string $path, Request $req = null, Response $res = null, callable $fn = null, callable $onerror = null){
  176. if(strpos($path, $this->base) === 0){
  177. $path = substr($path, strlen($this->base));
  178. if(strpos($path, '/') !== 0){
  179. $path = "/{$path}";
  180. }
  181. if(is_null($req)){
  182. $req = new Request(Request::get_verb(), Request::get_url(), Request::get_headers(), Request::get_body());
  183. }
  184. if(is_null($res)){
  185. $res = new Response();
  186. }
  187. if(!in_array($res,$this->responses)){
  188. array_push($this->responses,$res);
  189. }
  190. $this->fire('handle', $req, $res);
  191. $handled = false;
  192. foreach($this->_routers as $prefix => $router){
  193. $router->handle($path, $req, $res);
  194. $handled = $handled ||$router->handled;
  195. }
  196. ob_start();
  197. foreach($this->_paths as $k => $p){
  198. if($p->matches($path)){
  199. $handled = true;
  200. try{
  201. if($p($req, $res, $p->args($path)) === false){
  202. $handled = false;
  203. }
  204. }catch(\Exception $e){
  205. if(!is_null($onerror)){
  206. $onerror($req, $res,$e);
  207. }else{
  208. throw $e;
  209. }
  210. }
  211. }
  212. }
  213. $this->_handled = $handled;
  214. if(!$handled && !is_null($fn)){
  215. $fn($req, $res);
  216. }
  217. $res->output .= ob_get_contents();
  218. ob_end_clean();
  219. $this->fire('afterhandle', $req, $res);
  220. }
  221. return $res;
  222. }
  223. }
  224. ?>