Front Controllers act like centralized agents in an application whose primary area of concern is to dispatch commands, either statically or dynamically, to predefined handlers such as page controllers, REST resources, or pretty much anything else that comes to mind.

Building at least a naive front controller is a pretty instructional experience in understanding the nitty-gritty of them, and to promote this idea from a pragmatic standpoint, I went through the implementation of a contrived front controller in the introductory article which packaged all the logic required for routing and dispatching requests inside the boundaries of a single class.

One of the best things about front controllers is that you can keep them running as tight structures, just routing and dispatching incoming requests, or you can let your wild side show and implement a full-fledged完备的,成熟的 RESTful controller capable of parsing HTTP verbs, accommodating pre/post dispatch hooks, and the like, all behind a unified API. But while this approach is appealing, it breaks the Single Responsibility Principle (SRP) and goes against the nature of OOP itself which actively pushes the delegation of different tasks to several fine-grained objects.

So does this mean I’m just another sinful soul who dared to break from the commandments of the SRP? Well, in a sense I am. So I’d like to wash away my sins by showing you how easy is to deploy a small, yet extensible, HTTP framework capable of putting to work a front controller along with the ones of a standalone router and a dispatcher. Plus, the whole request/response cycle will be independently handled by a couple of reusable classes, which naturally you’ll be able to tweak at will.

With such a huge proliferation of HTTP frameworks available packaged with full-featured components, it seems absurd 荒谬的to implement from scratch a front controller that routes and dispatches requests through a few modular classes, even if these ones retain the essence of the SRP. In a humble attempt to avoid being judged for reinventing the wheel, some chunks of my custom implementation will be inspired by the nifty EPHPMVC library written by Lars Strojny.

Dissecting the Request/Route/Dispatch/Response Cycle

The first task we should tackle is defining a couple of classes charged with modeling the data and behavior of a typical HTTP request/response cycle. Here’s the first one, coupled to the interface that it implements:

<?php
namespace Acme\Library; interface RequestInterface
{
public function getUri();
public function setParam($key, $value);
public function getParam($key);
public function getParams();
}
<?php
namespace Acme\Library; class Request implements RequestInterface
{
protected $uri;
protected $params = array(); public function __construct($uri, array $params = array()) {
if (!filter_var($uri, FILTER_VALIDATE_URL)) {
throw new \InvalidArgumentException("The URI is invalid.");
}
$this->uri = $uri;
$this->params = $params;
} public function getUri() {
return $this->uri;
} public function setParam($key, $value) {
$this->params[$key] = $value;
return $this;
} public function getParam($key) {
if (!isset($this->params[$key])) {
throw new \InvalidArgumentException(
"The request parameter with key '$key' is invalid.");
}
return $this->params[$key];
} public function getParams() {
return $this->params;
}
}

The Request class encapsulates an incoming URI along with an array of parameters and models an extremely skeletal HTTP request. For the sake of brevity, additional data members such as the set of methods associated to the request in question have been deliberately left outside of the picture. If you feel in the mood to drop them into the class, go ahead and do so.

Having a slim HTTP request wrapper living happily on its own is all well and fine sure, but ultimately useless if not coupled to the counterpart that mimics the data and behavior of a typical HTTP response. Let’s fix and build up this complementary component:

<?php
namespace Acme\Library; interface ResponseInterface
{
public function getVersion();
public function addHeader($header);
public function addHeaders(array $headers);
public function getHeaders();
public function send();
}
<?php
namespace Acme\Library; class Response implements ResponseInterface
{
const VERSION_11 = "HTTP/1.1";
const VERSION_10 = "HTTP/1.0"; protected $version;
protected $headers = array(); public function __construct($version = self::VERSION_11) {
$this->version = $version;
} public function getVersion() {
return $this->version;
} public function addHeader($header) {
$this->headers[] = $header;
return $this;
} public function addHeaders(array $headers) {
foreach ($headers as $header) {
$this->addHeader($header);
}
return $this;
} public function getHeaders() {
return $this->headers;
} public function send() {
if (!headers_sent()) {
foreach($this->headers as $header) {
header("$this->version $header", true);
}
}
}
}

headers_sent() 函数检查 HTTP 标头是否已被发送以及在哪里被发送。

如果报头已发送,则返回 true,否则返回 false。

header('xxx',true/false) 第二个参数作用:

The optional replace parameter indicates whether the header should replace a previous similar header, or add a second header of the same type. By default it will replace, but if you pass in FALSE as the second argument you can force multiple headers of the same type. F

为true表示代替前面的header。默认为true.

The Response class is unquestionably a more active creature than its partner Request. It acts like a basic container which allows you to stack up HTTP headers at will and is capable of sending them out to the client too.

With these classes doing their thing in isolation, it’s time to tackle the next part in the construction of a front controller. In a typical implementation, the routing/dispatching processes are most of the time encapsulated inside the same method, which frankly speaking isn’t that bad at all. In this case, however, it’d be nice to break down the processes in question and delegate them to different classes. This way, things are balanced a little more in the equally of their responsibilities.

在典型的实现中,route和dispatch大多数时候写在一起。在我们的case中,我们会把他们分解,写在不同的类中。通过这种方式,

事情有点平衡了。

Here’s the batch of classes that get the routing module up and running:

<?php
namespace Acme\Library; interface RouteInterface
{
public function match(RequestInterface $request);
public function createController();
}
<?php
namespace Acme\Library; class Route implements RouteInterface
{
protected $path;
protected $controllerClass; public function __construct($path, $controllerClass) {
if (!is_string($path) || empty($path)) {
throw new \InvalidArgumentException("The path is invalid.");
}
if (!class_exists($controllerClass)) {
throw new \InvalidArgumentException("The controller class is invalid.");
}
$this->path = $path;
$this->controllerClass = $controllerClass;
} public function match(RequestInterface $request) {
return $this->path === $request->getUri();
} public function createController() {
return new $this->controllerClass;
}
}
<?php
namespace Acme\Library; interface RouterInterface
{
public function addRoute(RouteInterface $route);
public function addRoutes(array $routes);
public function getRoutes();
public function route(RequestInterface $request, ResponseInterface $response);
}
<?php
namespace Acme\Library; class Router implements RouterInterface
{
protected $routes = array(); public function __construct(array $routes = array()) {
if (!empty($routes)) {
$this->addRoutes($routes);
}
} public function addRoute(RouteInterface $route) {
$this->routes[] = $route;
return $this;
} public function addRoutes(array $routes) {
foreach ($routes as $route) {
$this->addRoute($route);
}
return $this;
} public function getRoutes() {
return $this->routes;
} public function route(RequestInterface $request, ResponseInterface $response) {
foreach ($this->routes as $route) {
if ($route->match($request)) {
return $route;
}
}
$response->addHeader("404 Page Not Found")->send();
throw new \OutOfRangeException("No route matched the given URI.");
}
}

php header 404

header("HTTP/1.1 404 Not Found");exit;

或者:

header("status: 404 not found");

最好2个一起写。

  1. header("HTTP/1.1 404 Not Found");
  2. header("Status: 404 Not Found");
  3. exit;

As one might expect, there’s a plethora of options to choose from when it comes to implementing a functional routing mechanism. The above, at least in my view, exposes both a pragmatic and straightforward solution. It defines an independent Route class that ties a path to a given action controller, and a simple router whose responsibility is limited to checking if a stored route matches the URI associated to a specific request object.

上面的写法,定义了一个独立的Route类来bind被给的控制器,一个简单Router用来检查存储的route是否匹配请求的url。

To get things finally sorted out, we would need to set up a swift dispatcher that can be put to work side by side with the previous classes. The below class does exactly that:

<?php
namespace Acme\Library; interface DispatcherInterface
{
public function dispatch(RouteInterface $route, RequestInterface $request, ResponseInterface $response);
}
<?php
namespace Acme\Library; class Dispatcher implements DispatcherInterface
{
public function dispatch(RouteInterface $route, RequestInterface $request, ResponseInterface $response) {
$controller = $route->createController();
$controller->execute($request, $response);
}
}

Scanning the Dispatcher, you’ll notice two things about. First, it doesn’t carry any state. Second, it implicitly assumes that each action controller will run under the surface of an execute()method.

This can be refactored in favor of a slightly more flexible schema if you wish (the first thing that comes to my mind is tweaking the implementation of the Route class), but for the sake of simplicity I’ll keep the dispatcher untouched.

By now you’re probably wondering how and where to drop a front controller capable of bring all of the previous classes together. Don’t be anxious, as that’s next!

Implementing a Customizable Front Controller

We’ve reached the moment we’ve all been waiting for since the very beginning, implementing the long awaited front controller. But if you were expecting the implementation to be pretty much some kind of epic quest, given the number of classes that we dropped up front, I’m afraid you’ll be disappointed. In fact, creating the controller boils down to just defining a class that shields the functionality of the router and the dispatcher behind a ridiculously simple API:

<?php
namespace Acme\Library; interface FrontControllerInterface
{
public function run(RequestInterface $request, ResponseInterface $response);
}
<?php
namespace Acme\Library; class FrontController implements FrontControllerInterface
{
protected $router;
protected $dispatcher; public function __construct(RouterInterface $router, DispatcherInterface $dispatcher) {
$this->router = $router;
$this->dispatcher = $dispatcher;
} public function run(RequestInterface $request, ResponseInterface $response) {
$route = $this->router->route($request, $response);
$this->dispatcher->dispatch($route, $request, $response);
}
}

All that the FrontController class does is employ its run() method for routing and dispatching a given request to the corresponding action controller by using the behind-the-scenes functionality of its collaborators. If you’d like, the method could be a lot fatter and encapsulate a bunch of additional implementation, such as pre/post dispatch hooks and so forth. I’ll leave this as homework for you in case you want to add a new notch to your developer belt.

