Browse Source

* Properly handle domains/prefixes
* Allow routers to have prefixes (Just a sub-router)
* Properly pull full url into response
* Handle 404's
* Make Uri class actually work

Nathaniel van Diepen 8 years ago
parent
commit
ccee370d54
3 changed files with 151 additions and 93 deletions
  1. 81 51
      app.class.php
  2. 56 30
      router.class.php
  3. 14 12
      uri.class.php

+ 81 - 51
app.class.php

@@ -2,13 +2,37 @@
 	require_once('base.class.php');
 	require_once('router.class.php');
 	require_once('response.class.php');
+	require_once('uri.class.php');
 	require_once('events.interface.php');
 	class App extends Base implements Events {
 		private static $apps = array();
+		private $domains;
 		private $routers;
+		private $router;
+		public $_onerror;
 		public function __construct($name){
 			$this->name = $name;
+			$this->router = new Router();
 			$this->routers = array();
+			$this->domains = array();
+			$this->_onerror = function($res, $error){
+				$o = json_encode(array(
+					'message'=>$error->getMessage(),
+					'code'=>$error->getCode(),
+					'file'=>$error->getFile(),
+					'line'=>$error->getLine(),
+					'traceback'=>$error->getTrace()
+				));
+				if($res){
+					$res->code(404);
+					$res->header('Content-Type', 'application/json');
+					$res->end($o);
+				}else{
+					http_response_code(404);
+					header('Content-Type: application/json');
+					print($o);
+				}
+			};
 			static::$apps[] = $this;
 		}
 		public function __destruct(){
@@ -23,11 +47,22 @@
 		}
 		public static function shutdown(){
 			$verb = $_SERVER['REQUEST_METHOD'];
-			$url = parse_url($_SERVER['REDIRECT_URL']);
+			if(isset($_SERVER['REDIRECT_URL'])){
+				$url = 'REDIRECT_URL';
+			}else{
+				$url = 'REQUEST_URI';
+			}
+			if(!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])){
+				$root = $_SERVER['HTTP_X_FORWARDED_PROTO'].'://';
+			}else{
+				$root = !empty($_SERVER['HTTPS']) ? "https://" : "http://";
+			}
+			// @todo - determine if http or http and also check cloudflare
+			$url = parse_url($root.$_SERVER['HTTP_HOST'].$_SERVER[$url]);
 			$data = file_get_contents( 'php://input','r');
 			foreach(static::$apps as $k => $app){
 				if($app instanceof App){
-					$app->handle($verb, $url, $data);
+					$app->handle($verb, $url, $data)->shutdown();
 				}
 			}
 			if(is_callable('parent::__destruct')){
@@ -36,70 +71,65 @@
 		}
 		public static function shutdown_error($error){
 			foreach(static::$apps as $k => $app){
-				$app->error($error);
+				if(is_callable($app->onerror)){
+					$app->onerror(null, $error);
+				}
 			}
 		}
 		public function handle($verb, $url, $data){
-			$res = new Response($url);
+			$res = new Response((string)new Uri($url));
 			$self = $this;
-			$onerror = function($res, $error){
-				$self->error($error);
+			$onerror = function($res, $error) use($self){
+				$self->onerror($res, $error);
 			};
-			foreach($this->routers as $type => $routers){
-				foreach($routers as $router){
-					switch($type){
-						case 'prefix':
-							$router->handle($url["path"], $res, null, $onerror);
-						break;
-						case 'domain':
-							$router->handle($url["host"], $res, null, $onerror);
-						break;
-					}
+			$handled = false;
+			// Domain routers
+			foreach($this->domains as $host => $router){
+				if($url['host'] == $host){
+					$router->handle($url["path"], $res, null, $onerror);
+					$handled = $handled || $router->handled;
 				}
 			}
-			$res->url = $url;
+			// Prefixed path routers
+			foreach($this->routers as $prefix => $router){
+				$router->handle($url["path"], $res, null, $onerror);
+				$handled = $handled || $router->handled;
+			}
+			// Base router for non-prefixed paths
+			$this->router->handle($url["path"], $res, function($res, $url) use($handled, $self){
+				if(!$handled){
+					$self->onerror($res,new Error("Not Found", 404));
+				}
+			}, $onerror);
 			return $res;
 		}
-		public function error($error){
-
+		public function error(Callable $fn){
+			$this->_onerror = $fn;
+			return $this;
 		}
-		public function get_router($type, $path){
-			$ret = false;
-			if(isset($this->routers[$type])){
-				foreach($this->routers[$type] as $k => $router){
-					if($router->base == $path){
-						$ret = $router;
-					}
-				}
-			}
-			return $ret;
+		public function route($path, Callable $fn){
+			$this->router->path($path, $fn);
+			return $this;
 		}
-		public function create_router($type, $path){
-			$router = new Router($path);
-			if(!isset($this->routers[$type])){
-				$this->routers[$type] = array();
+		public function prefix($prefix, Callable $fn){
+			if(!$this->routers[$prefix]){
+				$this->routers[$prefix] = new Router($prefix);
 			}
-			$this->routers[$type][] = $router;
-			return $router;
+			$fn($this->routers[$prefix]);
+			return $this;
 		}
-		public function route($type, $path, Callable $fn){
-			$router = $this->get_router($type, $prefix);
-			if($router == false){
-				$router = $this->create_router($type, $prefix);
+		public function domain($host, Callable $fn){
+			if(!isset($this->domains[$host])){
+				$this->domains[$host] = new Router();
 			}
-			call_user_func($fn, $router);
+			$fn($this->domains[$host]);
+			return $this;
 		}
-		public function prefix($prefix, Callable $fn){
-			$this->route('prefix', $prefix, function($router){
-				$router->base($prefix);
-				call_user_func($fn, $router);
-			});
-		}
-		public function domain($prefix, Callable $fn){
-			$this->route('domain', $prefix, function($router){
-				$router->base($prefix);
-				call_user_func($fn, $router);
-			});
+		public function onerror($res, $error){
+			$fn = $this->_onerror;
+			if(is_callable($fn)){
+				$fn($res, $error);
+			}
 		}
 	}
 	error_reporting(E_ALL);

+ 56 - 30
router.class.php

@@ -3,8 +3,10 @@
 	require_once('response.class.php');
 	class Router {
 		private $_paths = array();
+		private $_routers = array();
 		private $_base = '/';
 		private $responses = array();
+		private $_handled = false;
 		public function __construct($base = null, $paths = null){
 			if($paths != null){
 				$this->paths($paths);
@@ -18,6 +20,9 @@
 				case 'base':
 					return $this->_base;
 				break;
+				case 'handled':
+					return $this->_handled;
+				break;
 			}
 		}
 		public function __clone(){
@@ -35,8 +40,23 @@
 		public function url($url){
 			return preg_replace('/(\/+)/','/',$url);
 		}
+		public function prefix($prefix, Callable $fn){
+			$found = false;
+			foreach($this->_routers as $k => $router){
+				if($router->base == $prefix){
+					$found = true;
+					$fn($router);
+					break;
+				}
+			}
+			if(!$found){
+				$router= new Router($prefix);
+				$this->_routers[] = $router;
+				$fn($router);
+			}
+		}
 		// fn = function(response,args){}
-		public function path($path,$fn){
+		public function path($path, Callable $fn){
 			$obj = false;
 			foreach($this->_paths as $k => $p){
 				if($p->path == $path){
@@ -57,39 +77,45 @@
 		public function clear(){
 			$this->_paths = array();
 		}
-		public function handle($url,$res = null,$fn = null,$onerror = null){
-			if($url[0] != '/'){
-				$url = '/'.$url;
-			}
-			if(is_null($res)){
-				$res = new Response($url);
-			}else{
-				$res->url = $url;
-			}
-			if(!in_array($res,$this->responses)){
-				array_push($this->responses,$res);
-			}
-			ob_start();
-			$handled = false;
-			foreach($this->_paths as $k => $p){
-				if($p->matches($url)){
-					$handled = true;
-					try{
-						$p($res,$p->args($url));
-					}catch(Exception $e){
-						if(!is_null($onerror)){
-							$onerror($res,$e);
-						}else{
-							throw $e;
+		public function handle($url,$res = null,Callable $fn = null, Callable $onerror = null){
+			if(strpos($url,$this->base) !== false){
+				$url = substr($url,strpos($url,$this->base)+strlen($this->base));
+				if($url[0] != '/'){
+					$url = '/'.$url;
+				}
+				if(is_null($res)){
+					$res = new Response($url);
+				}
+				if(!in_array($res,$this->responses)){
+					array_push($this->responses,$res);
+				}
+				$handled = false;
+				foreach($this->_routers as $prefix => $router){
+					$router->handle($url, $res);
+					$handled = $handled ||$router->handled;
+				}
+				ob_start();
+				foreach($this->_paths as $k => $p){
+					if($p->matches($url)){
+						$handled = true;
+						try{
+							$p($res,$p->args($url));
+						}catch(Exception $e){
+							if(!is_null($onerror)){
+								$onerror($res,$e);
+							}else{
+								throw $e;
+							}
 						}
 					}
 				}
+				$this->_handled = $handled;
+				if(!$handled && !is_null($fn)){
+					$fn($res,$url);
+				}
+				$res->output .= ob_get_contents();
+				ob_end_clean();
 			}
-			if(!$handled && !is_null($fn)){
-				$fn($res,$url);
-			}
-			$res->output = ob_get_contents();
-			ob_end_clean();
 			return $res;
 		}
 	}

+ 14 - 12
uri.class.php

@@ -1,8 +1,8 @@
 <?php
 	class Uri{
 		private $url;
-		public __construct($url){
-			if($url instanceof Array){
+		public function __construct($url){
+			if(is_array($url)){
 				$this->url = $url;
 			}else if(is_string($url)){
 				$this->url = parse_url($url);
@@ -10,7 +10,7 @@
 				throw new Exception("Invalid Url");
 			}
 		}
-		public __get($name){
+		public function __get($name){
 			if(isset($this->url[$name])){
 				return $this->url[$name];
 			}else{
@@ -22,21 +22,23 @@
 				}
 			}
 		}
-		public __set($name, $value){
+		public function __set($name, $value){
 			if(isset($this->url[$name])){
 				$this->url[$name] = $value;
 			}
 		}
-		public __toString(){
-			$port = $this->port
-			if($this->scheme == 'http'){
-				$port = $port == 80?"":":{$port}"
-			}elseif($this->scheme = 'https'){
-				$port = $port == 443?"":":{$port}"
+		public function __toString(){
+			$port = $this->port;
+			if($port){
+				if($this->scheme == 'http'){
+					$port = $port == 80 ? "" : ":{$port}";
+				}elseif($this->scheme = 'https'){
+					$port = $port == 443 ? "" : ":{$port}";
+				}
 			}
 			$auth = $this->user;
 			if($auth){
-				$auth = $this->pass?"{$auth}:{$this->pass}@":"{$auth}@";
+				$auth = $this->pass ? "{$auth}:{$this->pass}@" : "{$auth}@";
 			}
 			$query = $this->query;
 			if($query){
@@ -46,7 +48,7 @@
 			if($fragmanet){
 				$fragmanet = "#{$fragmanet}";
 			}
-			return "{$this->scheme}://{$auth}{$this->host}{$port}/{$this->path}{$query}{$fragmanet}";
+			return "{$this->scheme}://{$auth}{$this->host}{$port}{$this->path}{$query}{$fragmanet}";
 		}
 	}
 ?>