AOP:Aspect Oriented Programming(面向切面编程)
字面解析:这里的切面(Aspect) 通常是指类的一个实例方法,也就是说其实我们只要在类的一个实例方法执行前或后面挂载前置或后置方法,有点像钩子,其实目的都是一样的,都是为了切分和扩展相应方法的功能,而不需要在原始方法里面改动。
题外话:什么是实例方法?就是类的方法前面没有 static 关键字修饰,并且只有类的实例被创建(new)出来之后才可以使用的方法。
实例1:模拟数据库的建立连接前后的动作
<?php namespace Mysql; class Database { public function beforeConnect($connStr) { echo "before connect\n"; } public function afterConnect($connStr) { echo "after connect\n"; } public function connect($connStr) { $beforeMethod = "before".ucfirst(__FUNCTION__); $args = func_get_args(); if(method_exists($this,$beforeMethod)){ call_user_func_array([$this,$beforeMethod], $args); } // todo: connect the database echo "connecting\n"; $afterMethod = "after".ucfirst(__FUNCTION__); if(method_exists($this,$afterMethod)){ call_user_func_array([$this,$afterMethod], $args); } } } $db = new Database();
$db->connect("...");
运行结果如下:
before connect connecting after connect
实例2:安装PECL C扩展,PHP AOP,仅支持PHP5.5
项目地址:https://github.com/AOP-PHP/AOP (可直接看源码)
假定有下面的类:
<?php class MyServices { public function doAdminStuff1 () { //some stuff only the admin should do echo "Calling doAdminStuff1"; } public function doAdminStuff2 () { //some stuff only the admin should do echo "Calling doAdminStuff2"; } }
我们接下来注入关注面:
aop_add_before('MyServices->doAdmin*()', 'adviceForDoAdmin');
其中adviceForDoAdmin:
function adviceForDoAdmin () { if ((! isset($_SESSION['user_type'])) || ($_SESSION['user_type'] !== 'admin')) { throw new Exception('Sorry, you should be an admin to do this'); } }
其实上面的代码作用很简单,就是当我们调用 MyServices 类下面的 doAdmin 开头的实例方法前先执行一遍 adviceForDoAdmin 方法,这个 C 扩展是不是挺神奇的。
其实,phper平时写业务代码比较多,较少接触到AOP相关的概念,若你看过某些开源框架的源码或者自己设计过需要AOP切分的业务逻辑那就另当别论了。然而Java里面包含了很多AOP的经典设计范例,比较知名的如Spring AOP。