Php URL路由器中的组和中间件

Php URL路由器中的组和中间件,php,url-routing,Php,Url Routing,我正在构建一个定制的PHP框架。目标是使此代码正常工作: $app = new Router(); $app->group("/admin", function($app) { $app->group("/pages", function($app) { $app->get("/home", "AdminPages@home") ->before("before_home1") ->bef

我正在构建一个定制的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()
应该返回什么?可能还有另一个
路由器

如果你想拥有这种链接行为,你必须分两个步骤执行你的路由:一个是路由定义阶段,你在其中构建你的路由对象,另一个是路由评估阶段,你在其中检查每一个,看哪一个首先匹配

实现对无限嵌套的支持的最直接的方法可能是构建嵌套的
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.");
    }
}
我根本没有测试这段代码,所以请谨慎行事