PHP反射:如何知道方法/属性/常量是否继承自trait?
我想从列表中排除trait中未在类中重写的所有继承方法 那么,如何知道类成员是否是从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
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()
,还有classZ
与方法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