正如DaveyShafik最近指出的那样,他和我一直在为ZendFramework开发一些Web服务。在这样做的过程中,我已经非常熟悉PHP5的反射API,并且开始爱上它。
当我第一次在PHP5之前的更新日志中读到反射API时,我最初的反应是,“谁在乎?”我根本看不出它对语言的有用补充。最近做了一些需要了解他们正在使用的类的项目,我现在了解何时以及如何使用它。当您需要使用在您编写代码时可能未定义的类时,它会大放异彩——基本上,任何分派给其他类的代码。
那么,您可以使用ReflectionAPI执行哪些操作?以下是我所做的一些事情的列表:
- 确定类中是否存在方法
- 从类、方法或函数的phpdoc文档块中检索并获取元信息(我在开发
Zend_Server_Reflection
时大量使用了它) - 确定方法是静态的、公共的、私有的还是受保护的
- 检索函数/方法参数并确定位置、参数是否可选以及默认值可能是什么be,以及参数的名称(即用于标识它的变量名称)
- 调用具有可变数量参数的函数或方法。这可以用来代替
call_user_func
/call_user_func_array()
,并且$method->invoke()
也允许静态方法调用(我不得不向php.net提交错误,因为invokeArgs()
不允许静态调用。 - 用可变数量的参数实例化一个对象实例使用
newInstanceArgs()
到构造函数。这是我寻找了一段时间的东西;以前,唯一的解决方案是使用eval()
(糟糕!)或者让您的构造函数都接受参数的关联数组。这是一个更好、更灵活的解决方案。
各种Reflection
类都可以扩展。然而,由于它们都非常相关,我发现代理它们并根据需要覆盖方法更容易。例如,在Zend_Server_Reflection
树中,我需要类反射来返回一个Zend_Server_Reflection_Methods
数组,这对文档块做了很多自省(获取方法原型,提示参数变量类型和描述等)。所以,我定义了这样的东西:
class Reflection_Class { public function __construct(ReflectionClass $r) { $this->_reflection = $r; foreach ($r->getMethods() as $method) { $this->_methods[] = new Reflection_Method($method); } } public function __call($method, $args) { if (method_exists($r, $method)) { return $r->{$method}($args); } } public function getMethods() { return $this->_methods; } }
显然,这是速记,但您明白了。
反射API对于各种服务器组件非常有用,因为我们可以有一组中央类来执行函数和类内省,然后可以使用这些类来定义服务器可以使用的调度回调。此外,我实现了一个__wakeup()
方法,它基本上恢复了整个反射架构,允许我们在调用之间序列化服务器定义——这大大减少了后续调用需要发生的处理量。
我们还在MVC组件中使用它,特别是在Dispatchers中。同样,这允许我们(a)确定是否存在用于分派的方法,以及(b)使用我们可能需要的任何参数调用它。它还允许我们使用可变数量的参数轻松实例化动作控制器对象。
如果您正在为插件架构编写任何类型的代码,我强烈建议您了解ReflectionAPI;它非常强大,可以为您的代码添加一些非常漂亮、简单和灵活的功能。