php笔记之:AOP的应用


Posted in PHP onApril 24, 2013

介绍
 
你以前听说过AOP(Aspect Oriented Programming)吗?虽然在php方面,好像没有过多的使用,但是在企业级开发中,AOP被广泛使用。我将借此文,向大家介绍PHP方面的AOP。

这篇文章主要解释AOP的概念。

 
什么是AOP?
 
在应用开发中,我们经常发现需要很多功能,这些功能需要经常被分散在代码中的多个点上,但是这些点事实上跟实际业务没有任何关联。比如,在执行一些特殊任务之前需要确保用户是在登陆状态中,我们把这些特殊人物就叫做"cross-cutting concerns",让我们通过Wikipedia来了解一下"cross-cutting concerns"(横向关系)的定义。
在计算机科学中,"cross-cutting concerns"指的是“切面(或方向)编程”。这些关系不能从其他系统(框架设计或者某些实现)中很好的分解出来,以至于出现代码重复,在系统中存在有意义的依赖关系,或者两者兼有之。
 现在你对于“横向关系”应该有一个基础的认识,让我们看看他们在代码中是怎么样的?

假设一种场景,你是一个博客站点的编辑。你需要登陆站点,然后进行创建帖子,验证帖子,编辑帖子等等。如果你没有登陆,那么你应该直接到登陆界面。为了确保这些行为是安全的,以上的任何操作都需要进行有效验证,代码如下。

<?php
class BlogPost extends CI_Controller
{
    public function createPost() {
        if (!Authentication::checkAuthentication()) {
            // redirect to login
        }
        else {
            // proceed
            Messages::notifyAdmin();
        }
    }
    public function approvePost() {
        if (!Authentication::checkAuthentication()) {
            // redirect to login
        }
        else {
            // proceed
        }
    }
    public function editPost() {
        if (!Authentication::checkAuthentication()) {
            // redirect to login
        }
        else {
            // proceed
        }
    }
    public function viewPost() {
        // ...
    }
}

 看上面的代码,你会发现在每个方法之前都调用了checkAuthentication(),因为这些行为需要用户登陆之后才能进行。还有就是notifyAdmin()来辨别是否是管理员帐号,以便创建新贴。看见没有,有很多“重复的代码”,而且BlogPost类,应该仅负责管理帖子。验证和辨别身份应当是分离的。我们违反了“单一职责原则”。

单一职责原则讲述的是每个类应该只有单一的责任(任务),而且应该把整个责任都封装在一个类中。所有服务应该按照职责严谨而均衡的进行分布。

 迄今为止,我们能够明白AOP所表达的意思。横向切面关系被成组的放进一个类中,我们管这个类叫“切面”。从我们核心代码中分离横向切面关系的过程就叫做Aspect Oriented Programming。

AOP专业术语

有很多条件专门用于解释AOP的特性。理解这些条件将是你成功把AOP集成到你的项目中的钥匙。
Aspect
Advice
Joinpoint
Pointcut
我们已经学习到切面(Aspect)是什么!现在让我们了解一下其他三个条件意味着什么?

Advice(通知)
Advice用于调用Aspect(切面),正如其名所暗示,Advice用于定义某种情况下做什么和什么时间做这件事情。在我们之前的例子中,checkAuthentication(做什么)是advice(通知),在指定方法中它应该在执行代码之前(什么时间)被调用。

 
Joinpoint(接入点)
Joinpoint是我们创建Advice应用中的位置。再翻看之前的代码,你会发现我调用了几个与业务逻辑没有直接关联的功能。在createPost()中,如,cross-cutting concerns应该在执行验证逻辑之前和发送信息给管理员之后发生。这些都可能是接入点。

在你的应用代码中,接入点可以放置在任何位置。但是Advice仅能在某些点中布置,这要根据你的AOP框架,过后我会讨论。

Pointcut(点切割)
 点切割定义了一种把通知匹配到某些接入点的方式。虽然在我们的例子中只有一对接入点,但是在你的应用中你可以放置上千个接入点,你也不需要把通知应用到所有的接入点上。你可以把一些你认为有必要的接入点绑定到通知上。

假设我们想要通知 createPost(),approvePost() 和 editPost(),但是现在没有viewPost()。我们使用某种方法把这三种方法绑定到通知上。之后我们创建一个包含切面细节的XML文件,这些细节包含一些匹配接入点的正则表达式。

总结:当有横向切入关系存在于我们的应用的时候,我们可以创建一个切面,这个切面在一些选择使用点切割的接入点上应用通知功能。

 
AOP 通知类型
 

通知代码我们可以用很多中方式表现。我之前提到,这些通知代码依赖你使用的框架,但是有些你需要熟悉的类型,请看下面:

前通知

返回后通知

抛出后通知

周边通知

前通知
在你的代码中一些特殊点之前使用通知——正常是调用一个方法。

迄今为止,为了简化概念和为了让你更快的理解你的代码,我经常把通知写到方法里。但是在真实的环境里,通知经常是不写在方法里的。应该有一个独立的控制器,每个方法都在这个控制器里,而且每个方法都包裹着AOP的功能。这个全局的控制器运行在整个系统里,而且对我们是不可见的。

<?php
class PathController
{
    function controlPaths($className, $funcName) {
        Authentication::checkAuthentication();
        $classObj = new $className();
        $classObj->$funcName();
    }
}

