AOP是时下比较流行的一种编程思想,它为程序的解耦带来了进一步的发展。在出现AOP的应用之前,我们如果需要在每个程序执行前或执行后记录LOG,在执行每个代码前进行用户访问控制,就不得不重复的显式调用同一个方法。如
public void anyfunc(){
createLog();
}或
public void anyfunc(){
if(RBAC::access()){
}
}这些“方面”的代码会显式的存在与每个需要它的方法中,与业务代码形成了紧耦合的关系。而AOP的出现解决了固定需求的代码与普通的业务代码之间的隔离。为程序提供了更高的可扩展性与可修改性,更加符合面向对象编程的原则。在现今流行的应用于WEB开发的语言中,Java通过动态代理技术原理可以很好的实现AOP的思想。Python也可以通过@修饰符来实现AOP。而PHP确不能通过语言本身的特性来实现AOP的需求。
对于PHP先天性的弱势,许多PHP框架也采取了一些措施,如Yii框架提供了beforeControllerAction与afterControllerAction方法,支持代码执行时的before与after操作。但与JAVA以注释的实现方式相比,还是缺少了一定的灵活性。那么PHP能不能也通过注释的方式来实现AOP呢?之前我一直认为除非PHP的语言特性支持,否则要想通过注释的方式来实现AOP是不可能的。但当我在一次工作中接触到一款名为OpenCart的开源电子商店的代码后,受到了一点启发。其实,我们完全可以通过重写页面的方法来“实现”注释方式的AOP呀。下面,我将以自己做的一个demo样本,与大家一起探讨下PHP的”注释AOP“实现,希望能对大家有一个启发。此段demo是在Yii框架下实现的,里面还有一些问题需要进行优化和完善。
首先给出此方法的一个逻辑流程:
思想很简单,就是根据注释,按照一定规则生成新的代码文件,然后让程序执行新生成的文件代码。由于每次操作都需要生成新的文件,这样对程序的执行效率会产生一定的影响,所以我们可以考虑按一定的策略来生成新文件,比如按照文件的修改时间来生成新文件,在执行相应操作时先判断controller文件有无修改,如果无修改,则读取上次生成的文件执行,如在上次执行的间隔时间中,controller文件有修改,则生成新的文件。
在样例中,我们主要支持before和after的操作,before是在controller方法执行前执行,after是在controller方法执行后执行。举个例子,有以下代码:
public function logic(){
echo 'Hello World';
}那么在执行logic()方法时,会先执行Logger类的createlog方法记录log,log方法不会渗透到logic()方法的实体内,而是以注释的方式存在。下面我们就来看看实现这项需求的具体代码。代码实现步骤:
一、找到CWebApplication.php文件,它的位置是frameworkwebCWebApplication.php。添加parseClassFile方法用来解析原有的Controller类,代码如下:
public function parseClassFile($id,$aopconfigpath){
$className=ucfirst($id).'Controller';
$classFile=$this->getControllerPath().DIRECTORY_SEPARATOR.$className.'.php';
if(!is_file($classFile)){
return false;
}
$originalContent = file_get_contents($classFile);
//获取原始Controller的内容
$mark_patten = '//s';
$mark_patten1 = '/(|)/s';
$mark_patten2 = '/([^d]|[^d])/s';
preg_match_all($mark_patten1, $originalContent,$mark,PREG_OFFSET_CAPTURE);
if(empty($mark[0])){
return $originalContent;
//如果没有找到相关的AOP注释,则直接返回原始内容
}
$newContent = $originalContent;
//以下是具体解析注释的过程
foreach($mark[0] as $k=>$m){
if($k!=0){
preg_match_all($mark_patten2, $newContent,$submark,PREG_OFFSET_CAPTURE);
$m[1] = $submark[0][0][1];
$m[0] = $submark[0][0][0];
}
$front_part = substr($newContent,0,$m[1]);
$behind_part = substr($newContent,$m[1]+strlen($m[0]));
$replace_part = preg_replace('/s+/', '', $m[0]).$m[1];
$exact_pattens[] = $replace_part;
$newContent = $front_part.$replace_part."n".$behind_part;
}
foreach($exact_pattens as $k=>$exact_patten){
$exact_patten = addcslashes($exact_patten,'的注释。在实际的用户请求中,就会在执行相应的controller方法前或后(取决于注释的关键字是before还是after)执行切面类的方法了。
虽然通过代码重构的技术,可以实现PHP的注释AOP功能,但此种方法也有一些缺点,如I/O操作带来的性能消耗。当Controller文件占用空间较大时,会对执行工作效率产生严重的影响。另外如果要让注释支持更多的功能,则需要提供更为复杂的解析方式,解析是通过正则表达式与替换、字符串查找方式来进行的,而这些操作对系统的性能也有一定影响。所以在进行解析时应尽量避免内嵌循环遍历等操作。样例中的代码还有许多地方需要修改和优化,希望此篇文章能为大家起到抛砖引玉的作用。



