21

所以我开始更深入地研究 MVC(真正的 MVC,而不是框架 MVC),并且我正在尝试开发一个小型框架。我通过阅读 Symphony 和 Zend 等其他框架来工作,看看它们是如何工作的,并尝试自己实现它。

我卡住的地方是 URL 路由系统:

<?php
namespace Application\Common;

class RouteBuilder {

    public function create($name, $parameters) {
        $route           = new Route($name);
        $route->resource = array_keys($parameters)[0];
        $route->defaults = $parameters["defaults"];
        $notation        = $parameters["notation"];
        $notation = preg_replace("/\[(.*)\]/", "(:?$1)?", $notation);
        foreach ($parameters["conditions"] as $param => $condition) {
            $notation = \str_replace($param, $condition, $notation);
        }

        $notation = preg_replace("/:([a-z]+)/i", "(?P<$1>[^/.,;?\n]+)", $notation);

        //@TODO: Continue pattern replacement!!
    }
}
/* How a single entry looks like
 * "main": {
    "notation": "/:action",
    "defaults": {
        "resource"  :   "Authentication",
    },
    "conditions":   {
        ":action"   :   "(login)|(register)"
    }
},

 */

我只是无法正确地缠绕它。从这里开始的应用程序工作流程是什么?

生成的模式,大概是Route对象下要保存的Request对象什么的,然后呢?它是如何工作的?

PS在这里寻找一个真实的,解释清楚的答案。我真的很想了解这个主题。如果有人花时间写一个真正详尽的答案,我将不胜感激。

4

2 回答 2

31

MVCRouter类(它是更广泛的Front Controller的一部分)分解 HTTP 请求的 URL——特别是路径组件(可能还有查询字符串)。

Router尝试将路径组件的第一个或两个部分匹配到相应的路由组合(/ ControllerAction [ method ],或者只是Controller执行默认操作(method)的一个。

一个动作或命令只是一个特定的方法Controller

通常有一个abstract Controller和多个子级Controller,每个网页一个(一般来说)。

有些人可能会说,如果 URL 中存在任何Router参数,也会将参数传递给所需的方法。Controller

注意:面向对象的编程纯粹主义者,遵循单一职责原则,可能会争辩说URL 的路由组件和分派一个Controller类是两个独立的职责。在这种情况下,一个Dispatcher类实际上将实例化Controller并传递其方法之一,任何从客户端 HTTP 请求派生的参数。

示例 1: Controller,但没有操作或参数。

http://localhost/contact在 GET 请求中,这可能会显示一个表单。

控制器= 合同

Action = Default(通常是一种index()方法)

=======================

示例 2:Controller和操作,但没有参数。

http://localhost/contact/send在 POST 请求中,这可能会启动服务器端验证并尝试发送消息。

控制器= 合同

行动= 发送

=======================

示例 3 Controller:、动作和参数。

http://localhost/contact/send/sync在 POST 请求中,这可能会启动服务器端验证并尝试发送消息。但是,在这种情况下,JavaScript 可能没有激活。因此,为了支持优雅降级,您可以告诉ContactController使用View支持屏幕重绘并使用 HTTP 标头Content-Type: text/html而不是Content-Type: application/json. sync将作为参数传递给ContactConroller::send(). 请注意,我的sync示例完全是任意的并且是虚构的,但我认为它符合要求!

控制器= 合同

行动= 发送

Arguments = [sync] // 是的,在数组中传递参数!

一个Router类实例化所请求的具体子项,从控制器实例Controller调用所请求的方法,并将其参数(如果有)传递给控制器​​方法。

1)你的Router类应该首先检查是否有一个Controller可以实例化的具体对象(使用 URL 中的名称,加上单词“Controller”)。如果找到控制器,则测试请求的方法(操作)是否存在。

2)如果Router在运行时找不到并加载必要的 PHP(建议使用自动加载器)来实例化一个具体的Controller子节点,它应该检查一个数组(通常在另一个类名中找到Route)以查看请求的 URL 是否匹配,使用常规表达式,其中包含的任何元素。下面是一个Route类的基本骨架。

注意:.*?= 零个或多个任意字符,非捕获。

class Route
{
    private $routes = [
        ['url' => 'nieuws/economie/.*?', // regular expression.
         'controller' => 'news',
         'action' => 'economie'],
        ['url' => 'weerbericht/locatie/.*?', // regular expression.
         'controller' => 'weather',
         'action' => 'location']
    ];

    public function __contstruct()
    {

    }

    public function getRoutes()
    {
        return $this->routes;
    }
}

为什么要使用正则表达式?对于 URL 中第二个正斜杠之后的数据,不太可能完成可靠的匹配。

/controller/method/param1/param2/...,其中 param[x] 可以是任何东西

警告:当目标数据包含模式分隔符(在这种情况下,正斜杠“/”)时,最好更改默认的正则表达式模式分隔符 ('/')。几乎任何无效的 URL 字符都是不错的选择。

该类的方法Router将遍历数组以查看目标 URL 和与二级索引关联的Route::routes之间是否存在正则表达式匹配。如果找到匹配项,则知道要实例化哪个具体以及要调用的后续方法。参数将根据需要传递给方法。string urlRouterController

始终警惕边缘情况,例如表示以下内容的 URL。

`/`   // Should take you to the home page / HomeController by default
`''`  // Should take you to the home page / HomeController by default
`/gibberish&^&*^&*%#&(*$%&*#`   // Reject
于 2012-09-14T19:32:25.380 回答
2

路由器类,来自我的框架。代码讲述了这个故事:

class Router
{
  const default_action = 'index';
  const default_controller = 'index';

  protected $request = array();

  public function __construct( $url )
  {
    $this->SetRoute( $url ? $url : self::default_controller );
  }

  /*
  *  The magic gets transforms $router->action into $router->GetAction();
  */
  public function __get( $name )
  {
    if( method_exists( $this, 'Get' . $name ))
      return $this->{'Get' . $name}();
    else
      return null;
  }

  public function SetRoute( $route )
  {
    $route = rtrim( $route, '/' );
    $this->request = explode( '/', $route );
  }

  private function GetAction()
  {
    if( isset( $this->request[1] ))
      return $this->request[1];
    else
      return self::default_action;
  }

  private function GetParams()
  {
    if( count( $this->request ) > 2 )
      return array_slice ( $this->request, 2 );
    else
      return array();
  }

  private function GetPost()
  {
    return $_SERVER['REQUEST_METHOD'] == 'POST';
  }

  private function GetController()
  {
    if( isset( $this->request[0] ))
      return $this->request[0];
    else
      return self::default_controller;
  }

  private function GetRequest()
  {
    return $this->request;
  }
于 2012-09-14T19:51:25.857 回答