正如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;它非常强大,可以为您的代码添加一些非常漂亮、简单和灵活的功能。
