php之aop實踐


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(點切割):定義了一種把通知匹配到某些接入點的方式。
 
 

參考文檔

 
了解了這些知識之后我們還需要下載aop-php的說明文檔。 官方文檔下載 
 
好了,E文好的可以看官方文檔,直接飄過下面的文字。
 
 

准備文件

在實踐之前我們需要准備四個文件:測試函數文件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編譯)

 

 

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM