php MVC 路由是如何工作的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12430181/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does MVC routing work?
提问by Madara's Ghost
So I've started studying MVC (real MVC, not framework MVC) a bit more in-depth, and I'm attempting to develop a small framework. I'm working by reading other frameworks such as Symphony and Zend, seeing how they do their job, and attempt to implement it myself.
所以我开始更深入地研究 MVC(真正的 MVC,而不是框架 MVC),并且我正在尝试开发一个小框架。我正在通过阅读其他框架(例如 Symphony 和 Zend)来工作,了解它们是如何工作的,并尝试自己实现它。
The place where I got stuck was the URL routing system:
我卡住的地方是 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("/\[(.*)\]/", "(:?)?", $notation);
foreach ($parameters["conditions"] as $param => $condition) {
$notation = \str_replace($param, $condition, $notation);
}
$notation = preg_replace("/:([a-z]+)/i", "(?P<>[^/.,;?\n]+)", $notation);
//@TODO: Continue pattern replacement!!
}
}
/* How a single entry looks like
* "main": {
"notation": "/:action",
"defaults": {
"resource" : "Authentication",
},
"conditions": {
":action" : "(login)|(register)"
}
},
*/
I just can't get my head wrapped around it properly. What is the application workflow from here?
我就是不能把我的头正确地缠绕在它上面。从这里开始的应用程序工作流程是什么?
The pattern is generated, probably a Routeobject to be kept under the Requestobject or something, then what? How does it work?
生成的模式,大概是一个Route对象要保存在Request对象下什么的,然后呢?它是如何工作的?
P.S.Looking for a real, well explained answer here. I really want to understand the subject. I would appreciate if someone took the time to write a real elaborate answer.
PS在这里寻找一个真实的,解释得很好的答案。我真的很想了解这个主题。如果有人花时间写一个真正详尽的答案,我将不胜感激。
回答by w00
An MVC Routerclass (which is part of a broader Front Controller) breaks down an HTTP request's URL--specifically, the path component (and potentially the query string).
MVCRouter类(它是更广泛的Front Controller 的一部分)分解 HTTP 请求的 URL——特别是路径组件(可能还有查询字符串)。
The Routerattempts to match the first one, or two, parts of the path component to a corresponding route combination (Controller/ Action [method], or just a Controllerthat executes a default action (method).
所述Router尝试匹配的第一个,或两个,至对应的路径组合的路径分量的部件(Controller/动作[方法],或者只是一个Controller执行默认动作(方法)。
An action, or command, is simply a methodoff of a specific Controller.
一个动作或命令,只是一个特定的方法Controller。
There is usually an abstract Controllerand many children of Controller, one for each webpage (generally speaking).
通常有一个abstract Controller和多个子Controller节点,每个网页一个(一般来说)。
Some might say that the Routeralso passes arguments to the desired Controller's method, if any are present in the URL.
有些人可能会说Router也将参数传递给所需Controller的方法,如果 URL 中存在任何参数。
Note: Object-oriented programming purists, following the Single Responsibility Principle, might argue that routingcomponents of a URL and dispatchinga Controllerclass are two separate responsibilities. In that case, a Dispatcherclass would actually instantiate the Controllerand pass one of its methods any arguments derived from the client HTTP request.
注:面向对象编程的纯粹主义者,继单一职责原则,可能会认为路由URL的组件和调度一Controller类是两个独立的责任。在这种情况下,一个Dispatcher类实际上会实例化Controller并传递它的方法之一,任何从客户端 HTTP 请求派生的参数。
Example 1: Controller, but no action or arguments.
示例 1: Controller,但没有操作或参数。
http://localhost/contactOn a GET request, this might display a form.
http://localhost/contact在 GET 请求中,这可能会显示一个表单。
Controller= Contract
控制器= 合同
Action= Default (commonly an index()method)
Action= Default(通常是一种index()方法)
======================
======================
Example 2: Controllerand action, but no arguments.
示例 2:Controller和动作,但没有参数。
http://localhost/contact/sendOn a POST request, this might kick of server-side validation and attempt to send a message.
http://localhost/contact/send在 POST 请求中,这可能会启动服务器端验证并尝试发送消息。
Controller= Contract
控制器= 合同
Action= send
行动= 发送
======================
======================
Example 3: Controller, action, and arguments.
示例 3 Controller:、动作和参数。
http://localhost/contact/send/syncOn a POST request, this might kick of server-side validation and attempt to send a message. However, in this case, maybe JavaScript is not active. Thus, to support graceful degradation, you can tell the ContactControllerto use a Viewthat supports screen redraw and responds with an HTTP header of Content-Type: text/html, instead of Content-Type: application/json. syncwould be passed as an argument to ContactConroller::send(). Note, my syncexample was totally arbitrary and made up, but I thought it fit the bill!
http://localhost/contact/send/sync在 POST 请求中,这可能会启动服务器端验证并尝试发送消息。但是,在这种情况下,JavaScript 可能未处于活动状态。因此,为了支持优雅降级,您可以告诉ContactController使用View支持屏幕重绘并使用Content-Type: text/html, 而不是 的HTTP 标头响应的Content-Type: application/json。sync将作为参数传递给ContactConroller::send(). 请注意,我的sync示例完全是随意的和编造的,但我认为它符合要求!
Controller= Contract
控制器= 合同
Action= send
行动= 发送
Arguments= [sync]// Yes, pass arguments in an array!
Arguments= [sync]// 是的,在数组中传递参数!
A Routerclass instantiates the requested, concrete child Controller, calls the requested methodfrom the controller instance, and passes the controller method its arguments(if any).
一个Router类实例化的要求,具体孩子Controller,调用请求的方法从控制器实例,并通过在控制器方法的参数(如果有的话)。
1)Your Routerclass should first check to see if there is a concrete Controllerthat it can instantiate (using the name as found in the URL, plus the word "Controller"). If the controller is found, test for the presence of the requested method(action).
1)您的Router类应该首先检查是否有Controller可以实例化的具体对象(使用在 URL 中找到的名称,加上单词“Controller”)。如果找到控制器,则测试请求的方法(操作)是否存在。
2)If the Routercannot find and load the necessary PHP at runtime (using an autoloaderis advised) to instantiate a concrete Controllerchild, it should then check an array (typically found in another class name Route) to see if the requested URL matches, using regular expressions, any of the elements contained within. A basic skeleton of a Routeclass follows.
2)如果Router在运行时无法找到并加载必要的 PHP(建议使用自动加载器)来实例化一个具体的Controller子项,那么它应该检查一个数组(通常在另一个类名中找到Route)以查看请求的 URL 是否匹配,使用常规表达式,其中包含的任何元素。类的基本骨架Route如下。
Note: .*?= Zero, or more, of any character, non-capturing.
注意:.*?= 零个或多个任何字符,非捕获。
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;
}
}
Why use a regular expression? One is not likely to get reliable matching accomplished for data after the second forward slash in the URL.
为什么要使用正则表达式?在 URL 中的第二个正斜杠之后,不太可能对数据进行可靠匹配。
/controller/method/param1/param2/..., where param[x] could be anything!
/controller/method/param1/param2/...,其中 param[x] 可以是任何东西!
Warning:It is good practice change the default regular expression pattern delimiter ('/') when targeting data contains the pattern delimiter (in this case, forward slashes '/'. Almost any non-valid URL character would be a great choice.
警告:当目标数据包含模式分隔符(在本例中为正斜杠“/”)时,最好更改默认的正则表达式模式分隔符 ('/')。几乎所有无效的 URL 字符都是不错的选择。
A method of the Routerclass will iterate over the Route::routesarray to see if there is a regular expression match between the target URL and the stringvalueassociated with a 2nd level urlindex. If a match is found, the Routerthen knows which concrete Controllerto instantiate and the subsequent method to call. Arguments will be passed to the method as necessary.
Router类的方法将遍历Route::routes数组以查看目标 URL 和与二级索引关联的string值之间是否存在正则表达式匹配url。如果找到匹配项,Router则知道Controller要实例化哪个具体以及要调用的后续方法。参数将根据需要传递给方法。
Always be wary of edge cases, such as URLs representing the following.
始终警惕边缘情况,例如表示以下内容的 URL。
`/` // Should take you to the home page / HomeController by default
`''` // Should take you to the home page / HomeController by default
`/gibberish&^&*^&*%#&(*$%&*#` // Reject
回答by JvdBerg
The router class, from my framework. The code tells the story:
路由器类,来自我的框架。代码讲述了这个故事:
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;
}

