web框架思考(2)-拦截器实现

By | 2016年1月22日

实现一个web框架需要哪些模块,需要包含什么样的功能,需要怎样分层。。。
针对不同领域开发的框架虽然侧重点会有区别(比如博客有typecho,emlog,开源CMS有wordpress,drupal,还有一些商城系统),但是一些基本的元素还是一致的。
本文简要分析了web框架中拦截器的php实现

不论是基于j2ee的struts、webwork,还是基于php的ThinkPHP,或者其他各类功能完善的web框架,都会引入拦截器机制。
![lj.jpg][1]
首先使用拦截器能够提高代码复用性。比如在执行某一类功能的多个action之前都需要对用户权限进行验证,或者需要给一系列action的输出加上一些特殊信息,虽然给每一个action前后都加上同样的处理可以实现此功能,但是会造成很多重复代码,一眼看上去就比较挫。

其次使用配置管理拦截器能够最大限度的减少代码耦合,可以对各种附加功能进行“可插拔式”的管理。在web应用中这样的机制毋庸置疑是极有优势的,比如应用发布后发现有一个功能的权限没有加保护,这个时候只要改一下对应的配置文件,甚至不需要重启服务器就可以将增加的功能部署到运行的应用中。

下面介绍具体在php中如何实现一个基本的拦截器机制。

还是从用户请求的action开始,假如用户现在请求一个action,我们的拦截器需要做的是验证用户的权限,并格式化返回的结果,加入这个拦截器之后应该是 |用户请求->拦截器处理请求参数->action处理->拦截器处理返回结果->返回结果给用户|,当然加入用户无权限的话,在第二步拦截器就会返回错误信息。首先将上一章的示例action封装成ActionInvoke对象,以便传递给拦截器处理:

class ActionInvoke {
    private $params;
    private $action;
    private $interceptors;
    public function __construct($action, $params) {
        $this->action = $action;
        $this->params = $params;
        // 从配置文件中获取action对应的拦截器列表
        $this->interceptors = $this->getInterceptors();
    }
    public function invoke() {
        // 拦截处理
    }
    private function getInterceptors() {
        // 示例数据:
        return array("AuthInterceptor", "DisplayInterceptor");
    }
    private function execute() {
        // 执行action处理,封装上一章action的处理过程
    }
}

代码中真正执行用户action的是execute方法,invoke是给拦截器调用的代理方法,是拦截器机制的核心部分。具体实现如下:

public function invoke() {
    if (! empty($this->interceptors)) {
        $interceptor = array_shift($this->interceptors);
        if (! class_exists($interceptor)) {
            return $this->invoke();
        }
        $cls = new ReflectionClass($interceptor);
        if (!$cls->hasMethod("intercept")) {
            return $this->invoke();
        }
        $method = $cls->getMethod("intercept");
        return $method->invoke($cls->newInstance(), $this);
    } else {
        return $this->execute();
    }
}

首先判断拦截器在列表中是否存在,假如存在,从列表中取出第一个拦截器执行,并将其从列表中删除。所有拦截器执行的都是invoke方法,所以一般可以定义一个公共的接口。如果拦截器列表为空,则真正开始执行action的处理。下面定义一个验证权限的拦截器:

require_once("Interceptor.php");
class AuthInterceptor implements Interceptor {
    public function intercept($action) {
        echo "--begin auth intercept<br/>";
        //处理输入
        $ret = $action->invoke();
        //处理输出
        echo "<br>--end auth intercept<br>";
        return $ret;
    }
}

这个代码比较简单,就是实现了Interceptor接口的intercept方法,此处只打印了一些输出,然后直接调用action的invoke代理方法。继续看到上面action的invoke方法,假如action只定义了这一个拦截器,那么此时拦截器列表为空,则执行action处理。结果经过拦截处理后返回。假如有其他的拦截器,会按上述步骤依次执行,直到执行完所有的拦截器,然后从各个拦截器依次返回。其实这里的实现就是一个简单的递归调用。

然后在router中的调用方式也有一些变化,将具体处理action的代码段删除,请求交给ActionInvoke对象:

$action = $router["action"];
// 寻找到第一个匹配的路由即执行,然后返回
$actionInvoke = new ActionInvoke($action, $params);
echo $actionInvoke->invoke();
return;

再给这个action加入一个新的拦截器DisplayInterceptor测试一把,页面上返回的结果如下:

--begin auth intercept
----begin display intercept
==>call UserAction method and return result
----end display intercept
--end auth intercept
==>action result

可以看到请求依次经过拦截器处理并依次返回的过程。

发表评论

电子邮件地址不会被公开。 必填项已用*标注