Middleware code

pull/514/head
Austin Collier 1 year ago
parent 4b98a610eb
commit f543d74bfe

@ -10,6 +10,7 @@ declare(strict_types=1);
namespace flight;
use Closure;
use ErrorException;
use Exception;
use flight\core\Dispatcher;
@ -19,6 +20,7 @@ use flight\net\Response;
use flight\net\Router;
use flight\template\View;
use Throwable;
use flight\net\Route;
/**
* The Engine class contains the core functionality of the framework.
@ -32,12 +34,12 @@ use Throwable;
* @method void halt(int $code = 200, string $message = '') Stops processing and returns a given response.
*
* Routing
* @method void route(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a URL to a callback function with all applicable methods
* @method Route route(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a URL to a callback function with all applicable methods
* @method void group(string $pattern, callable $callback) Groups a set of routes together under a common prefix.
* @method void post(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a POST URL to a callback function.
* @method void put(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a PUT URL to a callback function.
* @method void patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a PATCH URL to a callback function.
* @method void delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a DELETE URL to a callback function.
* @method Route post(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a POST URL to a callback function.
* @method Route put(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a PUT URL to a callback function.
* @method Route patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a PATCH URL to a callback function.
* @method Route delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a DELETE URL to a callback function.
* @method Router router() Gets router
* @method string getUrl(string $alias) Gets a url from an alias
*
@ -375,6 +377,7 @@ class Engine
ob_start();
// Route the request
$failed_middleware_check = false;
while ($route = $router->route($request)) {
$params = array_values($route->params);
@ -383,11 +386,60 @@ class Engine
$params[] = $route;
}
// Run any before middlewares
if(count($route->middleware) > 0) {
foreach($route->middleware as $middleware) {
$middleware_object = (is_callable($middleware) === true ? $middleware : (method_exists($middleware, 'before') === true ? [ $middleware, 'before' ]: false));
if($middleware_object === false) {
continue;
}
// It's assumed if you don't declare before, that it will be assumed as the before method
$middleware_result = $this->dispatcher->execute(
$middleware_object,
$params
);
if ($middleware_result === false) {
$failed_middleware_check = true;
break 2;
}
}
}
// Call route handler
$continue = $this->dispatcher->execute(
$route->callback,
$params
);
// Run any before middlewares
if(count($route->middleware) > 0) {
// process the middleware in reverse order now
foreach(array_reverse($route->middleware) as $middleware) {
// must be an object. No functions allowed here
$middleware_object = is_object($middleware) === true && !($middleware instanceof Closure) && method_exists($middleware, 'after') === true ? [ $middleware, 'after' ] : false;
// has to have the after method, otherwise just skip it
if($middleware_object === false) {
continue;
}
$middleware_result = $this->dispatcher->execute(
$middleware_object,
$params
);
if ($middleware_result === false) {
$failed_middleware_check = true;
break 2;
}
}
}
$dispatched = true;
@ -400,7 +452,9 @@ class Engine
$dispatched = false;
}
if (!$dispatched) {
if($failed_middleware_check === true) {
$this->halt(403, 'Forbidden');
} else if($dispatched === false) {
$this->notFound();
}
}
@ -464,10 +518,11 @@ class Engine
* @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback
* @param string $alias the alias for the route
* @return Route
*/
public function _route(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): void
public function _route(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route
{
$this->router()->map($pattern, $callback, $pass_route, $alias);
return $this->router()->map($pattern, $callback, $pass_route, $alias);
}
/**

@ -14,6 +14,7 @@ use flight\net\Request;
use flight\net\Response;
use flight\net\Router;
use flight\template\View;
use flight\net\Route;
/**
* The Flight class is a static representation of the framework.
@ -23,12 +24,12 @@ use flight\template\View;
* @method static void stop() Stops the framework and sends a response.
* @method static void halt(int $code = 200, string $message = '') Stop the framework with an optional status code and message.
*
* @method static void route(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Maps a URL pattern to a callback with all applicable methods.
* @method static Route route(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Maps a URL pattern to a callback with all applicable methods.
* @method static void group(string $pattern, callable $callback) Groups a set of routes together under a common prefix.
* @method static void post(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a POST URL to a callback function.
* @method static void put(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a PUT URL to a callback function.
* @method static void patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a PATCH URL to a callback function.
* @method static void delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a DELETE URL to a callback function.
* @method static Route post(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a POST URL to a callback function.
* @method static Route put(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a PUT URL to a callback function.
* @method static Route patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a PATCH URL to a callback function.
* @method static Route delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') Routes a DELETE URL to a callback function.
* @method static Router router() Returns Router instance.
* @method static string getUrl(string $alias) Gets a url from an alias
*

@ -57,6 +57,11 @@ final class Route
*/
public string $alias = '';
/**
* @var array<callable> The middleware to be applied to the route
*/
public array $middleware = [];
/**
* Constructor.
*
@ -190,4 +195,29 @@ final class Route
$url = rtrim($url, '/');
return $url;
}
/**
* Sets the route alias
*
* @return self
*/
public function setAlias(string $alias): self {
$this->alias = $alias;
return $this;
}
/**
* Sets the route middleware
*
* @param array<callable>|callable $middleware
* @return self
*/
public function addMiddleware($middleware): self {
if(is_array($middleware) === true) {
$this->middleware = array_merge($this->middleware, $middleware);
} else {
$this->middleware[] = $middleware;
}
return $this;
}
}

