在生产代码中使用反射会有什么不好的影响吗?

4
我有一个名为Validator的类,它创建了一个Validations类的实例,其中包含所有验证方法。当进行验证时,Validator中的__call被用来派发调用Validator->validate_methodValidations->method
例如,Validations中有一个名为length_of的方法。当运行以下代码时:
$v = new Validator();
$v->validate_length_of(...);

Validations类中,length_of验证被执行。

为了确保__call不会尝试分派到无效或非公共的Validation方法,我使用ReflectionMethod来检查指定的方法:

$method = new ReflectionMethod($this->validations, $validation_method);
if (!$method->isPublic())
{
  // error
}

我相信这是确定一个方法是否为公共方法的唯一方法,但我不确定在生产代码中使用反射是否合适。这是一个代码异味吗?


可见性修饰符是这里的问题所在。只需删除“private”并使用“method_exists”。 - user187291
@stereofrog 我不是在尝试访问私有方法。我正在尝试不允许访问私有方法。 - Daniel Vandersluis
3个回答

4

在生产代码中,您真的不应该使用反射。为什么不在这里使用is_callable呢?

if (!is_callable(array('Validations', $validation_method)) {
    throw new LogicException('method does not exist');
}

这个检查确保Validations类有一个静态方法$validation_method,并确保您可以从当前上下文中调用它。实际上,这比反射更灵活,因为它考虑了__call方法和类似的东西,而反射不考虑。


抱歉,我的问题表述不够清晰,Validations 中的方法并不是静态方法。 - Daniel Vandersluis
在这种情况下,可以使用new Validations(或者一个现有的Validations实例)作为数组中的第一个元素,而不是使用'Validations'。 ;) - NikiC
你忘记了数组 ;) 如果你这样使用,你必须传递一个数组给 is_callable - NikiC
啊,糟糕,我确实这样做了。那是一个脑抽,尤其是因为我在我的类中到处使用回调函数。 :) - Daniel Vandersluis
1
为什么不应该在生产代码中使用反射?除了“知道你在做什么并保持简单”之外,我没有看到任何理由。 - Raffael

1
如果您的代码需要使用反射 API 的功能,则可以使用它。但是反射可能会很慢,当您使用它时进行基准测试绝不会有坏处。最终只有您才能决定它对应用程序有多大影响。我们仍然只谈微秒级别的时间。
然而,针对您的特定用例,我不明白为什么要使用反射。只需使用接口,并将验证分离到实现该接口的单独类中即可。请参见我的 示例验证类
如果您想在其中使用 __call(魔术方法也很慢),则可以使用 class_implements 来检查验证器是否实现了接口,然后您可以确信 validate 方法存在,例如,在主验证器类中添加。
public function __call($method, $args)
{
    if ( class_exists( $method )) {
        $validator = new $method;
        if($validator instanceof IValidate) {
            return call_user_func(array($validator, 'validate'), $args);
        }
        throw new BadMethodCallException('Class exists but is not a Validator');
    }
    throw new RuntimeException('Method does not exist');
}

顺便提一下:Zend Framework已经有大量的验证器可以使用。由于ZF是一个组件库,您可以在不必将整个应用程序迁移到ZF的情况下使用它们。PEAR也有一个验证包。


0

在我看来,我会避免在生产代码中使用反射。相反,我会创建一个必须包含验证方法的接口,而不是尝试调用validate_something。然后对于每个验证器类,您只需调用$valid->validate()。这样解释器更容易缓存该代码。


2
我会远离可能是世界上最大的打字错误。 - Alexander Morland

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接