The primary role of a front controller in web-based applications is to encapsulate the typical request/route/dispatch/response cycles inside the boundaries of an easily-consumable API, which is exactly what the web server does. Indeed the process seems redundant at first blush咋一看, but when coupled to an MVC implementation, we’re actually trying to build up a controller that in turn controls other controllers.

Despite the apparent duplication of roles, the implementation of a front controller emerges as a response to the growing complexities of modern web application development. Tunneling all requests through a single entry point is certainly an efficient way to implement a command-based mechanism, which not only allows you to route and dispatch commands to appropriate handlers, but also exposes a flexible structure that can be massaged and scaled without much burden.

尽管角色看起来有点多余,但是前端控制器是现代web应用陈旭复杂性渐增的产物。通过单一的入口来处理所有的请求,是一个实现command-base 机制的高效方式,它不仅允许你route和分发命令道合适的处理者,同时显示了一个灵活的结构。

Frankly speaking, front controllers are easily-tameable可驯服的 可驯养的

creatures. (意思是非常简单)In the simplest scenario, a naive combination of URL rewriting along with a few switch statements is all we need to route and dispatch requests, though in production(在生产中) it might be necessary to appeal to more complex and granular粒状的 implementations, especially if we want to distill routing and dispatching processes through finer-grained 细粒度objects armed with well-segregated responsibilities.

In this two-part article I’ll be exploring in depth a couple of straightforward approaches that you might find appealing, especially if you’re trying to implement an expandable front controller from scratch without sweating excessively(不要出汗过多,即简单) during the process or having to cope with the burdens of a bloated framework.

Routing and Dispatching in a Straightforward Way

In reality, there are so many nifty俏皮的;漂亮的 options that can be used for building a functional front controller, but I’ll start by letting my pragmatic side 务实的一面show (yes, I have one). The first front controller implementation that I’ll go through will be charged with routing/dispatching URIs that conform to the following format:

basepath/controllername/actionname/param1/param2/.../paramN

If you’ve ever laid your hands on a framework that uses the notion of parameterized action controllers, the above URI should familiar to you. It is a fairly ubiquitous pattern. Of course, the most challenging task here is designing a flexible mechanism capable of parsing the URIs in question without much fuss. This can be achieved in all sort of creative ways, either by plain procedural code or appealing to object-oriented code. I’ll be encapsulating the nuts and bolts of the routing/dispatching logic beneath the shell of a single class:

最后挑战的工作是解析url,我们把route/dispatch放在一个类中:

<?php
namespace Library\Controller; interface FrontControllerInterface
{
public function setController($controller);
public function setAction($action);
public function setParams(array $params);
public function run();
}
<?php
namespace Library\Controller; class FrontController implements FrontControllerInterface
{
const DEFAULT_CONTROLLER = "IndexController";
const DEFAULT_ACTION = "index"; protected $controller = self::DEFAULT_CONTROLLER;
protected $action = self::DEFAULT_ACTION;
protected $params = array();
protected $basePath = "mybasepath/"; public function __construct(array $options = array()) { //$options前面有一个array显示表示是array,没有也可以
if (empty($options)) {
$this->parseUri();
}
else {
if (isset($options["controller"])) {
$this->setController($options["controller"]);
}
if (isset($options["action"])) {
$this->setAction($options["action"]);
}
if (isset($options["params"])) {
$this->setParams($options["params"]);
}
}
} protected function parseUri() {
$path = trim(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH), "/");
$path = preg_replace('/[^a-zA-Z0-9]\//', "", $path);
if (strpos($path, $this->basePath) === 0) {
$path = substr($path, strlen($this->basePath));
}
@list($controller, $action, $params) = explode("/", $path, 3);
if (isset($controller)) {
$this->setController($controller);
}
if (isset($action)) {
$this->setAction($action);
}
if (isset($params)) {
$this->setParams(explode("/", $params));
}
} public function setController($controller) {
$controller = ucfirst(strtolower($controller)) . "Controller";
if (!class_exists($controller)) {
throw new \InvalidArgumentException(
"The action controller '$controller' has not been defined.");
}
$this->controller = $controller;
return $this;
} public function setAction($action) {
$reflector = new \ReflectionClass($this->controller);
if (!$reflector->hasMethod($action)) {
throw new \InvalidArgumentException(
"The controller action '$action' has been not defined.");
}
$this->action = $action;
return $this;
} public function setParams(array $params) {
$this->params = $params;
return $this;
} public function run() {
call_user_func_array(array(new $this->controller, $this->action), $this->params);
}
}

$path = trim(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH), "/");

把url的path前面/去掉。

然后用正则:

$path = preg_replace('/[^a-zA-Z0-9]\//'""$path);

再把无效字符替换为空。

if (strpos($path, $this->basePath) === 0) {
$path = substr($path, strlen($this->basePath));
}

strpos寻找当前路径是否以basePath开头,(一定要用===判断,因为如果没有找到该字符串,则返回 false,用==0也是相等的)。

