您好,登錄后才能下訂單哦!
面向方面編程(AOP)對于PHP來說是一個新的概念。現(xiàn)在PHP對于 AOP 并沒有官方支持,但有很多擴展和庫實現(xiàn)了這個特性。本課中,我們將使用 Go! PHP library 來學(xué)習(xí) PHP 如何進(jìn)行 AOP 開發(fā),或者在需要的時候,可以回來看一眼。
Aspect-Oriented programming is like a new gadget for geeks.
面向方面編程的思想在二十世紀(jì)90年代中期,于施樂帕洛阿爾托研究中心(PARC)成型。同很多有趣的新技術(shù)一樣,由于缺少明確的定義,起初 AOP 備受爭議。因此相關(guān)小組決定將未完成的想法公之于眾,以便接受廣大社區(qū)的反饋。關(guān)鍵問題在于“關(guān)注點分離(Separation of Concerns)”的概念。AOP 是一種可以分離關(guān)注的可行系方案。
AOP 于90年代末趨于成熟,標(biāo)識為施樂 AspectJ 的發(fā)布,IBM 緊隨其后,于2001年發(fā)布了 Hyper/J。現(xiàn)在,AOP是一種對于常用編程語言來說都是一種成熟的技術(shù)。
AOP 的核心就是“方面”,但在我們定義「方面『aspect』」之前,我們需要先討論兩個術(shù)語;「切點『 point-cut』 」和「通知『advise』」。切點代表我們代碼中的一個時間點,特指運行我們代碼的某個時間。在切點運行代碼被稱為通知,結(jié)合一個活多個切點及通知的即為方面。
通常,每個類都會有一個核心的行為或關(guān)注點,但有時,類可能存在次要的行為。例如,類可能會調(diào)用一個日志記錄器或是通知一個觀察員。因為類中的這些功能是次要的,其行為通常都是相同的。這種行為被稱為“交叉關(guān)注點”;使用 AOP 可以避免。
Chris Peters 已經(jīng)討論過在PHP中實現(xiàn) AOP 的Flow 框架。 Lithium 框架也提供了對AOP的實現(xiàn)。
另一個框架采用了不同的方法,創(chuàng)建了一個 C/C++ 編寫的PHP擴展,在PHP解釋器的層級上宣示著它的魔力。名為AOP PHP Extension,我會在后續(xù)文章中討論它。
但正如我之前所言,本文將檢閱Go! AOP-PHP 庫。
Go! 庫并未擴展;它完全由PHP編寫,并為PHP5.4或更高版本使用。作為一個純PHP庫,它部署簡易,即使是在不允許編譯安裝你自己的PHP擴展的受限及共享主機環(huán)境,也可以輕易安裝。
Composer 是安裝 PHP 包的首選方法。如果你沒有使用過 Composer,你可以在Go! GitHub repository下載。
首先,將下面幾行加入你的 composer.json 文件。
1 2 3 4 5 | { "require" : { "lisachenko/go-aop-php" : "*" } } |
之后,使用 Composer 安裝 go-aop-php。在終端中運行下面命令:
1 2 | $ cd /your/project/folder $ php composer.phar update lisachenko /go-aop-php |
Composer 將會在之后數(shù)秒中內(nèi)安裝引用的包以及需求。如果成功,你將看到類似下面的輸出:
1 2 3 4 5 6 7 8 9 10 11 12 13 | Loading composer repositories with package information Updating dependencies - Installing doctrine /common (2.3.0) Downloading: 100% - Installing andrewsville /php-token-reflection (1.3.1) Downloading: 100% - Installing lisachenko /go-aop-php (0.1.1) Downloading: 100% Writing lock file Generating autoload files |
在安裝完成后,你可以在你的代碼目錄中發(fā)現(xiàn)名為 vendor 的文件夾。Go! 庫及其需求就安裝在這。
1 2 3 4 5 6 7 8 9 10 11 | $ ls -l . /vendor total 20 drwxr-xr-x 3 csaba csaba 4096 Feb 2 12:16 andrewsville -rw-r--r-- 1 csaba csaba 182 Feb 2 12:18 autoload.php drwxr-xr-x 2 csaba csaba 4096 Feb 2 12:16 composer drwxr-xr-x 3 csaba csaba 4096 Feb 2 12:16 doctrine drwxr-xr-x 3 csaba csaba 4096 Feb 2 12:16 lisachenko $ ls -l . /vendor/lisachenko/ total 4 drwxr-xr-x 5 csaba csaba 4096 Feb 2 12:16 go-aop-php |
我們需要創(chuàng)建一個調(diào)用,介于路由/應(yīng)用程序的入口點。自動裝彈機的然后自動包括類。開始吧!引用作為一個切面內(nèi)核。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | use Go\Core\AspectKernel; use Go\Core\AspectContainer; class ApplicationAspectKernel extends AspectKernel { protected function configureAop(AspectContainer $container ) { } protected function getApplicationLoaderPath() { } } |
現(xiàn)在,AOP是一種在通用編程語言中相當(dāng)成熟的技術(shù)。
例如,我創(chuàng)建了一個目錄,調(diào)用應(yīng)用程序,然后添加一個類文件: ApplicationAspectKernel.php 。
我們開始切面擴展!AcpectKernel 類提供了基礎(chǔ)的方法用于完切面內(nèi)核的工作。有兩個方法,我們必須知道:configureAop()用于注冊頁面特征,和 getApplicationLoaderPath() 返回自動加載程序的全路徑。
現(xiàn)在,一個簡單的建立一個空的 autoload.php 文件在你的程序目錄。和改變 getApplicationLoaderPath() 方法。如下:
1 2 3 4 5 6 7 8 9 10 | // [...] class ApplicationAspectKernel extends AspectKernel { // [...] protected function getApplicationLoaderPath() { return __DIR__ . DIRECTORY_SEPARATOR . 'autoload.php' ; } } |
別擔(dān)心 autoload.php 就是這樣。我們將會填寫被省略的片段。
當(dāng)我們第一次安裝 Go語言!和達(dá)到這一點我的過程中,我覺得需要運行一些代碼。所以開始構(gòu)建一個小應(yīng)用程序。
我們的「方面」為一個簡單的日志記錄器,但在繼續(xù)我們應(yīng)用的主要部分之前,有些代碼需要看一下。
我們的小應(yīng)用是一個電子經(jīng)紀(jì)人,能夠購買和***。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Broker { private $name ; private $id ; function __construct( $name , $id ) { $this ->name = $name ; $this ->id = $id ; } function buy( $symbol , $volume , $price ) { return $volume * $price ; } function sell( $symbol , $volume , $price ) { return $volume * $price ; } } |
這些代碼非常簡單,Broker 類擁有兩個私有字段,儲存經(jīng)紀(jì)人的名稱和 ID。
這個類同時提供了兩個方法,buy() 和 sell(),分別用于收購和***。每個方法接受三個參數(shù):股票標(biāo)識、股票數(shù)量、每股價格。sell() 方法***,并計算總收益。相應(yīng)的,buy()方法購買股票并計算總支出。
通過PHPUnit 測試程序,我們可以很容易的考驗我們經(jīng)紀(jì)人。在應(yīng)用目錄內(nèi)創(chuàng)建一個子目錄,名為 Test,并在其中添加 BrokerTest.php 文件。并添加下面的代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | require_once '../Broker.php' ; class BrokerTest extends PHPUnit_Framework_TestCase { function testBrokerCanBuyShares() { $broker = new Broker( 'John' , '1' ); $this ->assertEquals(500, $broker ->buy( 'GOOGL' , 100, 5)); } function testBrokerCanSellShares() { $broker = new Broker( 'John' , '1' ); $this ->assertEquals(500, $broker ->sell( 'YAHOO' , 50, 10)); } } |
這個檢驗程序檢查經(jīng)紀(jì)人方法的返回值。我們可以運行這個檢查程序檢驗我們的代碼,至少是不是語法正確。
讓我們創(chuàng)建一個自動加載器,在應(yīng)用需要的時候加載類。這是一個簡單的加載器,基于PSR-0 autoloader.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | ini_set ( 'display_errors' , true); spl_autoload_register( function ( $originalClassName ) { $className = ltrim( $originalClassName , '\\' ); $fileName = '' ; $namespace = '' ; if ( $lastNsPos = strripos ( $className , '\\' )) { $namespace = substr ( $className , 0, $lastNsPos ); $className = substr ( $className , $lastNsPos + 1); $fileName = str_replace ( '\\' , DIRECTORY_SEPARATOR, $namespace ) . DIRECTORY_SEPARATOR; } $fileName .= str_replace ( '_' , DIRECTORY_SEPARATOR, $className ) . '.php' ; $resolvedFileName = stream_resolve_include_path( $fileName ); if ( $resolvedFileName ) { require_once $resolvedFileName ; } return (bool) $resolvedFileName ; }); |
這就是我們 autoload.php 文件中的全部內(nèi)容?,F(xiàn)在,變更 BrokerTest.php, 改引用Broker.php 為引用自動加載器 。
1 2 3 4 5 | require_once '../autoload.php' ; class BrokerTest extends PHPUnit_Framework_TestCase { // [...] } |
運行 BrokerTest,驗證代碼運行情況。
我們最后的一件事是配置Go!.為此,我們需要連接所有的組件讓們能和諧工作。首先,創(chuàng)建一個php文件AspectKernelLoader.php,其代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | include __DIR__ . '/../vendor/lisachenko/go-aop-php/src/Go/Core/AspectKernel.php' ; include 'ApplicationAspectKernel.php' ; ApplicationAspectKernel::getInstance()->init( array ( 'autoload' => array ( 'Go' => realpath (__DIR__ . '/../vendor/lisachenko/go-aop-php/src/' ), 'TokenReflection' => realpath (__DIR__ . '/../vendor/andrewsville/php-token-reflection/' ), 'Doctrine\\Common' => realpath (__DIR__ . '/../vendor/doctrine/common/lib/' ) ), 'appDir' => __DIR__ . '/../Application' , 'cacheDir' => null, 'includePaths' => array (), 'debug' => true )); |
我們需要連接所有的組件讓們能和諧工作!
這個文件位于前端控制器和自動加載器之間。他使用AOP框架初始化并在需要時調(diào)用autoload.php
第一行,我明確地載入AspectKernel.php和ApplicationAspectKernel.php,因為,要記住,在這個點我們還沒有自動加載器。
接下來的代碼段,我們調(diào)用ApplicationAspectKernel對象init()方法,并且給他傳遞了一個數(shù)列參數(shù):
autoload 定義了初始化AOP類庫的路徑。根據(jù)你實際的目錄機構(gòu)調(diào)整為相應(yīng)的值。
appDir 引用了應(yīng)用的目錄
cacheDir 指出了緩存目錄(本例中中我們忽略緩存)。
includePaths 對aspects的一個過濾器。我想看到所有特定的目錄,所以設(shè)置了一個空數(shù)組,以便看到所有的值。
debug 提供了額外的調(diào)試信息,這對開發(fā)非常有用,但是對已經(jīng)要部屬的應(yīng)用設(shè)置為false。
為了最后實現(xiàn)各個不同部分的連接,找出你工程中autoload.php自動加載所有的引用并且用AspectKernelLoader.php替換他們。在我們簡單的例子中,僅僅test文件需要修改:
1 2 3 4 5 6 7 | require_once '../AspectKernelLoader.php' ; class BrokerTest extends PHPUnit_Framework_TestCase { // [...] } |
對大一點的工程,你會發(fā)現(xiàn)使用bootstrap.php作為單元測試但是非常有用;用require_once()做為autoload.php,或者我們的AspectKernelLoader.php應(yīng)該在那載入。
創(chuàng)建BrokerAspect.php文件,代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | use Go\Aop\Aspect; use Go\Aop\Intercept\FieldAccess; use Go\Aop\Intercept\MethodInvocation; use Go\Lang\Annotation\After; use Go\Lang\Annotation\Before; use Go\Lang\Annotation\Around; use Go\Lang\Annotation\Pointcut; use Go\Lang\Annotation\DeclareParents; class BrokerAspect implements Aspect { /** * @param MethodInvocation $invocation Invocation * @Before("execution(public Broker->*(*))") // This is our PointCut */ public function beforeMethodExecution(MethodInvocation $invocation ) { echo "Entering method " . $invocation ->getMethod()->getName() . "()\n" ; } } |
我們在程序開始指定一些有對AOP框架有用的語句。接著,我們創(chuàng)建了自己的方面類叫BrokerAspect,用它實現(xiàn)Aspect。接著,我們指定了我們aspect的匹配邏輯。
1 | * @Before( "execution(public Broker->*(*))" ) |
@Before 給出合適應(yīng)用建議. 可能的參數(shù)有@Before,@After,@Around和@After線程.
"execution(public Broker->*(*))" 給執(zhí)行一個類所有的公共方法指出了匹配規(guī)則,可以用任意數(shù)量的參數(shù)調(diào)用Broker,語法是:
1 | [operation - execution/access]([method/attribute type - public / protected ] [ class ]->[method/attribute]([params]) |
請注意匹配機制不可否認(rèn)有點笨拙。你在規(guī)則的每一部分僅可以使用一個星號‘*‘。例如public Broker->匹配一個叫做Broker的類;public Bro*->匹配以Bro開頭的任何類;public *ker->匹配任何ker結(jié)尾的類。
public *rok*->將匹配不到任何東西;你不能在同一個匹配中使用超過一個的星號。
緊接著匹配程序的函數(shù)會在有時間發(fā)生時調(diào)用。在本例中的方法將會在每一個Broker公共方法調(diào)用之前執(zhí)行。其參數(shù)$invocation(類型為MethodInvocation)子自動傳遞到我們的方法的。這個對象提供了多種方式獲取調(diào)用方法的信息。在第一個例子中,我們使用他獲取了方法的名字,并且輸出。
僅僅定義一個切面是不夠的;我們需要把它注冊到AOP架構(gòu)里。否則,它不會生效。編輯ApplicationAspectKernel.php同時在容器上的configureAop()方法里調(diào)用registerAspect():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | use Go\Core\AspectKernel; use Go\Core\AspectContainer; class ApplicationAspectKernel extends AspectKernel { protected function getApplicationLoaderPath() { return __DIR__ . DIRECTORY_SEPARATOR . 'autoload.php' ; } protected function configureAop(AspectContainer $container ) { $container ->registerAspect( new BrokerAspect()); } } |
運行測試和檢查輸出。你會看到類似下面的東西:
1 2 3 4 5 6 7 8 9 | PHPUnit 3.6.11 by Sebastian Bergmann. .Entering method __construct() Entering method buy() .Entering method __construct() Entering method sell() Time: 0 seconds, Memory: 5.50Mb OK (2 tests, 2 assertions) |
就這樣我們已設(shè)法讓代碼無論什么時候發(fā)生在broker上時都會執(zhí)行。
讓我們加入另外的方法到BrokerAspect。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // [...] class BrokerAspect implements Aspect { // [...] /** * @param MethodInvocation $invocation Invocation * @After("execution(public Broker->*(*))") */ public function afterMethodExecution(MethodInvocation $invocation ) { echo "Finished executing method " . $invocation ->getMethod()->getName() . "()\n" ; echo "with parameters: " . implode( ', ' , $invocation ->getArguments()) . ".\n\n" ; } } |
這個方法在一個公共方法執(zhí)行后運行(注意@After匹配器)。染污我們加入另外一行來輸出用來調(diào)用方法的參數(shù)。我們的測試現(xiàn)在輸出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | PHPUnit 3.6.11 by Sebastian Bergmann. .Entering method __construct() Finished executing method __construct() with parameters: John, 1. Entering method buy() Finished executing method buy() with parameters: GOOGL, 100, 5. .Entering method __construct() Finished executing method __construct() with parameters: John, 1. Entering method sell() Finished executing method sell() with parameters: YAHOO, 50, 10. Time: 0 seconds, Memory: 5.50Mb OK (2 tests, 2 assertions) |
目前為止,我們學(xué)習(xí)了在一個方法執(zhí)行的之前和之后,怎樣運行額外的代碼。當(dāng)這個漂亮的實現(xiàn)后,如果我們無法看到方法返回了什么的話,它還不是非常有用。我們給aspect增加另一個方法,修改現(xiàn)有的代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | //[...] class BrokerAspect implements Aspect { /** * @param MethodInvocation $invocation Invocation * @Before("execution(public Broker->*(*))") */ public function beforeMethodExecution(MethodInvocation $invocation ) { echo "Entering method " . $invocation ->getMethod()->getName() . "()\n" ; echo "with parameters: " . implode( ', ' , $invocation ->getArguments()) . ".\n" ; } /** * @param MethodInvocation $invocation Invocation * @After("execution(public Broker->*(*))") */ public function afterMethodExecution(MethodInvocation $invocation ) { echo "Finished executing method " . $invocation ->getMethod()->getName() . "()\n\n" ; } /** * @param MethodInvocation $invocation Invocation * @Around("execution(public Broker->*(*))") */ public function aroundMethodExecution(MethodInvocation $invocation ) { $returned = $invocation ->proceed(); echo "method returned: " . $returned . "\n" ; return $returned ; } } |
僅僅定義一個aspect是不夠的;我們需要將它注冊到AOP基礎(chǔ)設(shè)施。
這個新的代碼把參數(shù)信息移動到@Before方法。我們也增加了另一個特殊的@Around匹配器方法。這很整潔,因為原始的匹配方法調(diào)用被包裹于aroundMethodExecution()函數(shù)之內(nèi),有效的限制了原始的調(diào)用。在advise里,我們要調(diào)用$invocation->proceed(),以便執(zhí)行原始的調(diào)用。如果你不這么做,原始的調(diào)用將不會發(fā)生。
這種包裝也允許我們操作返回值。advise返回的就是原始調(diào)用返回的。在我們的案例中,我們沒有修改任何東西,輸出應(yīng)該看起來像這樣:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | PHPUnit 3.6.11 by Sebastian Bergmann. .Entering method __construct() with parameters: John, 1. method returned: Finished executing method __construct() Entering method buy() with parameters: GOOGL, 100, 5. method returned: 500 Finished executing method buy() .Entering method __construct() with parameters: John, 1. method returned: Finished executing method __construct() Entering method sell() with parameters: YAHOO, 50, 10. method returned: 500 Finished executing method sell() Time: 0 seconds, Memory: 5.75Mb OK (2 tests, 2 assertions) |
我們增加一點變化,賦以一個具體的broker一個discount。返回到測試類,寫如下的測試:
1 2 3 4 5 6 7 8 9 10 11 12 | require_once '../AspectKernelLoader.php' ; class BrokerTest extends PHPUnit_Framework_TestCase { // [...] function testBrokerWithId2WillHaveADiscountOnBuyingShares() { $broker = new Broker( 'Finch' , '2' ); $this ->assertEquals(80, $broker ->buy( 'MS' , 10, 10)); } } |
這會失?。?/p>
1 2 3 4 5 6 7 8 9 10 11 12 | Time: 0 seconds, Memory: 6.00Mb There was 1 failure: 1) BrokerTest::testBrokerWithId2WillHaveADiscountOnBuyingShares Failed asserting that 100 matches expected 80. /home/csaba/Personal/Programming/NetTuts/Aspect Oriented Programming in PHP /Source/Application/Test/BrokerTest .php:19 /usr/bin/phpunit :46 FAILURES! Tests: 3, Assertions: 3, Failures: 1. |
下一步,我們需要修改broker以便提供它的ID。只要像下面所示實現(xiàn)agetId()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class Broker { private $name ; private $id ; function __construct( $name , $id ) { $this ->name = $name ; $this ->id = $id ; } function getId() { return $this ->id; } // [...] } |
現(xiàn)在,修改aspect以調(diào)整具有ID值為2的broker的購買價格。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // [...] class BrokerAspect implements Aspect { // [...] /** * @param MethodInvocation $invocation Invocation * @Around("execution(public Broker->buy(*))") */ public function aroundMethodExecution(MethodInvocation $invocation ) { $returned = $invocation ->proceed(); $broker = $invocation ->getThis(); if ( $broker ->getId() == 2) return $returned * 0.80; return $returned ; } } |
無需增加新的方法,只要修改aroundMethodExecution()函數(shù)?,F(xiàn)在它正好匹配方法,稱作‘buy‘,并觸發(fā)了$invocation->getThis()。這有效的返回了原始的Broker對象,以便我們可以執(zhí)行它的代碼。于是我們做到了!我們向broker要它的ID,如果ID等于2的話就提供一個折扣。測試現(xiàn)在通過了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | PHPUnit 3.6.11 by Sebastian Bergmann. .Entering method __construct() with parameters: John, 1. Finished executing method __construct() Entering method buy() with parameters: GOOGL, 100, 5. Entering method getId() with parameters: . Finished executing method getId() Finished executing method buy() .Entering method __construct() with parameters: John, 1. Finished executing method __construct() Entering method sell() with parameters: YAHOO, 50, 10. Finished executing method sell() .Entering method __construct() with parameters: Finch, 2. Finished executing method __construct() Entering method buy() with parameters: MS, 10, 10. Entering method getId() with parameters: . Finished executing method getId() Finished executing method buy() Time: 0 seconds, Memory: 5.75Mb OK (3 tests, 3 assertions) |
我們現(xiàn)在可以在一個方法的開始和執(zhí)行之后、繞過時,執(zhí)行附加程序。但當(dāng)方法拋出異常時又如何呢?
添加一個測試方法來購買大量微軟的股票:
1 2 3 4 | function testBuyTooMuch() { $broker = new Broker( 'Finch' , '2' ); $broker ->buy( 'MS' , 10000, 8); } |
現(xiàn)在,創(chuàng)建一個異常類。我們需要它是因為內(nèi)建的異常類不能被 Go!AOP 或 PHPUnit 捕捉.
1 2 3 4 5 6 7 | class SpentTooMuchException extends Exception { public function __construct( $message ) { parent::__construct( $message ); } } |
修改經(jīng)紀(jì)人類,對大值拋出異常:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Broker { // [...] function buy( $symbol , $volume , $price ) { $value = $volume * $price ; if ( $value > 1000) throw new SpentTooMuchException(sprintf( 'You are not allowed to spend that much (%s)' , $value )); return $value ; } // [...] } |
運行測試,確保它們產(chǎn)生失敗消息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Time: 0 seconds, Memory: 6.00Mb There was 1 error: 1) BrokerTest::testBuyTooMuch Exception: You are not allowed to spend that much (80000) /home/csaba/Personal/Programming/NetTuts/Aspect Oriented Programming in PHP /Source/Application/Broker .php:20 // [...] /home/csaba/Personal/Programming/NetTuts/Aspect Oriented Programming in PHP /Source/Application/Broker .php:47 /home/csaba/Personal/Programming/NetTuts/Aspect Oriented Programming in PHP /Source/Application/Test/BrokerTest .php:24 /usr/bin/phpunit :46 FAILURES! Tests: 4, Assertions: 3, Errors: 1. |
現(xiàn)在,期待異常(在測試中),確保它們通過:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class BrokerTest extends PHPUnit_Framework_TestCase { // [...] /** * @expectedException SpentTooMuchException */ function testBuyTooMuch() { $broker = new Broker( 'Finch' , '2' ); $broker ->buy( 'MS' , 10000, 8); } } |
在我們的“方面”中建立一個新方法來匹配@AfterThrowing,別忘記指定 Use Go\Lang\Annotation\AfterThrowing;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // [...] Use Go\Lang\Annotation\AfterThrowing; class BrokerAspect implements Aspect { // [...] /** * @param MethodInvocation $invocation Invocation * @AfterThrowing("execution(public Broker->buy(*))") */ public function afterExceptionMethodExecution(MethodInvocation $invocation ) { echo 'An exception has happened' ; } } |
@AfterThrowing匹配器抑制拋出的異常,并允許你去采取自己的行動。在我們的代碼中,我們簡單的顯示一個信息,但你可以做任何你的應(yīng)用程序需要的事情。
這就是為什么我建議你小心使用“方面”。
面向方面編程就像給怪人們的新玩意兒;您可以立即看到其巨大的潛力。方面允許我們在我們的系統(tǒng)的不同部分引入額外的代碼,而無需修改原始代碼。當(dāng)你需要實現(xiàn)一些通過緊耦合引用和方法調(diào)用會污染你的方法和類的模塊時,這會非常有用。
然而,這種靈活性,是有代價的:陰暗朦朧。有沒有辦法告訴如果一方面表的方法只是在尋找方法或類。例如,在我們的Broker類中執(zhí)行方法時沒有跡象表明發(fā)生任何事情。這就是為什么我建議你小心使用“方面”的原因。
我們使用“方面”來給一個特定的經(jīng)紀(jì)人提供折扣是誤用的一個例子。不要在一個真實的項目中這樣做。經(jīng)紀(jì)人的折扣與經(jīng)紀(jì)人相關(guān);所以,在Broker類中保持這個邏輯。“方面”應(yīng)該只執(zhí)行不直接關(guān)系到對象主要行為的任務(wù)。
樂在其中吧!
英文原文:Aspect-Oriented Programming in PHP with Go!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。