Skip to content

Commit

Permalink
Fixes: #6 (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
gigili authored Apr 9, 2021
1 parent 77ff8fb commit 645c31e
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 73 deletions.
154 changes: 95 additions & 59 deletions Routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@

namespace Gac\Routing;


use Gac\Routing\Exceptions\CallbackNotFound;
use Gac\Routing\Exceptions\RouteNotFoundException;
use JetBrains\PhpStorm\Pure;


class Routes
{
/**
Expand Down Expand Up @@ -45,33 +45,29 @@ class Routes
public const DELETE = "DELETE";

/**
* @var Request $request Instance of a Request class to be passed as an argument to routes callback
* @var string $prefix Routes prefix
*/
public Request $request;
private string $prefix = "";

/**
* @var array $routes List of available routs
* @var array $middlewares List of middlewares to be executed before the routes
*/
private array $routes = [];
private array $middlewares = [];

/**
* Return the list of defined routed
*
* @return array
* @var array $routes List of available routs
*/
public function getRoutes(): array {
return $this->routes;
}
private array $routes = [];

/**
* @var string $prefix Prefix to be added to routes being created
* @var array $routes Temporary holder of list until the all get stored in the primary $routes array
*/
private string $prefix = "";
private array $tmpRoutes = [];

/**
* @var array $middlewares List of middlewares to be executed before accessing a route
* @var Request $request Instance of a Request class to be passed as an argument to routes callback
*/
private array $middlewares = [];
public Request $request;

/**
* Routes constructor
Expand All @@ -81,18 +77,38 @@ public function __construct() {
}

/**
* Method used for adding new routes
* Method used to set the prefix for routes
*
* @param string $path Path for the route
* @param callable|array|string $callback Callback method, an anonymous function or a class and method name to be executed
* @param string|array $methods Allowed request method(s) (GET, POST, PUT...)
* @param string $prefix Prefix to be added to all the routes in that chain.
*
* @return Routes Returns an instance of it self so that other methods could be chained onto it
*/
public function prefix(string $prefix = ""): self {
$this->prefix = $prefix;
return $this;
}

/**
* Method used to set the middlewares for routes
*
* @param array $data List of middlewares to be executed before the routes
*
* @return Routes returns an instance of it self so that the next method can be chained onto it
* @return Routes Returns an instance of it self so that other methods could be chained onto it
*/
public function add(string $path, callable|array|string $callback, string|array $methods = self::GET): self {
public function middleware(array $data): self {
$this->middlewares = $data;
return $this;
}

/**
* Method used to handle execution of routes and middlewares
*
* @throws RouteNotFoundException|CallbackNotFound
*/
public function route(string $path, callable|array|string $callback, string|array $methods = self::GET): self {
if (is_string($methods)) $methods = [$methods];

if (!empty($this->prefix)) $path = "{$this->prefix}{$path}"; // Prepend prefix to routes
if (!empty($this->prefix)) $path = $this->prefix . $path; // Prepend prefix to routes

if ($path !== "/") $path = rtrim($path, "/");

Expand All @@ -107,31 +123,65 @@ public function add(string $path, callable|array|string $callback, string|array
}

foreach ($methods as $method) {
$this->routes[$method][$path] = [
$this->tmpRoutes[$method][$path] = [
"callback" => $callback,
"middlewares" => $this->middlewares
];

if (!is_null($regex)) {
$this->routes[$method][$path]["regex"] = $regex;
$this->tmpRoutes[$method][$path]["regex"] = $regex;
if (!is_null($arguments)) {
$this->routes[$method][$path]["arguments"] = $arguments;
$this->tmpRoutes[$method][$path]["arguments"] = $arguments;
}
}
}

$this->middlewares = [];
$this->prefix = "";

return $this;
}

/**
* Method used for adding new routes
*
* @param string $path Path for the route
* @param callable|array|string|null $callback Callback method, an anonymous function or a class and method name to be executed
* @param string|array|null $methods Allowed request method(s) (GET, POST, PUT...)
*
* @throws CallbackNotFound
* @throws RouteNotFoundException
*/
public function add(string $path = "", callable|array|string|null $callback = NULL, string|array|null $methods = self::GET) {
if (empty($this->tmpRoutes)) {
$this->route($path, $callback, $methods);
}

foreach ($this->tmpRoutes as $method => $route) {
if (!isset($this->routes[$method])) $this->routes[$method] = [];
$path = array_key_first($route);

if (count($this->middlewares) > 0 && count($route[$path]["middlewares"]) === 0) {
$route[$path]["middlewares"] = $this->middlewares;
}

if (!empty($this->prefix) && !str_starts_with($path, $this->prefix)) {
$newPath = rtrim("{$this->prefix}$path", "/");
$route[$newPath] = $route[$path];
unset($route[$path]);
}

$this->routes[$method] = array_merge($this->routes[$method], $route);
}

$this->prefix = "";
$this->middlewares = [];
$this->tmpRoutes = [];
}

/**
* Method used to handle execution of routes and middlewares
*
* @throws RouteNotFoundException|CallbackNotFound
*/
public function route() {
public function handle() {
$path = $this->getPath();
$method = $_SERVER["REQUEST_METHOD"] ?? "GET";

Expand All @@ -140,7 +190,7 @@ public function route() {
$arguments = [];
if ($route === false) {
$dynamic_routes = array_filter($this->routes[$method], fn($route) => !is_null($route["regex"] ?? NULL));
foreach ($dynamic_routes as $route_path => $dynamic_route) {
foreach ($dynamic_routes as $dynamic_route) {
if (preg_match("/{$dynamic_route["regex"]}/", $path)) {
$route = $dynamic_route;
preg_match_all("/{$dynamic_route["regex"]}/", $path, $matches);
Expand Down Expand Up @@ -172,48 +222,24 @@ public function route() {
}
}

if ($route === false) throw new RouteNotFoundException("Route {$path} not found", 404);
if ($route === false) throw new RouteNotFoundException("Route $path not found", 404);

$middlewares = $route["middlewares"] ?? [];
$this->execute_middleware($middlewares);

$callback = $route["callback"] ?? false;
if ($callback === false) throw new CallbackNotFound("No callback specified for {$path}", 404);
if ($callback === false) throw new CallbackNotFound("No callback specified for $path", 404);

if ((is_string($callback) && class_exists($callback)) || is_array($callback)) {
$controller = is_string($callback) ? new $callback : new $callback[0]; // make a new instance of a controller class
$fn = is_string($callback) ? "index" : $callback[1] ?? "index"; // get the method to be execute or fallback to index method
$callback = [$controller, $fn];
}

if (!is_callable($callback)) throw new CallbackNotFound("Unable to execute callback for {$path}", 404);
if (!is_callable($callback)) throw new CallbackNotFound("Unable to execute callback for $path", 404);
call_user_func($callback, $this->request, ...$arguments);
}

/**
* Method which adds a prefix to route or a group of routes
*
* @param string $prefix Prefix to be added
*
* @return Routes returns an instance of it self so that the next method can be chained onto it
*/
public function prefix(string $prefix = ""): self {
$this->prefix = $prefix;
return $this;
}

/**
* Method used to set the middleware to be run before accessing API endpoint
*
* @param array $data List of middlewares to be executed before accessing the endpoint
*
* @return Routes returns an instance of it self so that the next method can be chained onto it
*/
public function middleware(array $data): self {
$this->middlewares = $data;
return $this;
}

/**
* Method which executes each specified middleware before the routes callback is executed
*
Expand All @@ -228,7 +254,7 @@ private function execute_middleware(array $data) {
$function = [new $function[0], $function[1]];
}

if (!is_callable($function)) throw new CallbackNotFound("Middleware method {$function} not found", 404);
if (!is_callable($function)) throw new CallbackNotFound("Middleware method $function not found", 404);

call_user_func($function, $this->request);
}
Expand All @@ -246,4 +272,14 @@ private function execute_middleware(array $data) {
$path = ($path !== "/") ? rtrim($path, "/") : $path;
return ($position === false) ? $path : substr($path, 0, $position);
}
}

/**
* Return the list of defined routed
*
* @return array
*/
public function getRoutes(): array {
return $this->routes;
}
}

36 changes: 22 additions & 14 deletions sample/index.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<?php
/** @noinspection PhpUnused */

/** @noinspection PhpUnusedParameterInspection */

use Gac\Routing\Exceptions\CallbackNotFound;
use Gac\Routing\Exceptions\RouteNotFoundException;
Expand All @@ -14,21 +17,28 @@

$routes = new Routes();
try {
$routes->add('/', function (Request $request) {

$routes->add('/', function () {
echo json_encode(["message" => "Hello World"]);
});

$routes
->prefix("/user")
->middleware(["verify_token"])
->route("/", [HomeController::class, "getUsers"], Routes::GET)
->route("/", [HomeController::class, "addUser"], Routes::POST)
->route("/", [HomeController::class, "updateUser"], Routes::PATCH)
->route("/", [HomeController::class, "replaceUser"], Routes::PUT)
->add("/", [HomeController::class, "deleteUser"], Routes::DELETE);

$routes->add('/test', function (Request $request) {
$request
->status(200, "OK")
->send(["message" => "Welcome"]);
});

$routes->add('/test', "test_route_function", [Routes::GET, Routes::POST]);

$routes->prefix("/user")
->middleware(["verify_token"])
->add('/', [HomeController::class, "getUsers"], Routes::GET)
->add("/", [HomeController::class, "addUser"], Routes::POST)
->add("/", [HomeController::class, "updateUser"], Routes::PATCH)
->add("/", [HomeController::class, "replaceUser"], Routes::PUT)
->add("/", [HomeController::class, "deleteUser"], Routes::DELETE);
$routes->add("/test", function () {
}, [Routes::PATCH, Routes::POST]);

$routes->add("/test/{int:userID}-{username}/{float:amount}/{bool:valid}", function (
Request $request,
Expand All @@ -40,7 +50,7 @@
echo "Dynamic route here";
});

// $routes->add("/test/{int:userID}-{username}/{float:amount}/{bool:valid}", [HomeController::class, "test"]); # It works like this also
$routes->add("/test/{int:userID}-{username}/{float:amount}/{bool:valid}", [HomeController::class, "test"]); # It works like this also

$routes
->middleware([
Expand All @@ -51,9 +61,7 @@
->add("/test", function (Request $request) {
$request->send(["message" => "Hello"]);
});


$routes->route();
$routes->handle();
} catch (RouteNotFoundException $ex) {
$routes->request->status(404, "Route not found")->send(["error" => ["message" => $ex->getMessage()]]);
} catch (CallbackNotFound $ex) {
Expand Down

0 comments on commit 645c31e

Please sign in to comment.