php - URL 路由器中的组和中间件

标签 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")
            ->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

最佳答案

如果您想要这种链接行为,您必须分两步执行路由:路由定义阶段,您在其中构建路由对象,路由评估 阶段,您遍历每一个并查看哪个最先匹配。

实现无限嵌套支持的最直接的方法可能是构建嵌套的 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.");
    }
}

我根本没有测试这段代码,所以请谨慎行事。

关于php - URL 路由器中的组和中间件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32370657/

相关文章:

php - 将一个表中的列与另一个表中的列进行匹配

javascript - jQuery 旋钮释放功能附加参数

php - mysql - 使用大表 200k 行

javascript - 如何访问:id parameter in the route file in ember. js?

javascript - 从 angularJS $stateProvider 获取 URL 中的 #

ASP.Net URL 路由仅在 `runAllManagedModulesForAllRequests` 为真时有效

javascript - 对 url 和 sammy.js 中带有尾部斜杠的 url 使用相同的路由

php - Eloquent (Laravel) 中两个表之间基于辅助表的关系

用于域但不是子域的 PHP setcookie()

asp.net-mvc - 路由约束到特定文件类型