標籤:
aop簡介
AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程(也叫面向方面),可以通過先行編譯方式和運行期動態代理實現在不修改原始碼的情況下給程式動態統一添加功能的一種技術。AOP實際是GoF設計模式的延續,設計模式孜孜不倦追求的是調用者和被調用者之間的解耦,AOP可以說也是這種目標的一種實現。
aop-php簡介
AOP-PHP是一個PECL擴充,您可以在PHP中使用面向方面的編程,無需編譯或進行其他任何中間步驟。
AOP擴充的設計是最簡單的方法,你可以認為PHP中的aop實現。
AOP旨在讓橫切關注點的分離(緩衝,日誌,安全,交易,……)
網址:http://aop-php.github.io/
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
./configure --with-aop --with-php-config=/usr/bin/php-config
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的一些專業術語。
Aspect(切面):橫向切面關係被成組的放進一個類中。Advice(通知):用於調用切面,定義某種情況下做什麼和什麼時間做這件事情。通知又分為:前通知、返回後通知、拋出後通知和周邊通知。Joinpoint(存取點):建立通知的位置。Pointcut(點切割):定義了一種把通知匹配到某些存取點的方式。 具體可以參考這篇部落格:http://www.cnblogs.com/baochuan/archive/2012/08/22/2644529.html 參考文檔 瞭解了這些知識之後我們還需要下載aop-php的說明文檔。官方文檔下載 好了,E文好的可以看官方文檔,直接飄過下面的文字。 準備檔案
在實踐之前我們需要準備四個檔案:測試函數檔案testfunction.php、測試類別檔案testclass.php、測試aop檔案testaop.php和運行檔案test.php。
這樣做可以真實類比我們的項目,大部分的項目都是這樣布局的。
aop-php實踐之通知
前通知aop_add_before
在代碼中一些特殊點之前使用的通知,正常是調用一個方法或者函數。
我們先測試函數
testfunction.php代碼:
<?phpfunction testFunc1(){ echo ‘aop_add_before <br/>‘;}
testaop.php代碼:
<?php$testpoint1 = function () {echo "這是前切點測試函數:";};aop_add_before(‘testFunc1()‘, $testpoint1);
test.php代碼:
<?phprequire ‘testaop.php‘;require ‘testclass.php‘;require ‘testfunction.php‘;header("Content-Type:text/html;charset=utf-8"); testFunc1();
不出意外,執行test.php我們將會看到:
這是前切點測試函數:aop_add_before
我們再玩哈類
testclass.php代碼:
<?phpclass 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代碼:
<?phprequire ‘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源碼
<?phprequire ‘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 這是前切點測試類別方法:testClass1skyboy 想要 測試前通知類屬性截取
返回後通知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
源碼如下:
<?phpini_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 enabledI‘m fooAfter fooaop is now disabledI‘m fooAfter fooBut you can still register new aspectsI‘m fooAfter fooAfter fooAop is now enabledI‘m fooAfter fooAfter foo |
aop-php總結
aop-php在真實意義上實現了php的aop,使用者無需用其他的方式即可輕鬆實現。aop的編程思想是一把利刃,可以讓耦合性差的項目輕鬆實現解耦。
全部測試檔案和編輯後檔案打包。點此下載。(基於ceotos環境php5.3編譯)
連結
php之aop實踐