面向方面编程(AOP)对于PHP来讲是一个新的概念。如今PHP对于 AOP 并无官方支持,但有不少扩展和库实现了这个特性。本课中,咱们将使用 Go! PHP library 来学习 PHP 如何进行 AOP 开发,或者在须要的时候,能够回来看一眼。php
Aspect-Oriented programming is like a new gadget for geeks.html
面向方面编程的思想在二十世纪90年代中期,于施乐帕洛阿尔托研究中心(PARC)成型。同不少有趣的新技术同样,因为缺乏明确的定义,起初 AOP 备受争议。所以相关小组决定将未完成的想法公之于众,以便接受广大社区的反馈。关键问题在于“关注点分离(Separation of Concerns)”的概念。AOP 是一种能够分离关注的可行系方案。前端
AOP 于90年代末趋于成熟,标识为施乐 AspectJ 的发布,IBM 紧随其后,于2001年发布了 Hyper/J。如今,AOP是一种对于经常使用编程语言来讲都是一种成熟的技术。git
AOP 的核心就是“方面”,但在咱们定义「方面『aspect』」以前,咱们须要先讨论两个术语;「切点『 point-cut』 」和「通知『advise』」。切点表明咱们代码中的一个时间点,特指运行咱们代码的某个时间。在切点运行代码被称为通知,结合一个活多个切点及通知的即为方面。程序员
一般,每一个类都会有一个核心的行为或关注点,但有时,类可能存在次要的行为。例如,类可能会调用一个日志记录器或是通知一个观察员。由于类中的这些功能是次要的,其行为一般都是相同的。这种行为被称为“交叉关注点”;使用 AOP 能够避免。github
Chris Peters 已经讨论过在PHP中实现 AOP 的Flow 框架。 Lithium 框架也提供了对AOP的实现。编程
另外一个框架采用了不一样的方法,建立了一个 C/C++ 编写的PHP扩展,在PHP解释器的层级上宣示着它的魔力。名为AOP PHP Extension,我会在后续文章中讨论它。json
但正如我以前所言,本文将检阅Go! AOP-PHP 库。bootstrap
Go! 库并未扩展;它彻底由PHP编写,并为PHP5.4或更高版本使用。做为一个纯PHP库,它部署简易,即便是在不容许编译安装你本身的PHP扩展的受限及共享主机环境,也能够轻易安装。数组
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 将会在以后数秒中内安装引用的包以及需求。若是成功,你将看到相似下面的输出:
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
|
在安装完成后,你能够在你的代码目录中发现名为 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
|
咱们须要建立一个调用,介于路由/应用程序的入口点。自动装弹机的而后自动包括类。开始吧!引用做为一个切面内核。
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() {
}
}
|
如今,AOP是一种在通用编程语言中至关成熟的技术。
例如,我建立了一个目录,调用应用程序,而后添加一个类文件: ApplicationAspectKernel.php 。
咱们开始切面扩展!AcpectKernel 类提供了基础的方法用于完切面内核的工做。有两个方法,咱们必须知道:configureAop()用于注册页面特征,和 getApplicationLoaderPath() 返回自动加载程序的全路径。
如今,一个简单的创建一个空的 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'
;
}
}
|
别担忧 autoload.php 就是这样。咱们将会填写被省略的片断。
当咱们第一次安装 Go语言!和达到这一点个人过程当中,我以为须要运行一些代码。因此开始构建一个小应用程序。
咱们的「方面」为一个简单的日志记录器,但在继续咱们应用的主要部分以前,有些代码须要看一下。
咱们的小应用是一个电子经纪人,可以购买和***。
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 类拥有两个私有字段,储存经纪人的名称和 ID。
这个类同时提供了两个方法,buy() 和 sell(),分别用于收购和***。每一个方法接受三个参数:股票标识、股票数量、每股价格。sell() 方法***,并计算总收益。相应的,buy()方法购买股票并计算总支出。
经过PHPUnit 测试程序,咱们能够很容易的考验咱们经纪人。在应用目录内建立一个子目录,名为 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));
}
}
|
这个检验程序检查经纪人方法的返回值。咱们能够运行这个检查程序检验咱们的代码,至少是否是语法正确。
让咱们建立一个自动加载器,在应用须要的时候加载类。这是一个简单的加载器,基于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 文件中的所有内容。如今,变动 BrokerTest.php, 改引用Broker.php 为引用自动加载器 。
1
2
3
4
5
|
require_once
'../autoload.php'
;
class
BrokerTest
extends
PHPUnit_Framework_TestCase {
// [...]
}
|
运行 BrokerTest,验证代码运行状况。
咱们最后的一件事是配置Go!.为此,咱们须要链接全部的组件让们能和谐工做。首先,建立一个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框架初始化并在须要时调用autoload.php
第一行,我明确地载入AspectKernel.php和ApplicationAspectKernel.php,由于,要记住,在这个点咱们尚未自动加载器。
接下来的代码段,咱们调用ApplicationAspectKernel对象init()方法,而且给他传递了一个数列参数:
autoload 定义了初始化AOP类库的路径。根据你实际的目录机构调整为相应的值。
appDir 引用了应用的目录
cacheDir 指出了缓存目录(本例中中咱们忽略缓存)。
includePaths 对aspects的一个过滤器。我想看到全部特定的目录,因此设置了一个空数组,以便看到全部的值。
debug 提供了额外的调试信息,这对开发很是有用,可是对已经要部属的应用设置为false。
为了最后实现各个不一样部分的链接,找出你工程中autoload.php自动加载全部的引用而且用AspectKernelLoader.php替换他们。在咱们简单的例子中,仅仅test文件须要修改:
1
2
3
4
5
6
7
|
require_once
'../AspectKernelLoader.php'
;
class
BrokerTest
extends
PHPUnit_Framework_TestCase {
// [...]
}
|
对大一点的工程,你会发现使用bootstrap.php做为单元测试可是很是有用;用require_once()作为autoload.php,或者咱们的AspectKernelLoader.php应该在那载入。
建立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框架有用的语句。接着,咱们建立了本身的方面类叫BrokerAspect,用它实现Aspect。接着,咱们指定了咱们aspect的匹配逻辑。
1
|
* @Before(
"execution(public Broker->*(*))"
)
|
@Before 给出合适应用建议. 可能的参数有@Before,@After,@Around和@After线程.
"execution(public Broker->*(*))" 给执行一个类全部的公共方法指出了匹配规则,能够用任意数量的参数调用Broker,语法是:
1
|
[operation - execution/access]([method/attribute type -
public
/
protected
] [
class
]->[method/attribute]([params])
|
请注意匹配机制不能否认有点笨拙。你在规则的每一部分仅可使用一个星号‘*‘。例如public Broker->匹配一个叫作Broker的类;public Bro*->匹配以Bro开头的任何类;public *ker->匹配任何ker结尾的类。
public *rok*->将匹配不到任何东西;你不能在同一个匹配中使用超过一个的星号。
紧接着匹配程序的函数会在有时间发生时调用。在本例中的方法将会在每个Broker公共方法调用以前执行。其参数$invocation(类型为MethodInvocation)子自动传递到咱们的方法的。这个对象提供了多种方式获取调用方法的信息。在第一个例子中,咱们使用他获取了方法的名字,而且输出。
仅仅定义一个切面是不够的;咱们须要把它注册到AOP架构里。不然,它不会生效。编辑ApplicationAspectKernel.php同时在容器上的configureAop()方法里调用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)
|
就这样咱们已设法让代码不管何时发生在broker上时都会执行。
让咱们加入另外的方法到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"
;
}
}
|
这个方法在一个公共方法执行后运行(注意@After匹配器)。染污咱们加入另一行来输出用来调用方法的参数。咱们的测试如今输出:
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)
|
目前为止,咱们学习了在一个方法执行的以前和以后,怎样运行额外的代码。当这个漂亮的实现后,若是咱们没法看到方法返回了什么的话,它还不是很是有用。咱们给aspect增长另外一个方法,修改现有的代码:
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基础设施。
这个新的代码把参数信息移动到@Before方法。咱们也增长了另外一个特殊的@Around匹配器方法。这很整洁,由于原始的匹配方法调用被包裹于aroundMethodExecution()函数以内,有效的限制了原始的调用。在advise里,咱们要调用$invocation->proceed(),以便执行原始的调用。若是你不这么作,原始的调用将不会发生。
这种包装也容许咱们操做返回值。advise返回的就是原始调用返回的。在咱们的案例中,咱们没有修改任何东西,输出应该看起来像这样:
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));
}
}
|
这会失败:
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。只要像下面所示实现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;
}
// [...]
}
|
如今,修改aspect以调整具备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()函数。如今它正好匹配方法,称做‘buy‘,并触发了$invocation->getThis()。这有效的返回了原始的Broker对象,以便咱们能够执行它的代码。因而咱们作到了!咱们向broker要它的ID,若是ID等于2的话就提供一个折扣。测试如今经过了。
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)
|
咱们如今能够在一个方法的开始和执行以后、绕过期,执行附加程序。但当方法抛出异常时又如何呢?
添加一个测试方法来购买大量微软的股票:
1
2
3
4
|
function
testBuyTooMuch() {
$broker
=
new
Broker(
'Finch'
,
'2'
);
$broker
->buy(
'MS'
, 10000, 8);
}
|
如今,建立一个异常类。咱们须要它是由于内建的异常类不能被 Go!AOP 或 PHPUnit 捕捉.
1
2
3
4
5
6
7
|
class
SpentTooMuchException
extends
Exception {
public
function
__construct(
$message
) {
parent::__construct(
$message
);
}
}
|
修改经纪人类,对大值抛出异常:
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
;
}
// [...]
}
|
运行测试,确保它们产生失败消息:
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.
|
如今,期待异常(在测试中),确保它们经过:
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匹配器抑制抛出的异常,并容许你去采起本身的行动。在咱们的代码中,咱们简单的显示一个信息,但你能够作任何你的应用程序须要的事情。
这就是为何我建议你当心使用“方面”。
面向方面编程就像给怪人们的新玩意儿;您能够当即看到其巨大的潜力。方面容许咱们在咱们的系统的不一样部分引入额外的代码,而无需修改原始代码。当你须要实现一些经过紧耦合引用和方法调用会污染你的方法和类的模块时,这会很是有用。
然而,这种灵活性,是有代价的:阴暗朦胧。有没有办法告诉若是一方面表的方法只是在寻找方法或类。例如,在咱们的Broker类中执行方法时没有迹象代表发生任何事情。这就是为何我建议你当心使用“方面”的缘由。
咱们使用“方面”来给一个特定的经纪人提供折扣是误用的一个例子。不要在一个真实的项目中这样作。经纪人的折扣与经纪人相关;因此,在Broker类中保持这个逻辑。“方面”应该只执行不直接关系到对象主要行为的任务。
乐在其中吧!