From 61f90338ddb9aeddd2551c8df79e4cdc43b9fff9 Mon Sep 17 00:00:00 2001 From: ahwelp Date: Thu, 19 Jul 2018 21:37:01 -0300 Subject: [PATCH] First Commit --- composer.json | 17 ++ src/BBRouter/Response.php | 133 +++++++++++++++ src/BBRouter/Route.php | 237 ++++++++++++++++++++++++++ src/BBRouter/RouteCollection.php | 278 +++++++++++++++++++++++++++++++ 4 files changed, 665 insertions(+) create mode 100755 composer.json create mode 100755 src/BBRouter/Response.php create mode 100755 src/BBRouter/Route.php create mode 100755 src/BBRouter/RouteCollection.php diff --git a/composer.json b/composer.json new file mode 100755 index 0000000..3af9cad --- /dev/null +++ b/composer.json @@ -0,0 +1,17 @@ +{ + "name" : "ahwelp/bbrouter", + "description" : "Different Routes", + "type" : "library", + "authors" : [ + { + "name" : "ahwelp", + "email" : "ahwelp@ahwelp.com" + } + ], + "autoload" : { + "psr-4" : { + "BBRouter\\" : "src/BBRouter/" + } + }, + "require" : { } +} diff --git a/src/BBRouter/Response.php b/src/BBRouter/Response.php new file mode 100755 index 0000000..2118b08 --- /dev/null +++ b/src/BBRouter/Response.php @@ -0,0 +1,133 @@ +_headers[$key] = $value; + return $istance; + } + + public static function body_prepend($body = null){ + $instance = self::getInstance(); + if (null !== $body) { + $instance->_body = (string) $body . $instance->_body; + return $instance; + } + return $instance; + } + + public static function body($body = null){ + $instance = self::getInstance(); + if (null !== $body) { + $instance->_body = (string) $body; + return $instance; + } + return $instance; + } + + public static function body_append($body = null){ + $instance = self::getInstance(); + if (null !== $body) { + $instance->_body .= (string) $body; + return $instance; + } + return $instance; + } + + private function send_headers(){ + foreach ($this->_headers as $key => $value) { + header($key .': '. $value, false); + } + return $this; + } + + private function send_body(){ + echo (string) $this->_body; + return $this; + } + + public static function send(){ + $instance = self::getInstance(); + $instance->send_headers(); + $instance->send_body(); + return $instance; + } + + public static function json($object, $jsonp_prefix = null){ + $instance = self::getInstance(); + + $instance->body(''); + + $json = json_encode($object); + if (null !== $jsonp_prefix) { + // Should ideally be application/json-p once adopted + $instance->header('Content-Type', 'text/javascript'); + $instance->body("$jsonp_prefix($json);"); + } else { + $instance->header('Content-Type', 'application/json'); + $instance->body($json); + } + $instance->send(); + return $instance; + } + + public static function redirect($url, $code = 302){ + $instance = self::getInstance(); + + $instance->_code = $code; + $instance->header('Location', $url); + + $instance->body(''); + $instance->send(); + + return $instance; + } + + function done(){ + die; + } +} diff --git a/src/BBRouter/Route.php b/src/BBRouter/Route.php new file mode 100755 index 0000000..1340f89 --- /dev/null +++ b/src/BBRouter/Route.php @@ -0,0 +1,237 @@ + 'Continue', + 101 => 'Switching Protocols', + + // Successful 2xx + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + + // Redirection 3xx + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => '(Unused)', + 307 => 'Temporary Redirect', + + // Client Error 4xx + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + + // Server Error 5xx + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + ); + + //======================================================= + function execute(){ + foreach ($this->_before as $before){ + call_user_func($before); + } + foreach ($this->_callback as $callback){ + call_user_func_array($callback, $this->_params); + } + foreach ($this->_after as $after){ + call_user_func($after); + } + } + + function prepare(){ + $this->_segments = explode('/', $this->_uri); + + foreach ($this->_segments as $segment){ + if( strpos($segment, 'i:') > -1 ){ + $this->_regex .= "\/[0-9]+"; + }else if( strpos($segment, 'h:') > -1 ){ + $this->_regex .= "\/[A-z]+"; + }else if( strpos($segment, ':') > -1 ){ + $this->_regex .= "\/[A-z0-9]+"; + }else{ + $this->_regex .= "\/".$segment; + } + } + $this->_regex = ltrim($this->_regex, '\/'); + $this->_regex = "/\/" . $this->_regex . "$/"; + } + + function create_indexes(){ + foreach ($this->_segments as $key => $segment){ + if(preg_match('/\[.*?\]$/', $segment)){ + $segment = trim( trim( $segment, '[' ), ']'); + $param = explode(':', $segment); + $this->_params[ $param[1] ] = $this->_uri_segments[$key]; + } + } + } + + function set_uri($uri){ + $this->_uri = $uri; + } + + function get_segments(){ + return $this->_segments; + } + + function do_block(){ + $this->_block = true; + return $this; + } + function not_block(){ + $this->_block = false; + return $this; + } + + function do_ignore(){ + $this->_ignore = true; + return $this; + } + function not_ignore(){ + $this->_ignore = false; + return $this; + } + function set_weight($weigth = 0){ + $this->_weight = $weigth; + return $this; + } + + function set_http_error($code = 400, $message = null){ + if($message == null){ + $message = self::$http_messages[$code]; + } + + $info = new \stdClass(); + $info->code = $code; + $info->message = $message; + + $this->_http_error = $info; + return $this; + } + + function match($uri){ + //Wildcards allways match + if(in_array('*', $this->_verb) || $this->_uri == '*'){ + return true; + } + + if( preg_match($this->_regex, $uri) ){ + $this->_uri_segments = explode('/', $uri); + $this->create_indexes(); + return true; + } + + return false; + } +} + + + diff --git a/src/BBRouter/RouteCollection.php b/src/BBRouter/RouteCollection.php new file mode 100755 index 0000000..6f77f2a --- /dev/null +++ b/src/BBRouter/RouteCollection.php @@ -0,0 +1,278 @@ +_uri = isset( $_GET['uri'] ) ? '/'. $_GET['uri'] : '/'; + self::$_route_collection->define_verb(); + } + return self::$_route_collection; + } + + public function getInstance(){ + if (!isset(self::$_route_collection)) { + return self::newObj(); + } + return self::$_route_collection; + } + + + //HELPERS================================================ + + /* + * Find route files with names under pathname + */ + public function crawl($basepath = __DIR__, $filenames = Array('Routes.php', 'routes.php') ){ + $instance = self::getInstance(); + + $rdi = new \RecursiveDirectoryIterator($basepath); + + foreach(new \RecursiveIteratorIterator($rdi) as $file){ + foreach ($filenames as $filename) { + if( strpos($file, $filename) ){ + $instance->_loaded_files[] = $file; + } + } + } + + return $instance; + } + + /* + * Load the files with the routes + */ + function load_routes(){ + $instance = self::getInstance(); + + foreach ($instance->_loaded_files as $loaded_file){ + include $loaded_file; + } + return $instance; + } + + /* + * GET | POST | PUT | PATCH | DELETE + * + */ + private function define_verb(){ + + $verbs_whitelist = Array('*', 'GET','POST','PUT','PATCH','DELETE', 'CLI'); + $verb = ''; + + if(isset($_POST['_method'])){ + $verb = $_POST['_method']; + }else if(php_sapi_name() == "cli"){ + $verb = "CLI"; + }else{ + $verb = $_SERVER['REQUEST_METHOD']; + } + + if(in_array($verb, $verbs_whitelist)){ + $this->_verb = $verb; + }else{ + echo 'Invalid HTTP Verb'; + } + } + + /* + * + */ + private function sort_routes(){ + usort($this->_routes, function($a, $b){ + if ($a->_weight == $b->_weight) { + return 0; + } + return ($a->_weight < $b->_weight) ? -1 : 1; + }); + } + + /* + * Dispatch routes + */ + function submit(){ + global $ROUTE; + + self::$_route_collection->sort_routes(); + + $one_hit = false; + + foreach (self::$_route_collection->_routes as $route){ + + if(!in_array( self::$_route_collection->_verb, $route->_verb) && !in_array( '*', $route->_verb) ){ + //No it is not + continue; + } + + //Request match a route + $match = $route->match(self::$_route_collection->_uri); + + if($match){ + //Yup. Execute the route + + $ROUTE = $route; + $route->execute(); + + if(!$route->_ignore){ + $one_hit = true; + } + + if($route->_http_error){ + $this->http_error($route->_http_error); + return; + } + + //If Executed, interrupt route chain? + if($route->_block){ + return; + } + } + } + + if(!$one_hit){ + $info = new \stdClass(); + $info->code = 404; + $info->message = 'Not Found'; + self::$_route_collection->http_error($info); + } + } + + //DEFINITORS============================================= + + static function get($uri, $callback, $weight = 0){ + $route = new Route(); + $route->_verb[] = 'GET'; + + $route->_uri = $uri; + $route->_callback[] = $callback; + $route->_weight = $weight; + $route->prepare(); + + self::getInstance()->_routes[] = $route; + + return $route; + } + + static function post($uri, $callback, $weight = 0){ + $route = new Route(); + $route->_verb[] = 'POST'; + + $route->_uri = $uri; + $route->_callback[] = $callback; + $route->_weight = $weight; + $route->prepare(); + + self::getInstance()->_routes[] = $route; + + return $route; + } + + static function put($uri, $callback, $weight = 0){ + $route = new Route(); + $route->_verb[] = 'PUT'; + + $route->_uri = $uri; + $route->_callback[] = $callback; + $route->_weight = $weight; + $route->prepare(); + + self::getInstance()->_routes[] = $route; + + return $route; + } + + static function patch($uri, $callback, $weight = 0){ + $route = new Route(); + $route->_verb[] = 'PATCH'; + + $route->_uri = $uri; + $route->_callback[] = $callback; + $route->_weight = $weight; + $route->prepare(); + + self::getInstance()->_routes[] = $route; + + return $route; + } + + static function delete($uri, $callback, $weight = 0){ + $route = new Route(); + $route->_verb[] = 'DELETE'; + + $route->_uri = $uri; + $route->_callback[] = $callback; + $route->_weight = $weight; + $route->prepare(); + + self::getInstance()->_routes[] = $route; + + return $route; + } + + static function resource($uri){ + throw new Exception('Not implemented'); + } + + /* + * + */ + static function add($verb, $uri, $callback, $weight = 0){ + $route = new Route(); + + if(is_array( $verb ) ){ + $route->_verb = $verb; + }else{ + $route->_verb[] = $verb; + } + $route->_uri = $uri; + $route->_callback[] = $callback; + $route->_weight = $weight; + $route->prepare(); + + self::getInstance()->_routes[] = $route; + + return $route; + } + + /* + * + */ + function addRoute(Route $route){ + $this->_routes[] = $route; + } + + static function on_http_error($function){ + $instance = self::getInstance(); + $instance->_errors[] = $function; + } + + /* + * + */ + private function http_error(\stdClass $info){ + $info = (array) $info; + foreach ($this->_errors as $error) { + call_user_func_array($error, $info); + } + + + } +}