使用java中的动态代理可以完成很多事情,比如将业务实例进行托管,实现AOP等,但是Php中没有实现这样的东西,昨天突然想到其实可以通过eval来模拟一个动态代理机制。php比java不同的是,php是不需要编译的,因此只要我们能够动态生成一段代码,然后用eval来执行就可以达到效果。代码如下:
/** * 代理实现类 */ interface IInvocationHandler { function invoke($method, array $arr_args); } /** * 代理类 */ final class Proxy { const CLASS_TEMPLATE = ' final class %s implements %s { private $handler; public function __construct(IInvocationHandler $handler) { $this->handler = $handler; } %s } '; const FUNCTION_TEMPLATE = ' public function %s(%s) { $arr_args = func_get_args (); $arr_method = explode("::", __METHOD__); $this->handler->invoke ( $arr_method[1], $arr_args ); } '; public static function newProxyInstance(array $arr_interface, IInvocationHandler $handler) { $className = self::getClassName ( $arr_interface ); if (class_exists ( $className )) { return new $className ( $handler ); } self::checkInterfaceExists ( $arr_interface ); self::generateClass ( $arr_interface ); return new $className ( $handler ); } protected static function generateClass(array $arr_interface) { $className = self::getClassName ( $arr_interface ); $interfaceList = implode ( ',', $arr_interface ); $functionList = ''; foreach ( $arr_interface as $interface ) { $class = new ReflectionClass ( $interface ); $arr_method = $class->getMethods (); foreach ( $arr_method as $method ) { $methodName = $method->getName (); $arr_parameter = $method->getParameters (); $parameterArray = array (); foreach ( $arr_parameter as $parameter ) { $parameterName = $parameter->getName (); $parameterArray [] = '$' . $parameterName; } $parameterList = implode ( ',', $parameterArray ); } $functionList .= sprintf ( self::FUNCTION_TEMPLATE, $methodName, $parameterList ); } $code = sprintf ( self::CLASS_TEMPLATE, $className, $interfaceList, $functionList ); eval ( $code ); } protected static function checkInterfaceExists(array $arr_interface) { foreach ( $arr_interface as $interface ) { if (! interface_exists ( $interface )) { throw new Exception ( "interface $interface do not loaded" ); } } } protected static function getClassName(array $arr_interface) { return 'Class_' . implode ( '_', $arr_interface ) . '_Implementation'; } } class Test implements ITest1, ITest2 { /** * @see ITest1::test1() * * @param unknown_type $arg1 * @param unknown_type $arg2 */ function test1($arg1, $arg2) { echo "Test::test1($arg1, $arg2)/n"; return "ok"; } /** * @see ITest2::test2() * */ function test2() { echo "Test::test2()/n"; return "failed"; } } class Target implements IInvocationHandler { private $target; public function __construct() { $this->target = new Test ( ); } /** * @see IInvocationHandler::invoke() * * @param ReflectionMethod $method * @param array $arr_args */ function invoke($methodName, array $arr_args) { $method = new ReflectionMethod ( 'Test', $methodName ); echo "before call method $methodName/n"; $ret = $method->invokeArgs ( $this->target, $arr_args ); echo "after call method $methodName return $ret/n"; return $ret; } } interface ITest1 { function test1($arg1, $arg2); } interface ITest2 { function test2(); } $proxy = Proxy::newProxyInstance ( array ('ITest1', 'ITest2' ), new Target ( ) ); $proxy->test2 (); $proxy = Proxy::newProxyInstance ( array ('ITest1' ), new Target ( ) ); $proxy->test1 ( 'a', 1 );
其实从这个例子可以推出来,我们甚至可以模拟asm的相关功能,而且实现要比java简单很多。看来以前没有挖掘php的功能呀,这个语言被我低估了。