app.class.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. <?php
  2. require_once('base.class.php');
  3. require_once('router.class.php');
  4. require_once('response.class.php');
  5. require_once('uri.class.php');
  6. require_once('events.interface.php');
  7. class App extends Base implements Events {
  8. private static $apps = array();
  9. private $domains;
  10. private $routers;
  11. private $router;
  12. public $_onerror;
  13. public function __construct($name){
  14. $this->name = $name;
  15. $this->router = new Router();
  16. $this->routers = array();
  17. $this->domains = array();
  18. $this->_onerror = function($res, $error){
  19. $o = json_encode(array(
  20. 'message'=>$error->getMessage(),
  21. 'code'=>$error->getCode(),
  22. 'file'=>$error->getFile(),
  23. 'line'=>$error->getLine(),
  24. 'traceback'=>$error->getTrace()
  25. ));
  26. if($res){
  27. $res->code(404);
  28. $res->header('Content-Type', 'application/json');
  29. $res->end($o);
  30. }else{
  31. http_response_code(404);
  32. header('Content-Type: application/json');
  33. print($o);
  34. }
  35. };
  36. static::$apps[] = $this;
  37. }
  38. public function __destruct(){
  39. $this->routers = array();
  40. $index = array_search(static::$apps, $this);
  41. if($index !== false){
  42. array_splice(static::$apps, $index, 1);
  43. }
  44. if(is_callable('parent::__destruct')){
  45. parent::__destruct();
  46. }
  47. }
  48. public static function shutdown(){
  49. $verb = $_SERVER['REQUEST_METHOD'];
  50. if(isset($_SERVER['REDIRECT_URL'])){
  51. $url = 'REDIRECT_URL';
  52. }else{
  53. $url = 'REQUEST_URI';
  54. }
  55. if(!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])){
  56. $root = $_SERVER['HTTP_X_FORWARDED_PROTO'].'://';
  57. }else{
  58. $root = !empty($_SERVER['HTTPS']) ? "https://" : "http://";
  59. }
  60. // @todo - determine if http or http and also check cloudflare
  61. $url = parse_url($root.$_SERVER['HTTP_HOST'].$_SERVER[$url]);
  62. $data = file_get_contents( 'php://input','r');
  63. foreach(static::$apps as $k => $app){
  64. if($app instanceof App){
  65. $app->handle($verb, $url, $data)->shutdown();
  66. }
  67. }
  68. if(is_callable('parent::__destruct')){
  69. parent::__destruct();
  70. }
  71. }
  72. public static function shutdown_error($error){
  73. foreach(static::$apps as $k => $app){
  74. if(is_callable($app->onerror)){
  75. $app->onerror(null, $error);
  76. }
  77. }
  78. }
  79. public function handle($verb, $url, $data){
  80. $res = new Response((string)new Uri($url));
  81. $self = $this;
  82. $onerror = function($res, $error) use($self){
  83. $self->onerror($res, $error);
  84. };
  85. $handled = false;
  86. // Domain routers
  87. foreach($this->domains as $host => $router){
  88. if($url['host'] == $host){
  89. $router->handle($url["path"], $res, null, $onerror);
  90. $handled = $handled || $router->handled;
  91. }
  92. }
  93. // Prefixed path routers
  94. foreach($this->routers as $prefix => $router){
  95. $router->handle($url["path"], $res, null, $onerror);
  96. $handled = $handled || $router->handled;
  97. }
  98. // Base router for non-prefixed paths
  99. $this->router->handle($url["path"], $res, function($res, $url) use($handled, $self){
  100. if(!$handled){
  101. $self->onerror($res,new Error("Not Found", 404));
  102. }
  103. }, $onerror);
  104. return $res;
  105. }
  106. public function error(Callable $fn){
  107. $this->_onerror = $fn;
  108. return $this;
  109. }
  110. public function route($path, Callable $fn){
  111. $this->router->path($path, $fn);
  112. return $this;
  113. }
  114. public function prefix($prefix, Callable $fn){
  115. if(!$this->routers[$prefix]){
  116. $this->routers[$prefix] = new Router($prefix);
  117. }
  118. $fn($this->routers[$prefix]);
  119. return $this;
  120. }
  121. public function domain($host, Callable $fn){
  122. if(!isset($this->domains[$host])){
  123. $this->domains[$host] = new Router();
  124. }
  125. $fn($this->domains[$host]);
  126. return $this;
  127. }
  128. public function onerror($res, $error){
  129. $fn = $this->_onerror;
  130. if(is_callable($fn)){
  131. $fn($res, $error);
  132. }
  133. }
  134. }
  135. error_reporting(E_ALL);
  136. ini_set('display_errors', 'On');
  137. register_shutdown_function(function(){
  138. App::shutdown();
  139. });
  140. set_error_handler(function($errno, $errstr, $errfile, $errline){
  141. App::shutdown_error(array(
  142. 'number'=> $errno,
  143. 'msg'=> $errstr,
  144. 'file'=> $errfile,
  145. 'line'=> $errline,
  146. 'backtrace'=> debug_backtrace(),
  147. 'included'=> get_included_files()
  148. ));
  149. },E_ALL);
  150. register_shutdown_function(function(){
  151. $error = error_get_last();
  152. if($error['type'] == 1){
  153. App::shutdown_error(array(
  154. 'number'=> $error['type'],
  155. 'msg'=> $error['message'],
  156. 'file'=> $error['file'],
  157. 'line'=> $error['line'],
  158. 'backtrace'=> array(),
  159. 'included'=> get_included_files()
  160. ));
  161. }
  162. });
  163. ?>