在这里假设有这么一个类,主要是用于给你展现这个类实际上发生了什么事情。假设那个controlPaths方法是应用中全局切入点,访问应用中的每个方法都需要通过这个方法访问。上面的方法中在执行每个方法之前,我们调用了通知checkAuthentication()。——这就是前通知。

返回后通知

这个通知在指定功能执行完后只执行一次,并且返回那个访问点。考虑下面的代码:

<?php
class PathController
{
    function controlPaths($className, $funcName) {
        $classObj = new $className();
        $classObj->$funcName();
        Database::closeConnection();
    }
}

按 Ctrl+C 复制代码注意这里,当方法完成之后,我们清理了数据库资源。在返回通知之后,我们调用这个通知。

抛出后通知
如果在执行进程期间函数抛出异常,那么在抛出完异常之后应用通知。这里是抛出完异常之后,通知就变成错误提示。

<?php
class PathController
{
    function controlPaths($className, $funcName) {
        try {
            $classObj = new $className();
            $classObj->$funcName();
        }
        catch (Exception $e) {
            Error::reportError();
        }
    }
}

 

周边通知
第四种通知是周边通知,他是前通知和返回后通知的合并体。

 <?php
class PathController
{
    function controlPaths($className, $funcName) {
        Logger::startLog();
        $classObj = new $className();
        $classObj->$funcName();
        Logger::endLog();
    }
}
PHP 相关文章推荐
建立动态的WML站点(三)
Oct 09 PHP
转换中文日期的PHP程序
Oct 09 PHP
PHP+JS无限级可伸缩菜单详解(简单易懂)
Jan 02 PHP
php一些公用函数的集合
Mar 27 PHP
PHP similar_text 字符串的相似性比较函数
May 26 PHP
php header Content-Type类型小结
Jul 03 PHP
php class类的用法详细总结
Oct 17 PHP
ThinkPHP CURD方法之order方法详解
Jun 18 PHP
thinkphp模板用法和内容输出实例
Nov 28 PHP
PHP实现在数据库百万条数据中随机获取20条记录的方法
Apr 19 PHP
Laravel实现autoload方法详解
May 07 PHP
swoole和websocket简单聊天室开发
Nov 18 PHP
php class中self,parent,this的区别以及实例介绍
Apr 24 #PHP
PHP中::、-&amp;gt;、self、$this几种操作符的区别介绍
Apr 24 #PHP
php判断终端是手机还是电脑访问网站的思路及代码
Apr 24 #PHP
用PHP提取中英文词语以及数字的首字母的方法介绍
Apr 23 #PHP
基于PHP读取TXT文件向数据库导入海量数据的方法
Apr 23 #PHP
『PHP』PHP截断函数mb_substr()使用介绍
Apr 22 #PHP
基于magic_quotes_gpc与magic_quotes_runtime的区别与使用介绍
Apr 22 #PHP
You might like
同时提取多条新闻中的文本一例
2006/10/09 PHP
PHP投票系统防刷票判断流程分析
2012/02/04 PHP
php设计模式之命令模式使用示例
2014/03/02 PHP
smarty简单入门实例
2014/11/28 PHP
php正则表达式基本知识与应用详解【经典教程】
2017/04/17 PHP
PHP中的函数声明与使用详解
2017/05/27 PHP
laravel解决迁移文件一次删除创建字段报错的问题
2019/10/24 PHP
js GridView 实现自动计算操作代码
2009/03/25 Javascript
jquery tools之tooltip
2009/07/25 Javascript
javascript delete 使用示例代码
2010/03/29 Javascript
jQuery 获取对象 定位子对象
2010/05/31 Javascript
解决jquery异步按一定的时间间隔刷新问题
2012/12/10 Javascript
javascript实现存储hmtl字符串示例
2014/04/25 Javascript
angularJS 入门基础
2015/02/09 Javascript
在浏览器中打开或关闭JavaScript的方法
2015/06/03 Javascript
javascript事件委托的方式绑定详解
2015/06/10 Javascript
js实现prototype扩展的方法(字符串,日期,数组扩展)
2016/01/14 Javascript
使用PHP+JavaScript将HTML页面转换为图片的实例分享
2016/04/18 Javascript
js前端面试题及答案整理(一)
2016/08/26 Javascript
Angular 应用技巧总结
2016/09/14 Javascript
jquery实现放大镜简洁代码(推荐)
2017/06/08 jQuery
Vue自定义指令实现checkbox全选功能的方法
2018/02/28 Javascript
原生JS实现逼真的图片3D旋转效果详解
2019/02/16 Javascript
javascript自定义右键菜单插件
2019/12/16 Javascript
解决vue字符串换行问题(绝对管用)
2020/08/06 Javascript
OpenLayers3实现鼠标移动显示坐标
2020/09/25 Javascript
swiperjs实现导航与tab页的联动
2020/12/13 Javascript
python动态加载包的方法小结
2016/04/18 Python
python3调用百度翻译API实现实时翻译
2018/08/16 Python
一看就懂得Python的math模块
2018/10/21 Python
python实现多层感知器MLP(基于双月数据集)
2019/01/18 Python
使用Python和Prometheus跟踪天气的使用方法
2019/05/06 Python
Python 实现顺序高斯消元法示例
2019/12/09 Python
关于 HTML5 的七个传说小结
2012/04/12 HTML / CSS
贷款委托书范本
2014/04/08 职场文书
群众路线剖析材料怎么写
2014/10/09 职场文书