我正在构建自定义PHP框架。目标是让这段代码有效:
$app = new Router();
$app->group("/admin", function($app) {
$app->group("/pages", function($app) {
$app->get("/home", "AdminPages@home")
->before("before_home1")
->before("before_home2")
->after("after_home1")
->after("after_home2");
})->before("before_pages1")->before("before_pages2")->after("after_pages1")->after("after_pages2");
})->before("before_admin1")->before("before_admin2")->after("after_admin1")->after("after_admin2");
目前,Router::get()
会返回Route
个对象,因此我可以添加前置和后置中间件(before()
和after()
,并将其保存到Route::$before[]
和Route::$after[]
),但我不知道如何继续。
问题是函数的顺序(在两个数组中)应该是
before_admin1
before_admin2
before_pages1
before_pages2
before_home1
before_home2
AdminPages@home
after_home1
after_home2
after_pages1
after_pages2
after_admin1
after_admin2
但是上面代码中的执行顺序是
before_home1
before_home2
before_pages1
before_pages2
before_admin1
before_admin2
AdminPages@home
after_home1
after_home2
after_pages1
after_pages2
after_admin1
after_admin2
将中间件调用按此顺序放入的最简单方法是什么? Router::group()
应该返回什么?也许是另一个Router
?
答案 0 :(得分:1)
如果您想拥有这种链接行为,则必须分两步执行路由:路由定义阶段,您可以在其中构建路径对象,以及路由评估阶段,您可以在其中查看每个匹配项,然后查看哪个匹配项。
实现对无限嵌套的支持的最直接的方法可能是构建嵌套的Route
对象,并让递归整理所有“执行顺序”问题。
这应该让你开始:
class Route
{
protected $method;
protected $pattern;
protected $controller;
protected $parent = null;
protected $before = array();
protected $after = array();
public function __construct($method, $pattern, $controller)
{
$this->method = $method;
$this->pattern = $pattern;
$this->controller = $controller;
}
public function setParent($parent)
{
$this->parent = $parent;
}
public function before($controller)
{
$this->before[] = $controller;
}
public function after($controller)
{
$this->after[] = $controller;
}
/* Returns itself if the provided method and URI match this route,
otherwise returns null */
public function match($method, $uri)
{
/* Match on simple equality for the sake of simplicity */
return $uri === $this->getFullPattern() && $method === $this->method ?
$this : null;
}
protected function getFullPattern()
{
/* Recursively concatenate all parent patterns */
return is_null($this->parent) ?
$this->pattern :
$this->parent->getFullPattern() . $this->pattern;
}
public function dispatch()
{
$this->runBefore();
/* Call controller function */
$this->runAfter();
}
public function runBefore()
{
/* Run the before filters on the parent first */
if(!is_null($this->parent))
{
$this->parent->runBefore();
}
foreach($this->before as $controller)
{
/* Execute before filter */
}
}
public function runAfter()
{
foreach($this->after as $controller)
{
/* Execute after filter */
}
/* Run the after filters on the parent next */
if(!is_null($this->parent))
{
$this->parent->runAfter();
}
}
}
/* A router is considered a special "group" route */
class Router extends Route
{
protected $routes = array();
public function __construct($pattern = "")
{
parent::__construct(null, $pattern, null);
}
public function addChild($route)
{
$this->routes[] = $route;
$route->setParent($this);
return $route;
}
public function group($pattern, $func)
{
$child = new Router($pattern);
$this->addChild($child);
call_user_func($func, $child);
return $child;
}
public function get($pattern, $controller)
{
return $this->addChild(new Route("GET", $pattern, $controller));
}
/* And the same goes for POST, PUT, DELETE, etc. */
/* Returns the child route that matches the provided parameters,
or null if there is no match; since we are calling 'match' on
each child, we perform a recursive matching */
public function match($method, $uri)
{
foreach($this->routes as $route)
{
$result = $route->match($method, $uri);
if($result instanceof Route)
{
return $result;
}
}
return null;
}
public function dispatch()
{
throw new Exception("Group routes cannot be dispatched.");
}
}
我根本没有测试此代码,所以请谨慎行事。