@ -26,7 +26,7 @@ class Router
public bool $case_sensitive = false;
/**
* Mapped routes.
* @var array<int, Route>
* @var array<int,Route>
*/
protected array $routes = [];
@ -42,6 +42,13 @@ class Router
*/
protected string $group_prefix = '';
/**
* Group Middleware
*
* @var array
*/
protected array $group_middlewares = [];
/**
* Gets mapped routes.
*
@ -67,9 +74,9 @@ class Router
* @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback
* @param string $route_alias Alias for the route
* @return void
* @return Route
*/
public function map(string $pattern, callable $callback, bool $pass_route = false, string $route_alias = ''): void
public function map(string $pattern, callable $callback, bool $pass_route = false, string $route_alias = ''): Route
{
$url = trim($pattern);
$methods = ['*'];
@ -80,7 +87,16 @@ class Router
$methods = explode('|', $method);
}
$this->routes[] = new Route($this->group_prefix.$url, $callback, $methods, $pass_route, $route_alias);
$route = new Route($this->group_prefix.$url, $callback, $methods, $pass_route, $route_alias);
// to handle group middleware
foreach($this->group_middlewares as $gm) {
$route->addMiddleware($gm);
}
$this->routes[] = $route;
return $route;
}
/**
@ -90,10 +106,10 @@ class Router
* @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback
* @param string $alias Alias for the route
* @return void
* @return Route
*/
public function get(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): void {
$this->map('GET ' . $pattern, $callback, $pass_route, $alias);
public function get(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route {
return $this->map('GET ' . $pattern, $callback, $pass_route, $alias);
}
/**
@ -103,10 +119,10 @@ class Router
* @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback
* @param string $alias Alias for the route
* @return void
* @return Route
*/
public function post(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): void {
$this->map('POST ' . $pattern, $callback, $pass_route, $alias);
public function post(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route {
return $this->map('POST ' . $pattern, $callback, $pass_route, $alias);
}
/**
@ -116,10 +132,10 @@ class Router
* @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback
* @param string $alias Alias for the route
* @return void
* @return Route
*/
public function put(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): void {
$this->map('PUT ' . $pattern, $callback, $pass_route, $alias);
public function put(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route {
return $this->map('PUT ' . $pattern, $callback, $pass_route, $alias);
}
/**
@ -129,10 +145,10 @@ class Router
* @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback
* @param string $alias Alias for the route
* @return void
* @return Route
*/
public function patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): void {
$this->map('PATCH ' . $pattern, $callback, $pass_route, $alias);
public function patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route {
return $this->map('PATCH ' . $pattern, $callback, $pass_route, $alias);
}
/**
@ -142,10 +158,10 @@ class Router
* @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback
* @param string $alias Alias for the route
* @return void
* @return Route
*/
public function delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): void {
$this->map('DELETE ' . $pattern, $callback, $pass_route, $alias);
public function delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route {
return $this->map('DELETE ' . $pattern, $callback, $pass_route, $alias);
}
/**
@ -153,13 +169,17 @@ class Router
*
* @param string $group_prefix group URL prefix (such as /api/v1)
* @param callable $callback The necessary calling that holds the Router class
* @param array<int,mixed> $middlewares The middlewares to be applied to the group Ex: [ $middleware1, $middleware2 ]
* @return void
*/
public function group(string $group_prefix, callable $callback): void {
public function group(string $group_prefix, callable $callback, array $group_middlewares = []): void {
$old_group_prefix = $this->group_prefix;
$old_group_middlewares = $this->group_middlewares;
$this->group_prefix .= $group_prefix;
$this->group_middlewares = array_merge($this->group_middlewares, $group_middlewares);
$callback($this);
$this->group_prefix = $old_group_prefix;
$this->group_middlewares = $old_group_middlewares;
}
/**

@ -272,4 +272,77 @@ class EngineTest extends PHPUnit\Framework\TestCase
$url = $engine->getUrl('path1', [ 'param' => 123 ]);
$this->assertEquals('/path1/123', $url);
}
public function testMiddlewareCallableFunction() {
$engine = new Engine();
$engine->route('/path1/@id', function($id) { echo 'OK'.$id; })
->addMiddleware(function($id) { echo 'before'.$id; });
$engine->request()->url = '/path1/123';
$engine->start();
$this->expectOutputString('before123OK123');
}
public function testMiddlewareCallableFunctionReturnFalse() {
$engine = new class extends Engine {
public function _halt(int $code = 200, string $message = ''): void
{
$this->response()->status($code);
$this->response()->write($message);
}
};
$engine->route('/path1/@id', function($id) { echo 'OK'.$id; })
->addMiddleware(function($id) { echo 'before'.$id; return false; });
$engine->request()->url = '/path1/123';
$engine->start();
$this->expectOutputString('Forbiddenbefore123');
$this->assertEquals(403, $engine->response()->status());
}
public function testMiddlewareClassBefore() {
$middleware = new class {
public function before($id) {
echo 'before'.$id;
}
};
$engine = new Engine();
$engine->route('/path1/@id', function($id) { echo 'OK'.$id; })
->addMiddleware($middleware);
$engine->request()->url = '/path1/123';
$engine->start();
$this->expectOutputString('before123OK123');
}
public function testMiddlewareClassBeforeAndAfter() {
$middleware = new class {
public function before($id) {
echo 'before'.$id;
}
public function after($id) {
echo 'after'.$id;
}
};
$engine = new Engine();
$engine->route('/path1/@id', function($id) { echo 'OK'.$id; })
->addMiddleware($middleware);
$engine->request()->url = '/path1/123';
$engine->start();
$this->expectOutputString('before123OK123after123');
}
public function testMiddlewareClassAfter() {
$middleware = new class {
public function after($id) {
echo 'after'.$id;
}
};
$engine = new Engine();
$engine->route('/path1/@id', function($id) { echo 'OK'.$id; })
->addMiddleware($middleware);
$engine->request()->url = '/path1/123';
$engine->start();
$this->expectOutputString('OK123after123');
}
}

Loading…
Cancel
Save