如果以basePath开头,则取后面的字串。

下面颜色上面几句话作用:

$url='http://localhost/php/controller/action/param1/param2';
$path = trim(parse_url($url, PHP_URL_PATH), "/"); // php/controller/action/param1/param2
print $path.'<br/>';  //php/pattern/parseUrL.php/controller/action/param1/param2
$path = preg_replace('/[^a-zA-Z0-9]\//', "", $path); //php/controller/action/param1/param2
print $path.'<br/>'; //php/pattern/parseUrL.php/controller/action/param1/param2
$basePath='php/'; //最开始后面没有带/,导致错误.
if (strpos($path,$basePath) === 0) {
$path = substr($path, strlen($basePath));
}
print $path.'<br/>';///controller/action/param1/param2

@list($controller, $action, $params) = explode("/", $path, 3);分解url。

array explode ( string $delimiter , string $string [, int $limit ] ) 最后一个参数现在分解的大小。

3说明我们最多分成3段。结果:

controller
action
param1/param2

为什么要用@。

当参数少时。

$arr=array(1,2);
list($a)=$arr;

print $a,等于1.

当参数不足时。

$arr=array(1,2);
list($a,$b,$c)=$arr;
echo $a,$b,$c;

Notice: Undefined offset: 2 in F:\xampp\htdocs\php\pattern\parseUrL.php on line 48
1,2

可以看到,产生错误信息,$a=1,$b=2;$c没有值,用@就看不到错误信息了。

判断controller是否存在使用了

bool class_exists ( string $class_name [, bool $autoload = true ] )

这个函数

默认回调用默认调用 __autoload。载入类。

if (!class_exists($controller)) {
throw new InvalidArgumentException(
"The action controller '$controller' has not been defined.");
}

InvalidArgumentException

Exception thrown if an argument does not match with the expected value.

当参数不满足期待的值我们都可以throw这个异常。

检查action:

 public function setAction($action) {
$reflector = new \ReflectionClass($this->controller);
if (!$reflector->hasMethod($action)) {
throw new \InvalidArgumentException(
"The controller action '$action' has been not defined.");
}
$this->action = $action;
return $this;
}

用到了反射。

参数没有做过多检查:

  public function setParams(array $params) {
$this->params = $params;
return $this;
}

注意这个是怎么调用的:

if (isset($params)) {            $this->setParams(explode("/", $params));        }

$params变成了array(param1,param2).

最后run函数;

 public function run() {
call_user_func_array(array(new $this->controller, $this->action), $this->params);
}

可以参考我以前写的文章http://www.cnblogs.com/youxin/p/3150314.html。

The FrontController class’ duties boil down to parsing the request URI, or eventually assembling a brand new one from scratch through a few basic mutators. Once this task has been carried out, the run() method neatly dispatches the request to the appropriate action controller, along with the supplied arguments, if any.

Given its minimal API, consuming the class is a simple two-step process. First, drop into the web root a typical .htaccess file, like this one:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php
上面的写法有错误,index.php前面应该没有/,因为现在已经在该目录下了,如果写了会产生找不到文件错误。 rewrite参考以前写的:http://www.cnblogs.com/youxin/p/3150235.html

Second, set up the following code snippet as index.php:

<?php
use Library\Loader\Autoloader,
Library\Controller\FrontController; require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader = new Autoloader;
$autoloader->register(); $frontController = new FrontController();
$frontController->run();

In this case, the controller simply parses the request URI and feeds it to the given action controller, which is its default behavior by the way. It’s feasible, though, to explicitly provide a URI by calling the corresponding setters, as follows:

<?php
$frontController = new FrontController(array(
"controller" => "test",
"action" => "show",
"params" => array(1)
)); $frontController->run();

The front controller here is pretty malleable, easily configurable either for internally parsing requests or for routing/dispatching custom ones supplied directly from client code. Moreover, the previous example shows in a nutshell how to call the show() method of an hypotheticalTestController class and pass around a single numeric argument to it. Of course, it’s possible to use different URIs at will and play around with the controller’s abilities. Therefore, if you’re bored and want to have some fun for free, just go ahead and do so.

Though I have to admit that my carefully-crafted FrontController class has pretty limited functionality, it makes a statement on its own. It demonstrates that building up a customizable front controller is in fact a straightforward process that can be tamed successfully without having to use obscure and tangled programming principles.

On the flip side, the bad news is that the class has way to many responsibilities to watch over. If you’re skeptical, just check out its run() method. Certainly its implementation is clean and compact, and can be even accommodated to intercept pre/post dispatch hooks. But it does multiple things at the same time and behaves pretty much as a catch-all point for routes and dispatches. It’s preferable to have a front controller dissected in more granular classes, whose responsibilities are narrowed to perform discrete tasks.

Needless to say that getting such a distilled front controller up and running as expected requires traveling down the road to a fairly prolific number of classes and interfaces. I’m reluctant to make this installment excessively lengthy, so I’ll be covering in depth the details of the whole implementation process in the next article. This way you can have some time to twist and bend my sample front controller and make it suit the taste of your delicate palate.

