AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程(也叫面向方面),能夠經過預編譯方式和運行期動態代理實如今不修改源代碼的狀況下給程序動態統一添加功能的一種技術。AOP實際是GoF設計模式的延續,設計模式孜孜不倦追求的是調用者和被調用者之間的解耦,AOP能夠說也是這種目標的一種實現。php
AOP-PHP是一個PECL擴展,您能夠在PHP中使用面向方面的編程,無需編譯或進行其餘任何中間步驟。html
AOP擴展的設計是最簡單的方法,你能夠認爲PHP中的aop實現。node
AOP旨在讓橫切關注點的分離(緩存,日誌,安全,交易,……)git
網址:http://aop-php.github.io/github
有兩種安裝模式:編程
第一種方法:centos
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的一些專業術語。
在實踐以前咱們須要準備四個文件:測試函數文件testfunction.php、測試類文件testclass.php、測試aop文件testaop.php和運行文件test.php。
這樣作能夠真實模擬咱們的項目,大部分的項目都是這樣佈局的。
在代碼中一些特殊點以前使用的通知,正常是調用一個方法或者函數。
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 想要 測試前通知類屬性截取
在代碼中一些特殊點以後使用的通知,通常是調用一個方法或者函數。
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
類和類屬性和前通知相似,爲了節省篇幅,這裏偷懶了。
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_add_before,aop_add_after,aop_add_around以外,咱們還要記住這幾個重要的函數。
獲取通知的類型。有如下幾個默認值。通常用在方法的屬性更改。
• 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)
獲取方法的參數。通常用在aop_add_before/aop_add_around。
設置方法的參數。通常用在aop_add_before/aop_add_around。
獲取方法的返回值。通常用在aop_add_after/aop_add_around。
設置方法的返回值。通常用在aop_add_after/aop_add_around。
讓方法運行。通常用在aop_add_around。
具體詳細說明,請參考官方文檔。
新建一個文件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在真實意義上實現了php的aop,用戶無需用其餘的方式便可輕鬆實現。aop的編程思想是一把利刃,可讓耦合性差的項目輕鬆實現解耦。
所有測試文件和編輯後文件打包。點此下載。(基於ceotos環境php5.3編譯)