router.class.php 5.1 KB

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