Closing Thoughts

Front controllers are ridiculously simple to implement from scratch, regardless if the approach makes use of procedural code or object-oriented code. And because of its easy-going nature, it’s fairly easy to scale up a naïve, primitive front controller and pitch over its shoulders the whole shebang required for handling RESTful resources behind the scenes.

Quite possibly, the most tangled aspect of writing a front controller is solving the implicit dilemma困境

when it comes to deciding if the requests must be either statically or dynamically routed and dispatched to the appropriate handlers. There’s no formal principle that prescribes规定 all the routing/dispatching logic should be encapsulated within the controller’s boundaries or broken down into standalone modules that can be reused independently.

Precisely, the latter is the form of implementation that I’ll be discussing over the course of the next article. So, stay tuned!

转自:http://phpmaster.com/front-controller-pattern-1/

php前端控制器设计1的更多相关文章

  1. 前后端分离之Web前端架构设计

    架构设计:前后端分离之Web前端架构设计 在前面的文章里我谈到了前后端分离的一些看法,这个看法是从宏观的角度来思考的,没有具体的落地实现,今天我将延续上篇文章的主题,从纯前端的架构设计角度谈谈前后端分 ...

  2. tableview前端基础设计(初级版)

    tableView前端基础设计 实现的最终效果 操作目的:熟悉纯代码编辑TableView和常用的相关控件SearchBar.NavigationBar.TabBar等,以及布局和基本功能的实现. 一 ...

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

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

  4. 前端控制器DispatcherServlet 详解

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

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

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

  6. 前端架构师 摘自《前端架构设计》-micah godbolt

    作为前端架构师,你经常需要制定,或至少能够掌握以上所列的每一项内容.流程中的任何一个环节出现问题,都会迅速演变为开发人员的痛苦,或者导致网站无法持续满足用户要求,甚至崩溃.  前端架构师的用户是开发人 ...

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

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

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

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

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

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

随机推荐

  1. 一,PHP 语法

    基本的 PHP 语法 PHP 的脚本块以 <?php 开始,以 ?> 结束.您可以把 PHP 的脚本块放置在文档中的任何位置. 当然,在支持简写的服务器上,您可以使用 <? 和 ?& ...

  2. 在Android Studio中使用Gradle方便地修改包名

    情景: 主Module引用了多个module,在代码使用R.xx.xx的时候,会import 当前包名.R.而由于需要上架Play做测试,可是目前的包名已经被使用了,所以需要修改包名. 正确使用bui ...

  3. Xvfb+YSlow+ShowSlow搭建前端性能测试框架 - 前端技术 | TaoBaoUED

    Xvfb+YSlow+ShowSlow搭建前端性能测试框架 - 前端技术 | TaoBaoUED Xvfb+YSlow+ShowSlow搭建前端性能测试框架 作者:黑三 | 时间:2010-07-07 ...

  4. 【示例代码】HTML+JS 画图板源码分享

    一个有趣的画图板, 用了 HTML5中的本地存储.下载.canvas 等技术,这个项目中用到了canvas 的很多基础功能,初学者可以学习一下 . 建议开发童鞋使用统一开发环境UDE来进行查看.调试. ...

  5. 如何判断一个变量是数组Array类型

    在很多时候,我们都需要对一个变量进行数组类型的判断.JavaScript中如何判断一个变量是数组Array类型呢?我最近研究了一下,并分享给大家,希望能对大家有所帮助. JavaScript中检测对象 ...

  6. 基于RYU控制器(controller)上的simple-switch 的APP做的測试-SDN/OpenFlow

    近期一直在学习RYU控制器,在使用的过程中,发现有下面几方面的长处:RYU控制器全然使用Python语言编写,在理解起来和上手速度上是挺快的:RYU控制器的总体架构清晰明了,在日后有时间我会整理一个关 ...

  7. Java自定义简单标签

     Java自定义简单标签可以方便的在页面输出信息,并且对于权限的控制,和对于Jsp标签和servlet代码的分离有着很好的作用. 下面将以权限的控制为例自定义一个标签: 一.标签类型 <wxt: ...

  8. IE浏览器下<A>标签不能显示背景图片

    修改前 background: url('./img/active/legendBg.png')no-repeat 修改后 background: url('./img/active/legendBg ...

  9. 关于left join 和 inner join

    今天遇到一个逻辑很复杂的SQL,虽然写出来了,但是并没有完全体会,找了找资料,算是摸清楚了left join和inner join 的实际意义. 感谢PCJIM的文章,写的非常明白,原文地址:http ...

  10. C#中静态方法的运用和字符串的常用方法(seventh day)

    又来到了今天的总结时间,由于昨天在云和学院学的知识没有弄懂,今天老师又专门给我们非常详细地讲了一遍,在这里非常谢谢老师.O(∩_∩)O 话不多说,下面就开始为大家总结一下静态方法的运用和字符串的常用方 ...