9 php reflection inheritance traits
我想从列表中的特征中排除所有继承的方法,这些方法在类中没有被覆盖 那么如何知道类成员是否从特征继承?
是的,我可以这样检查:
if ($trait->hasMethod($methodName)
|| $ref->getTraitAliases()[$methodName] !== null)
{
//
}
Run Code Online (Sandbox Code Playgroud)
但是如果在类中重写特征方法怎么办?怎么知道呢?一种方法是检查方法体是否相似,如果是,我可以排除它,但是有更好的方法来实现这一点吗?
一种更简单的方法是ReflectionMethod::getFileName(). 这将返回特征的文件名,而不是类。
对于 trait 和 class 在同一个文件中的特殊情况,可以使用ReflectionMethod::getStartLine(),并将其与 trait 和 class 的开始和结束行进行比较。
对于特征、类和方法都在同一条线上的奇特情况..拜托!
重要笔记
这只是因为“学术”兴趣,在实际情况中您不应该关心 - 方法是从哪里派生的,因为它与特征的概念相矛盾,例如透明替代。
此外,由于特征的工作方式,任何类型的此类操作都可能被视为“hacky”,因此不同 PHP 版本的行为可能有所不同,我不建议依赖于此。
区别:困难
在 PHP 的反射中,有一些getTraits()方法会返回ReflectionClass实例,指向特征的反射。这可用于获取在特征中声明并在类中使用的所有方法。但是 - 不,这对您的问题没有帮助,因为不可能区分类中随后覆盖了哪些方法。
想象一下,有一个X带有方法的特质foo(),并且bar()有一个Z带有方法的类bar()。然后你将能够知道方法foo()和bar()是在特征中声明的,但是如果你尝试getMethods()在类上使用Z- 你显然会同时得到foo()和bar()。因此,您无法直接区分情况。
区别:解决方法
然而,是的,有一种方法仍然可以让它发挥作用。第一种方法 - 就像你提到的 - 尝试调查源代码。这很丑陋,但最终,这是解决问题的唯一 100% 可靠的方法。
但是 - 不,还有另一种“不太难看”的方法 - 检查为类/特征方法创建的类实例。ReflectionMethodPHP 可能会为特征方法使用相同的实例,但会覆盖在类中声明的方法的实例。
这种“检查”可以通过 来完成spl_object_hash()。简单设置:
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()';
}
}
Run Code Online (Sandbox Code Playgroud)
现在,要获取这两种情况的哈希值:
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()));
}
Run Code Online (Sandbox Code Playgroud)
简而言之:它只是从类特征(第一个函数)或类本身(第二个函数)中获取所有方法,然后合并结果以获取key=>value映射,其中键是对象哈希,值是方法名称。
然后我们需要在同一个实例上使用它,如下所示:
$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));
Run Code Online (Sandbox Code Playgroud)
因此,结果$traitOnlyMethods将仅包含从特征派生的那些方法。
相应的小提琴在这里。但要注意结果 - 它们可能因版本而异,就像在 HHVM 中一样,它不起作用(我认为由于spl_object_hash实现方式的原因 - 无论哪种方式,依赖它进行对象区分都是不安全的 - 请参阅该函数的文档)。
所以,TD;DR;- 是的,即使没有源代码解析,它也可以(以某种方式)完成 - 但我无法想象为什么需要它,因为特征旨在用于将代码替换到类中。
| 归档时间: |
|
| 查看次数: |
1417 次 |
| 最近记录: |