aop簡介
AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程(也叫面向方面),可以通過預編譯方式和運行期動態代理實現在不修改源代碼的情況下給程序動態統一添加功能的一種技術。AOP實際是GoF設計模式的延續,設計模式孜孜不倦追求的是調用者和被調用者之間的解耦,AOP可以說也是這種目標的一種實現。
aop-php簡介
AOP-PHP是一個PECL擴展,您可以在PHP中使用面向方面的編程,無需編譯或進行其他任何中間步驟。
AOP擴展的設計是最簡單的方法,你可以認為PHP中的aop實現。
AOP旨在讓橫切關注點的分離(緩存,日志,安全,交易,……)
aop-php安裝
安裝
有兩種安裝模式:
第一種方法:
sudo pecl install aop-beta
第二種方法:
#Clone the repository on your computer git clone https://github.com/AOP-PHP/AOP cd AOP #prepare the package, you will need to have development tools for php phpize #compile the package
make #before the installation, check that it works properly make test #install make install
錯誤處理
筆者在第二種方法安裝中出現了錯誤(如果沒有錯誤這里可以飄過):
Can't locate Autom4te/C4che.pm in @INC (@INC contains: /usr/local/share/autoconf...
解決辦法是重新安裝autoconf:
#wget http://ftp.gnu.org/gnu/autoconf/autoconf-latest.tar.gz #tar -zxf autoconf-latest.tar.gz #rpm -qf /usr/bin/autoconf #查看autoconf的版本 #rpm -e --nodeps autoconf-2.59-12 #卸載原來版本 #./configure --prefix=/usr #make && make install
編譯安裝成功后,需要在php.ini里裝載模塊,一般在centos里php的模塊裝載在/etc/php.d里面,新建一個文件aop.ini ,內容為:
extension=aop.so
安裝成功后查看phpinfo,會看到一下內容:
aop-php學前准備
專業術語
在實踐之前我們需要先學習哈aop的一些專業術語。
參考文檔
准備文件
在實踐之前我們需要准備四個文件:測試函數文件testfunction.php、測試類文件testclass.php、測試aop文件testaop.php和運行文件test.php。
這樣做可以真實模擬我們的項目,大部分的項目都是這樣布局的。
aop-php實踐之通知
前通知aop_add_before
在代碼中一些特殊點之前使用的通知,正常是調用一個方法或者函數。
我們先測試函數
testfunction.php代碼:
<?php function testFunc1(){ echo 'aop_add_before <br/>'; }
testaop.php代碼:
<?php $testpoint1 = function () { echo "這是前切點測試函數:"; }; aop_add_before('testFunc1()', $testpoint1);
test.php代碼:
<?php require 'testaop.php'; require 'testclass.php'; require 'testfunction.php'; header("Content-Type:text/html;charset=utf-8"); testFunc1();
不出意外,執行test.php我們將會看到:
這是前切點測試函數:aop_add_before
我們再玩哈類
testclass.php代碼:
<?php class testClass1 { public function testBeforAdd1() { echo get_class($this); } }
testaop.php代碼:
<?php $testpoint1 = function () { echo "這是前切點測試函數:"; }; $testpoint2 = function () { echo "這是前切點測試類方法:"; }; aop_add_before('testFunc1()', $testpoint1); aop_add_before('testClass1->testBeforAdd1()', $testpoint2);
test.php代碼:
<?php require 'testaop.php'; require 'testclass.php'; require 'testfunction.php'; header("Content-Type:text/html;charset=utf-8"); testFunc1(); $testClass1 = new testClass1(); echo $testClass1->testBeforAdd1();
執行test.php
這是前切點測試函數:aop_add_before 這是前切點測試類方法:testClass1
再測試類屬性
testclass.php源碼
<?php //測試前通知類 class testClass1 { public function testBeforAdd1() { echo get_class($this) .'<br />'; } } //測試前通知類屬性 class testClass2 { private $name; public $publicProperty1 = 'test'; public function __construct ($name) { $this->name = $name; } public function getName () { return $this->name; } public function test () { $this->publicProperty1 = 'test'; return $this->publicProperty1; } }
testaop.php源碼
<?php $testpoint11 = function () { echo "這是前切點測試函數:"; }; $testpoint12 = function () { echo "這是前切點測試類方法:"; }; aop_add_before('testFunc1()', $testpoint11); aop_add_before('testClass1->testBeforAdd1()', $testpoint12); //------測試類屬性 class changeProperty { public function shoot ( $who, $what) { if($what == 'test'){ $what = '測試前通知類屬性截取 <br/>'; } echo "$who 想要 $what "; } } $testclass1 = new changeProperty(); $testpoint2 = function ( AopJoinPoint $aop_tjp ) use( $testclass1 ) { if ( $aop_tjp->getKindOfAdvice() === AOP_KIND_BEFORE_READ_PROPERTY ) { return; // 如果屬性不能讀則返回 } elseif ( $aop_tjp->getKindOfAdvice() === AOP_KIND_BEFORE_WRITE_PROPERTY ) { $testclass1->shoot($aop_tjp->getObject()->getName(),$aop_tjp->getAssignedValue()); } }; //測試類屬性 aop_add_before('testClass2->publicProperty1', $testpoint2);
test.php源碼
<?php require 'testaop.php'; require 'testclass.php'; require 'testfunction.php'; header("Content-Type:text/html;charset=utf-8"); //前通知 testFunc1(); $testClass1 = new testClass1(); echo $testClass1->testBeforAdd1(); $runtest2 = new testClass2('skyboy'); $runtest2->test();
執行test.php
這是前切點測試函數:aop_add_before 這是前切點測試類方法:testClass1 skyboy 想要 測試前通知類屬性截取
返回后通知aop_add_after
在代碼中一些特殊點之后使用的通知,一般是調用一個方法或者函數。
測試函數
testfunction.php源碼:
function testFunc2(){ echo '這是返回后通知測試:'; }
testaop.php源碼:
//測試返回后通知 $testpoint22 = function () { echo "aop_add_after <br/>"; }; aop_add_after('testFunc2()', $testpoint22);
test.php源碼:
//后通知 testFunc2();
執行test.php
這是返回后通知測試:aop_add_after
類和類屬性和前通知類似,為了節省篇幅,這里偷懶了。
周邊通知aop_add_around
測試函數
testfunction.php源碼:
function testFunc3($param1,$param2){ return $param1. $param2; }
testaop.php源碼:
//測試周邊通知 function testaround (AopJoinPoint $object) { $args = $object->getArguments(); if ($args[0] !== null) { $args[0] = '我想測試'; } if ($args[1] !== null) { $args[1] = '周邊通知:'; } $object->setArguments($args); $object->process(); $returnValue = $object->getReturnedValue(); $returnValue .= 'aop_add_around<br/>'; $object->setReturnedValue($returnValue); } aop_add_around('testFunc3()', 'testaround');
test.php源碼:
//周邊通知 echo testFunc3(1,2);
執行test.php
我想測試周邊通知:aop_add_around
類和類屬性和前通知類似。
aop-php函數說明
除了三個重要函數aop_add_before,aop_add_after,aop_add_around之外,我們還要記住這幾個重要的函數。
getKindOfAdvice
獲取通知的類型。有以下幾個默認值。一般用在方法的屬性更改。
• AOP_KIND_BEFORE before a given call, may it be function, method or property
• AOP_KIND_BEFORE_METHOD before a method call (method of an object)
• AOP_KIND_BEFORE_FUNCTION before a function call (not a method call)
• AOP_KIND_BEFORE_PROPERTY before a property (read or write)
• AOP_KIND_BEFORE_READ_PROPERTY before a property access (read only)
• AOP_KIND_BEFORE_WRITE_PROPERTY before a property write (write only)
• AOP_KIND_AROUND around a given call, may it be function, method or property access (read / write)
• AOP_KIND_AROUND_METHOD around a method call (method of an object)
• AOP_KIND_AROUND_FUNCTION around a function call (not a method call)
• AOP_KIND_AROUND_PROPERTY around a property (read or write)
• AOP_KIND_AROUND_READ_PROPERTY around a property access (read only)
• AOP_KIND_AROUND_WRITE_PROPERTY around a property write (write only)
• AOP_KIND_AFTER after a given call, may it be function, method or property access (read / write)
• AOP_KIND_AFTER_METHOD after a method call (method of an object)
• AOP_KIND_AFTER_FUNCTION after a function call (not a method call)
• AOP_KIND_AFTER_PROPERTY after a property (read or write)
• AOP_KIND_AFTER_READ_PROPERTY after a property access (read only)
• AOP_KIND_AFTER_WRITE_PROPERTY after a property write (write only)
getArguments
獲取方法的參數。一般用在aop_add_before/aop_add_around。
setArguments
設置方法的參數。一般用在aop_add_before/aop_add_around。
getReturnedValue
獲取方法的返回值。一般用在aop_add_after/aop_add_around。
setReturnedValue
設置方法的返回值。一般用在aop_add_after/aop_add_around。
process
讓方法運行。一般用在aop_add_around。
具體詳細說明,請參考官方文檔。
aop-php開啟和關閉
新建一個文件aopopenclose.php
源碼如下:
<?php ini_set("aop.enable", "1"); echo "aop is enabled<br />"; function foo () { echo "I'm foo<br />"; } $adviceShowFoo = function () { echo "After foo<br />"; }; aop_add_after('foo()', $adviceShowFoo); foo(); ini_set('aop.enable', '0'); echo "aop is now disabled<br />"; foo(); echo "But you can still register new aspects<br />"; aop_add_after('f*()', $adviceShowFoo); foo(); ini_set('aop.enable', '1'); echo "Aop is now enabled<br />"; foo();
運行結果:
aop is enabled I'm foo After foo aop is now disabled I'm foo After foo But you can still register new aspects I'm foo After foo After foo Aop is now enabled I'm foo After foo After foo
aop-php總結
aop-php在真實意義上實現了php的aop,用戶無需用其他的方式即可輕松實現。aop的編程思想是一把利刃,可以讓耦合性差的項目輕松實現解耦。
全部測試文件和編輯后文件打包。點此下載。(基於ceotos環境php5.3編譯)