PHP反射:如何知道方法/属性/常量是否从特征继承?

时间:2015-06-05 08:38:42

标签: php inheritance reflection traits

我想从列表中排除特征中所有未继承的方法 那么如何知道一个类成员是否继承了trait?

是的,我可以这样检查一下:

    if ($trait->hasMethod($methodName)
        || $ref->getTraitAliases()[$methodName] !== null)
    {
        //
    }

但是如果在一个类中覆盖了特征方法怎么办?怎么知道呢? 一种方法是检查方法体是否相似,如果是,我可以排除它, 但有没有更好的方法来实现这一目标?

3 个答案:

答案 0 :(得分:3)

更简单的方法是ReflectionMethod::getFileName()。这将返回特征的文件名,而不是类。

对于特征和类在同一文件中的异国情况,可以使用ReflectionMethod::getStartLine(),并将其与特征和类的起点和终点线进行比较。

对于奇特的情况,特质和阶级和方法都在同一条线上..哦,请!

答案 1 :(得分:2)

重要提示

这只是因为“学术上”的兴趣,在实际情况下你不应该关心 - 从哪里获得方法,因为它与特征的观念相矛盾,例如:透明替代。

另外,由于特征是如何工作的,任何类型的操作都可能被视为“hacky”,因此不同PHP版本的行为可能会有所不同,我不建议依赖它。

区别:困难

在PHP的反思中,有getTraits()个方法将返回ReflectionClass实例,指向特征的反映。这可以用于获取在类中使用的所有方法,这些方法在类中使用。但是 - 不,它对你的问题没有帮助,因为不可能区分哪些方法在课堂上被覆盖。

想象一下,特征X包含方法foo()bar(),并且类Z包含方法bar()。然后,您将能够知道方法foo()bar()已在特征中声明,但如果您尝试在课程Z上使用getMethods() - 您显然会同时获得foo()bar()也是trait x { public function foo() { echo 'Trait x foo()'; } public function bar() { echo 'Trait x bar()'; } } class z { use x; public function foo() { echo 'Class foo()'; } } 。因此,直接你无法区分这种情况。

区别:工作骄傲

然而,是的,还有一种方法可以让它发挥作用。第一种方式 - 就像你提到的那样 - 尝试调查源代码。这很难看,但最后,这是解决问题的唯一可靠方法。

但是 - 不,还有另一种“不那么难看”的方式 - 检查ReflectionMethod类上的 实例 ,它们是为类/特征方法创建的。碰巧PHP会对trait方法使用相同的实例,但会覆盖那个在类中声明的方法。

可以使用spl_object_hash()完成此“检查”。设置简单:

function getTraitMethodsRefs(ReflectionClass $class)
{
    $traitMethods = call_user_func_array('array_merge', array_map(function(ReflectionClass $ref) {
        return $ref->getMethods();
    }, $class->getTraits()));
    $traitMethods = call_user_func_array('array_merge', array_map(function (ReflectionMethod $method) {
        return [spl_object_hash($method) => $method->getName()];
    }, $traitMethods));

    return $traitMethods;    
}

function getClassMethodsRefs(ReflectionClass $class)
{
    return call_user_func_array('array_merge', array_map(function (ReflectionMethod $method) {
        return [spl_object_hash($method) => $method->getName()];
    }, $class->getMethods()));
}

现在,要为这两种情况获取哈希值:

key=>value

简而言之:它只是从类特征(第一个函数)或类本身(第二个函数)中获取所有方法,然后将结果合并到get $obj = new z; $ref = new ReflectionClass($obj); $traitRefs = getTraitMethodsRefs($ref); $classRefs = getClassMethodsRefs($ref); $traitOnlyHashes = array_diff( array_keys($traitRefs), array_keys($classRefs) ); $traitOnlyMethods = array_intersect_key($traitRefs, array_flip($traitOnlyHashes)); map,其中key是对象哈希值,value是方法名称。

然后我们需要在同一个实例上使用它:

$traitOnlyMethods

结果,spl_object_hash将只包含那些从特征派生的方法。

相应的小提琴是here。但要注意结果 - 它们可能因版本而异,就像在HHVM中它不起作用(我假设因为<ion-header-bar class="thediv" ng-class="{scrolling: isActive}"> <ion-nav-bar class="bar-clear " > </ion-nav-bar> </ion-header-bar> 的实现方式 - 无论哪种方式,依赖它都是不安全的对象区分 - 请参阅函数文档。

所以,TD; DR; - 是的,即使没有源代码解析,它也可以(某种方式)完成 - 但我无法想象为什么需要它因为特征是有意的用于将代码替换为类。

答案 2 :(得分:2)

很抱歉,Alma Do接受的答案完全错误

即使您克服了spl_object_hash()值被回收的问题,此解决方案也无法正常工作。通过将get*MethodRefs()函数重构为一个计算两个结果的函数并确保在创建类方法的类似对象时,特征方法的ReflectionMethod对象仍然存在,可以解决此问题。这可以防止回收spl_object_hash()值。

问题是,假设&#34; PHP将使用相同的实例用于特征方法&#34;是完全错误的,这种情况的出现恰恰来自于幸运&#34; spl_object_hash()回收。 $traitRef->getMethod('someName')返回的对象总是$classRef->getMethod('someName')返回的对象不同,因此ReflectionMethod返回的集合中的->getMethods()对应实例也是如此1}},无论是否在类中重写方法someName()。这些对象不仅是截然不同的,它们甚至不会相等&#34;从ReflectionMethod获得的$traitRef实例将具有该特征的名称作为值它的class属性,从$classRef获得的属性将具有该类的名称。

小提琴:https://3v4l.org/CqEW3

似乎只有基于解析器的方法是可行的。