router.class.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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. }else{
  105. return false;
  106. }
  107. });
  108. }
  109. public function post(string $path, callable $fn){
  110. return $this->path($path, function($req, $res, $args) use($fn){
  111. if($req->verb === 'POST'){
  112. return $fn($req, $res, $args);
  113. }else{
  114. return false;
  115. }
  116. });
  117. }
  118. public function put(string $path, callable $fn){
  119. return $this->path($path, function($req, $res, $args) use($fn){
  120. if($req->verb === 'PUT'){
  121. return $fn($req, $res, $args);
  122. }else{
  123. return false;
  124. }
  125. });
  126. }
  127. public function delete(string $path, callable $fn){
  128. return $this->path($path, function($req, $res, $args) use($fn){
  129. if($req->verb === 'DELETE'){
  130. return $fn($req, $res, $args);
  131. }else{
  132. return false;
  133. }
  134. });
  135. }
  136. public function patch(string $path, callable $fn){
  137. return $this->path($path, function($req, $res, $args) use($fn){
  138. if($req->verb === 'PATCH'){
  139. return $fn($req, $res, $args);
  140. }else{
  141. return false;
  142. }
  143. });
  144. }
  145. public function paths(array $paths){
  146. foreach($paths as $path => $fn){
  147. $this->path($path, $fn);
  148. }
  149. return $this;
  150. }
  151. public function gets(array $paths){
  152. foreach($paths as $path => $fn){
  153. $this->get($path, $fn);
  154. }
  155. return $this;
  156. }
  157. public function posts(array $paths){
  158. foreach($paths as $path => $fn){
  159. $this->post($path, $fn);
  160. }
  161. return $this;
  162. }
  163. public function puts(array $paths){
  164. foreach($paths as $path => $fn){
  165. $this->put($path, $fn);
  166. }
  167. return $this;
  168. }
  169. public function deletes(array $paths){
  170. foreach($paths as $path => $fn){
  171. $this->delete($path, $fn);
  172. }
  173. return $this;
  174. }
  175. public function patches(array $paths){
  176. foreach($paths as $path => $fn){
  177. $this->patch($path, $fn);
  178. }
  179. return $this;
  180. }
  181. public function clear(){
  182. $this->_paths = [];
  183. return $this;
  184. }
  185. public function handle(string $path, Request $req = null, Response $res = null, callable $fn = null, callable $onerror = null){
  186. if(strpos($path, $this->base) === 0){
  187. $path = substr($path, strlen($this->base));
  188. if(strpos($path, '/') !== 0){
  189. $path = "/{$path}";
  190. }
  191. if(is_null($req)){
  192. $req = new Request(Request::get_verb(), Request::get_url(), Request::get_headers(), Request::get_body());
  193. }
  194. if(is_null($res)){
  195. $res = new Response();
  196. }
  197. if(!in_array($res,$this->responses)){
  198. array_push($this->responses,$res);
  199. }
  200. $this->fire('handle', $req, $res);
  201. $handled = false;
  202. foreach($this->_routers as $prefix => $router){
  203. $router->handle($path, $req, $res);
  204. $handled = $handled ||$router->handled;
  205. }
  206. ob_start();
  207. foreach($this->_paths as $k => $p){
  208. if($p->matches($path)){
  209. $handled = true;
  210. try{
  211. if($p($req, $res, $p->args($path)) === false){
  212. $handled = false;
  213. }
  214. }catch(\Exception $e){
  215. if(!is_null($onerror)){
  216. $onerror($req, $res,$e);
  217. }else{
  218. throw $e;
  219. }
  220. }
  221. }
  222. }
  223. $this->_handled = $handled;
  224. if(!$handled && !is_null($fn)){
  225. $fn($req, $res);
  226. }
  227. $res->output .= ob_get_contents();
  228. ob_end_clean();
  229. $this->fire('afterhandle', $req, $res);
  230. }
  231. return $res;
  232. }
  233. }
  234. ?>