AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面),可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。
AOP-PHP是一个PECL扩展,您可以在PHP中使用面向方面的编程,无需编译或进行其他任何中间步骤。
AOP扩展的设计是最简单的方法,你可以认为PHP中的aop实现。
AOP旨在让横切关注点的分离(缓存,日志,安全,交易,……)
网址:http://aop-php.github.io/
有两种安装模式:
第一种方法:
sudo pecl install aop-beta
第二种方法:
<span>#</span><span>Clone the repository on your computer</span> git clone https://github.com/AOP-PHP/<span>AOP cd AOP </span><span>#</span><span>prepare the package, you will need to have development tools for php</span> <span> phpize </span><span>#</span><span>compile the package</span>
<span> make </span><span>#</span><span>before the installation, check that it works properly</span> <span> make test </span><span>#</span><span>install</span> make install
笔者在第二种方法安装中出现了错误(如果没有错误这里可以飘过):
Can<span>'</span><span>t locate Autom4te/C4che.pm in @INC (@INC contains: /usr/local/share/autoconf...</span>
解决办法是重新安装autoconf:
<span>#</span><span>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</span>
编译安装成功后,需要在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代码:
<?<span>php </span><span>function</span><span> testFunc1(){ </span><span>echo</span> 'aop_add_before <br/>'<span>; }</span>
testaop.php代码:
<?<span>php </span><span>$testpoint1</span> = <span>function</span><span> () { </span><span>echo</span> "这是前切点测试函数:"<span>; }; aop_add_before(</span>'testFunc1()', <span>$testpoint1</span>);
test.php代码:
<?<span>php </span><span>require</span> 'testaop.php'<span>; </span><span>require</span> 'testclass.php'<span>; </span><span>require</span> 'testfunction.php'<span>; </span><span>header</span>("Content-Type:text/html;charset=utf-8"<span>); testFunc1();</span>
不出意外,执行test.php我们将会看到:
这是前切点测试函数:aop_add_before
testclass.php代码:
<?<span>php </span><span>class</span><span> testClass1 { </span><span>public</span> <span>function</span><span> testBeforAdd1() { </span><span>echo</span> <span>get_class</span>(<span>$this</span><span>); } }</span>
testaop.php代码:
<?<span>php </span><span>$testpoint1</span> = <span>function</span><span> () { </span><span>echo</span> "这是前切点测试函数:"<span>; }; </span><span>$testpoint2</span> = <span>function</span><span> () { </span><span>echo</span> "这是前切点测试类方法:"<span>; }; aop_add_before(</span>'testFunc1()', <span>$testpoint1</span><span>); aop_add_before(</span>'testClass1->testBeforAdd1()', <span>$testpoint2</span>);
test.php代码:
<?<span>php </span><span>require</span> 'testaop.php'<span>; </span><span>require</span> 'testclass.php'<span>; </span><span>require</span> 'testfunction.php'<span>; </span><span>header</span>("Content-Type:text/html;charset=utf-8"<span>); testFunc1(); </span><span>$testClass1</span> = <span>new</span><span> testClass1(); </span><span>echo</span> <span>$testClass1</span>->testBeforAdd1();
执行test.php
这是前切点测试函数:aop_add_before 这是前切点测试类方法:testClass1
testclass.php源码
<?<span style="color: #000000;">php </span><span style="color: #008000;">//</span><span style="color: #008000;">测试前通知类</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> testClass1 { </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> function testBeforAdd1() { echo get_class($</span><span style="color: #0000ff;">this</span>) .<span style="color: #800000;">'</span><span style="color: #800000;"><br /></span><span style="color: #800000;">'</span><span style="color: #000000;">; } } </span><span style="color: #008000;">//</span><span style="color: #008000;">测试前通知类属性</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> testClass2 { </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> $name; </span><span style="color: #0000ff;">public</span> $publicProperty1 = <span style="color: #800000;">'</span><span style="color: #800000;">test</span><span style="color: #800000;">'</span><span style="color: #000000;">; </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> function __construct ($name) { $</span><span style="color: #0000ff;">this</span>->name =<span style="color: #000000;"> $name; } </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> function getName () { </span><span style="color: #0000ff;">return</span> $<span style="color: #0000ff;">this</span>-><span style="color: #000000;">name; } </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> function test () { $</span><span style="color: #0000ff;">this</span>->publicProperty1 = <span style="color: #800000;">'</span><span style="color: #800000;">test</span><span style="color: #800000;">'</span><span style="color: #000000;">; </span><span style="color: #0000ff;">return</span> $<span style="color: #0000ff;">this</span>-><span style="color: #000000;">publicProperty1; } }</span>
testaop.php源码
<?<span style="color: #000000;">php </span><span style="color: #800080;">$testpoint11</span> = <span style="color: #0000ff;">function</span><span style="color: #000000;"> () { </span><span style="color: #0000ff;">echo</span> "这是前切点测试函数:"<span style="color: #000000;">; }; </span><span style="color: #800080;">$testpoint12</span> = <span style="color: #0000ff;">function</span><span style="color: #000000;"> () { </span><span style="color: #0000ff;">echo</span> "这是前切点测试类方法:"<span style="color: #000000;">; }; aop_add_before(</span>'testFunc1()', <span style="color: #800080;">$testpoint11</span><span style="color: #000000;">); aop_add_before(</span>'testClass1->testBeforAdd1()', <span style="color: #800080;">$testpoint12</span><span style="color: #000000;">); </span><span style="color: #008000;">//</span><span style="color: #008000;">------测试类属性</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> changeProperty { </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> shoot ( <span style="color: #800080;">$who</span>, <span style="color: #800080;">$what</span><span style="color: #000000;">) { </span><span style="color: #0000ff;">if</span>(<span style="color: #800080;">$what</span> == 'test'<span style="color: #000000;">){ </span><span style="color: #800080;">$what</span> = '测试前通知类属性截取 <br/>'<span style="color: #000000;">; } </span><span style="color: #0000ff;">echo</span> "<span style="color: #800080;">$who</span> 想要 <span style="color: #800080;">$what</span> "<span style="color: #000000;">; } } </span><span style="color: #800080;">$testclass1</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> changeProperty(); </span><span style="color: #800080;">$testpoint2</span> = <span style="color: #0000ff;">function</span> ( AopJoinPoint <span style="color: #800080;">$aop_tjp</span> ) <span style="color: #0000ff;">use</span>( <span style="color: #800080;">$testclass1</span><span style="color: #000000;"> ) { </span><span style="color: #0000ff;">if</span> ( <span style="color: #800080;">$aop_tjp</span>->getKindOfAdvice() ===<span style="color: #000000;"> AOP_KIND_BEFORE_READ_PROPERTY ) { </span><span style="color: #0000ff;">return</span>; <span style="color: #008000;">//</span><span style="color: #008000;"> 如果属性不能读则返回</span> <span style="color: #000000;"> } </span><span style="color: #0000ff;">elseif</span> ( <span style="color: #800080;">$aop_tjp</span>->getKindOfAdvice() ===<span style="color: #000000;"> AOP_KIND_BEFORE_WRITE_PROPERTY ) { </span><span style="color: #800080;">$testclass1</span>->shoot(<span style="color: #800080;">$aop_tjp</span>->getObject()->getName(),<span style="color: #800080;">$aop_tjp</span>-><span style="color: #000000;">getAssignedValue()); } }; </span><span style="color: #008000;">//</span><span style="color: #008000;">测试类属性</span> aop_add_before('testClass2->publicProperty1', <span style="color: #800080;">$testpoint2</span>);
test.php源码
<?<span style="color: #000000;">php </span><span style="color: #0000ff;">require</span> 'testaop.php'<span style="color: #000000;">; </span><span style="color: #0000ff;">require</span> 'testclass.php'<span style="color: #000000;">; </span><span style="color: #0000ff;">require</span> 'testfunction.php'<span style="color: #000000;">; </span><span style="color: #008080;">header</span>("Content-Type:text/html;charset=utf-8"<span style="color: #000000;">); </span><span style="color: #008000;">//</span><span style="color: #008000;">前通知</span> <span style="color: #000000;">testFunc1(); </span><span style="color: #800080;">$testClass1</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> testClass1(); </span><span style="color: #0000ff;">echo</span> <span style="color: #800080;">$testClass1</span>-><span style="color: #000000;">testBeforAdd1(); </span><span style="color: #800080;">$runtest2</span> = <span style="color: #0000ff;">new</span> testClass2('skyboy'<span style="color: #000000;">); </span><span style="color: #800080;">$runtest2</span>->test();
执行test.php
这是前切点测试函数:aop_add_before 这是前切点测试类方法:testClass1 skyboy 想要 测试前通知类属性截取
在代码中一些特殊点之后使用的通知,一般是调用一个方法或者函数。
testfunction.php源码:
<span style="color: #0000ff;">function</span><span style="color: #000000;"> testFunc2(){ </span><span style="color: #0000ff;">echo</span> '这是返回后通知测试:'<span style="color: #000000;">; }</span>
testaop.php源码:
<span style="color: #008000;">//</span><span style="color: #008000;">测试返回后通知</span> <span style="color: #800080;">$testpoint22</span> = <span style="color: #0000ff;">function</span><span style="color: #000000;"> () { </span><span style="color: #0000ff;">echo</span> "aop_add_after <br/>"<span style="color: #000000;">; }; aop_add_after(</span>'testFunc2()', <span style="color: #800080;">$testpoint22</span>);
test.php源码:
<span style="color: #008000;">//</span><span style="color: #008000;">后通知</span> testFunc2();
执行test.php
这是返回后通知测试:aop_add_after
类和类属性和前通知类似,为了节省篇幅,这里偷懒了。
testfunction.php源码:
<span style="color: #0000ff;">function</span> testFunc3(<span style="color: #800080;">$param1</span>,<span style="color: #800080;">$param2</span><span style="color: #000000;">){ </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$param1</span>. <span style="color: #800080;">$param2</span><span style="color: #000000;">; }</span>
testaop.php源码:
<span style="color: #008000;">//</span><span style="color: #008000;">测试周边通知</span> <span style="color: #0000ff;">function</span> testaround (AopJoinPoint <span style="color: #800080;">$object</span><span style="color: #000000;">) { </span><span style="color: #800080;">$args</span> = <span style="color: #800080;">$object</span>-><span style="color: #000000;">getArguments(); </span><span style="color: #0000ff;">if</span> (<span style="color: #800080;">$args</span>[0] !== <span style="color: #0000ff;">null</span><span style="color: #000000;">) { </span><span style="color: #800080;">$args</span>[0] = '我想测试'<span style="color: #000000;">; } </span><span style="color: #0000ff;">if</span> (<span style="color: #800080;">$args</span>[1] !== <span style="color: #0000ff;">null</span><span style="color: #000000;">) { </span><span style="color: #800080;">$args</span>[1] = '周边通知:'<span style="color: #000000;">; } </span><span style="color: #800080;">$object</span>->setArguments(<span style="color: #800080;">$args</span><span style="color: #000000;">); </span><span style="color: #800080;">$object</span>-><span style="color: #000000;">process(); </span><span style="color: #800080;">$returnValue</span> = <span style="color: #800080;">$object</span>-><span style="color: #000000;">getReturnedValue(); </span><span style="color: #800080;">$returnValue</span> .= 'aop_add_around<br/>'<span style="color: #000000;">; </span><span style="color: #800080;">$object</span>->setReturnedValue(<span style="color: #800080;">$returnValue</span><span style="color: #000000;">); } aop_add_around(</span>'testFunc3()', 'testaround');
test.php源码:
<span style="color: #008000;">//</span><span style="color: #008000;">周边通知</span> <span style="color: #0000ff;">echo</span> 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
源码如下:
<?<span style="color: #000000;">php </span><span style="color: #008080;">ini_set</span>("aop.enable", "1"<span style="color: #000000;">); </span><span style="color: #0000ff;">echo</span> "aop is enabled<br />"<span style="color: #000000;">; </span><span style="color: #0000ff;">function</span><span style="color: #000000;"> foo () { </span><span style="color: #0000ff;">echo</span> "I'm foo<br />"<span style="color: #000000;">; } </span><span style="color: #800080;">$adviceShowFoo</span> = <span style="color: #0000ff;">function</span><span style="color: #000000;"> () { </span><span style="color: #0000ff;">echo</span> "After foo<br />"<span style="color: #000000;">; }; aop_add_after(</span>'foo()', <span style="color: #800080;">$adviceShowFoo</span><span style="color: #000000;">); foo(); </span><span style="color: #008080;">ini_set</span>('aop.enable', '0'<span style="color: #000000;">); </span><span style="color: #0000ff;">echo</span> "aop is now disabled<br />"<span style="color: #000000;">; foo(); </span><span style="color: #0000ff;">echo</span> "But you can still register new aspects<br />"<span style="color: #000000;">; aop_add_after(</span>'f*()', <span style="color: #800080;">$adviceShowFoo</span><span style="color: #000000;">); foo(); </span><span style="color: #008080;">ini_set</span>('aop.enable', '1'<span style="color: #000000;">); </span><span style="color: #0000ff;">echo</span> "Aop is now enabled<br />"<span style="color: #000000;">; foo();</span>
运行结果:
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编译)