To see if the front controller set up is actually as functional as it seems, let’s create a couple of banal action controllers which implement an execute() method:

其余:

http://phpmaster.com/front-controller-pattern-2/

php前端控制器2的更多相关文章

  1. 前端控制器DispatcherServlet 详解

    DispatcherServlet 是前端控制器设计模式的实现,提供 Spring Web MVC 的集中访问点,而且负责职责的分派,而且与 Spring IoC 容器无缝集成,从而可以获得 Spri ...

  2. spring mvc DispatcherServlet详解之前传---前端控制器架构

    前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端.前端控制器既可以使用Filter实现 ...

  3. php前端控制器设计1

    The primary role of a front controller in web-based applications is to encapsulate the typical reque ...

  4. 淘淘商城之springmvc前端控制器

    一.web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi=&qu ...

  5. Spring MVC的前端控制器模式

    前端控制器模式 spring mvc也是依赖servlet,所以spring mvc的请求处理是从一个servlet开始,这个servlet就是DispatcherServlet.前端控制器模式(Fr ...

  6. Spring MVC中前端控制器拦截问题

    <!-- 前端控制器 --> <servlet> <servlet-name>ssm</servlet-name> <servlet-class& ...

  7. 通过前端控制器源码分析springmvc的执行过程

    第一步:前端控制器接收请求调用doDiapatch 第二步:前端控制器调用处理器映射器查找 Handler 第三步:调用处理器适配器执行Handler,得到执行结果ModelAndView 第四步:视 ...

  8. springmvc中Controller前端控制器的映射与返回数据,以及异常处理

    .@RequestMapping映射 该注解中存的是映射路径的字符串 1.1 value的值是一个数组,也就是说可以定义多个访问路径,同时“.action”可以省略,前端控制器中主要查找主体部分.注意 ...

  9. Spring入门程序-前端控制器配置器

    1,处理器的第二种配置方式 <!--配置handler --> <bean id="/FirstController" class="com.songy ...

  10. 前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端。前端控制器既可以使用Filter实现(Struts2采用这种方式),也可以使用Servlet来实现(spring MVC框架)。

    本文转自http://www.cnblogs.com/davidwang456/p/4090058.html 感谢作者 前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并 ...

随机推荐

  1. Protel 99SE PCB 打印技巧

    1. 打开 Protel99SE PCB 设计文档.从菜单File 下单击Print/Preview 打印预览菜单.出现PCB 打印预览介面. 2.从File 下单击 Setup Printer 设置 ...

  2. 盛希泰:办公室就像男人的春药——人的一生的精力是有限的,你把有限的时间分配给谁决定你的成败——你有N多选择,你人生的积累就是N多选择加起来的结果

    欢迎关注“创事记”的微信订阅号:sinachuangshiji 创事记注:12月22日晚上,盛希泰在清华大学旧经管报告厅面对清华师生讲了一堂<创业引导课>.本文由洪泰帮根据课堂录音整理完成 ...

  3. VC获取精确时间的做法

    声明:本文章是我整合网上的资料而成的,其中的大部分文字不是我所为的,我所起的作用只是归纳整理并添加我的一些看法.非常感谢引用到的文字的作者的辛勤劳动,所参考的文献在文章最后我已一一列出. 对关注性能的 ...

  4. HDU 1787 GCD Again

    题目大意:求小于n的gcd(i,n)大于1的个数: 题解:欧拉函数直接求gcd(i,n)==1的个数  用n减即可 #include <cstdio> int eular(int n){ ...

  5. Symfony Composer icu requires lib-icu

    运行php compser.phar 的时候出现此问题的时候解决办法 问题描述Problem 1 -Installation request for symfony/icu v1.2.1 -> ...

  6. hdoj 5311 Hidden String(KMP)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5311 思路分析:该问题要求在字符串中是否存在三个不相交的子串s[l1..r1], s[l2..r2], ...

  7. [Leetcode]-containsNearbyDuplicate

    //题目: //给定一个整数数组与一个整数k,当且存在两个不同的下标i和j满足nums[i] = nums[j]而且| i - j | <= k时返回true.否则返回false. #inclu ...

  8. 10个JavaScript小技巧

    1.变量转换 看起来很简单,但据我所看到的,使用构造函数,像Array()或者Number()来进行变量转换是常用的做法.始终使用原始数据类型(有时也称为字面量)来转换变量,这种没有任何额外的影响的做 ...

  9. 杭电ACM 偶数求和

    偶数求和 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  10. NSUserDefaults概述

    原创,转载请注明原文:NSUserDefaults概述  By Lucio.Yang 首先,iOS中有四种存储数据的方式-对比iOS中的四种数据存储 NSUserDefaults是其中很常用的一种.N ...