Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/261.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
PHP反射:如何知道方法/属性/常量是否继承自trait?_Php_Inheritance_Reflection_Traits - Fatal编程技术网

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

PHP反射:如何知道方法/属性/常量是否继承自trait?,php,inheritance,reflection,traits,Php,Inheritance,Reflection,Traits,我想从列表中排除trait中未在类中重写的所有继承方法 那么,如何知道类成员是否是从trait继承的呢 是的,我可以这样检查: if ($trait->hasMethod($methodName) || $ref->getTraitAliases()[$methodName] !== null) { // } $obj = new z; $ref = new ReflectionClass($obj); $traitRefs

我想从列表中排除trait中未在类中重写的所有继承方法 那么,如何知道类成员是否是从trait继承的呢

是的,我可以这样检查:

    if ($trait->hasMethod($methodName)
        || $ref->getTraitAliases()[$methodName] !== null)
    {
        //
    }
$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));
但是如果trait方法在类中被重写呢?怎么知道呢? 一种方法是检查方法体是否相似,如果相似,我可以排除它,
但是有更好的方法来实现这一点吗?

重要注意事项

这仅仅是因为“学术”兴趣,在实际情况下,你不应该在意——方法从何而来,因为它与特征的概念相矛盾,例如透明替代

此外,由于traits是如何工作的,任何类型的此类操作都可能被认为是“黑客行为”,因此不同PHP版本的行为可能不同,我不建议依赖于此

区别:困难

在PHP反射中,有一些方法将返回实例,指向trait的反射。这可用于获取类中使用的traits中声明的所有方法。但是-不,这对您的问题没有帮助,因为无法区分哪些方法随后在类中被重写

假设有trait
X
与方法
foo()
bar()
,还有class
Z
与方法
bar()
。然后,您将能够知道方法
foo()
bar()
是在trait中声明的,但是如果您尝试在类
Z
上使用,您显然会同时得到
foo()
bar()
。因此,你不能直接区分情况

区别:工作环境

然而,是的,还有一种方法可以让它继续发挥作用。第一种方法——正如您所提到的——尝试研究源代码。这很难看,但最终,这是唯一100%可靠的解决问题的方法

但是-不,还有另一种“不那么难看”的方法-检查类上的实例,这些实例是为类/traits方法创建的。PHP恰好会对trait方法使用相同的实例,但会覆盖在类中声明的方法实例

这种“检查”可以通过以下方式完成。简单设置:

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()';
    }
}
现在,要获取这两种情况的哈希值:

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()));
}
简而言之:它只是从类trait(第一个函数)或类本身(第二个函数)获取所有方法,然后合并结果以获得
key=>value
map,其中key是对象哈希,value是方法名

然后我们需要在相同的实例上使用它,如下所示:

    if ($trait->hasMethod($methodName)
        || $ref->getTraitAliases()[$methodName] !== null)
    {
        //
    }
$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));
因此,
$traitOnlyMethods
将只包含那些从trait派生的方法

相应的小提琴是。但请注意结果-不同版本的结果可能不同,就像在HHVM中一样,它根本不起作用(我假设是因为
spl\u object\u hash
是如何实现的-无论哪种方式,依靠它来区分对象都是不安全的-请参阅函数文档)


So,TD;博士-是的,即使没有源代码解析也可以(以某种方式)完成-但我无法想象为什么需要它,因为traits是用来将代码替换到类中的。

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

即使您克服了spl_object_hash()值被回收的问题,此解决方案也无法运行。可以通过将
get*MethodRefs()
函数重构为一个函数来解决此问题,该函数计算两个结果,并确保在创建类方法的类似对象时,trait方法的
ReflectionMethod
对象仍然存在。这样可以防止spl\u对象\u hash()值的循环

问题是,“PHP将为trait方法使用相同实例”的假设是完全错误的,这种情况的出现正是“幸运的”spl\u object\u hash()循环造成的。
$traitRef->getMethod('someName')
返回的对象将始终与
$classRef->getMethod('someName')
返回的对象不同,因此
->getMethods()返回的集合中的
ReflectionMethod
的对应实例也将不同,不管类中是否重写了方法
someName()
。这些对象不仅是不同的,甚至不是“相等的”:从
$traitRef
获取的
ReflectionMethod
实例将具有trait的名称作为其
class
属性的值,从
$classRef
获取的实例将具有该类的名称

小提琴:


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

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

对于trait和class位于同一个文件中的特殊情况,可以使用
ReflectionMethod::getstartine()
,并将其与trait和class的开始行和结束行进行比较


对于特殊情况,特征、类别和方法都在同一条线上。。哦,求你了

这是否有帮助:在这种情况下,绝对有必要回答原始问题:注释引擎!trait中的方法可以有docblock注释,其在类中的重写也可以有docblock注释。批注引擎必须能够组合来自trait方法的批注和类中的重写。此解决方案不正确。这似乎只是偶然发生的。如果交换trait x定义中的方法,即首先定义方法bar(),最后定义方法foo(),那么该程序会错误地将foo()报告为th