以编程方式识别覆盖特征方法的方法

时间:2018-12-28 14:47:23

标签: php

我正在尝试在重写特质方法的类中生成方法数组。这是一个简化的示例,显示了我的应用中典型类和特征的方法的结构:

class demo
{
    public function MethodA()
    {
        return 'method A from class';
    }

    public function MethodC()
    {
        return 'method C from class';
    }

    public function MethodE()
    {
        return 'method E from class';
    }

    use tGeneric;
}

trait tGeneric
{
    public function MethodA()
    {
        return 'method A from trait';
    }

    public function MethodB()
    {
        return 'method B from trait';
    }

    public function MethodD()
    {
        return 'method D from trait';
    }

    public function MethodE()
    {
        return 'method E from trait';
    }
}

根据PHP manual中列出的优先级规则:

  

优先顺序是当前类中的方法会覆盖Trait方法,而Trait方法又会覆盖基类中的方法。

这表现出预期,因为此代码的输出:

$object = new demo();
$array = [
    $object->MethodA(),
    $object->MethodB(),
    $object->MethodC(),
    $object->MethodD(),
    $object->MethodE()
];

$br = '<br>';
$msg = '';

foreach ($array as $value):
    $msg .= $value . $br;
endforeach;
echo $msg . $br;

是:
enter image description here

demo中重写tGeneric中特征方法的方法是MethodA()和MethodE()。有没有一种方法可以通过编程方式在类中仅生成这些方法的数组,以覆盖特征中的方法?

我已经尝试了反射,但是GetMethods()方法检索了一个类的所有方法,无论它们是起源于类还是通过使用特征获取的。

此代码:

$rc = new ReflectionClass('demo');
$d = $rc->GetMethods();

$traits = class_uses('demo');
foreach ($traits as $trait):
    $reflection = new ReflectionClass($trait);
    $t = $reflection->GetMethods();
endforeach;

DisplayInfo($d);
DisplayInfo($t);

function DisplayInfo($array)
{
    $br = '<br>';
    echo '<b>' . $array[0]->class . '</b>' .  $br;
    foreach ($array as $value):
        echo $value->name . $br;
    endforeach; 
    echo $br;
}

提供以下输出:
enter image description here

2 个答案:

答案 0 :(得分:2)

通过比较,您可以确保几乎确保方法覆盖特征方法:

条件

if ($class_method->getFileName() !== $trait_method->getFileName() 
    || $class_method->getStartLine() !== $trait_method->getStartLine()) {
  $methods_overridden[] = $class_method->getName();
}

(当然,它们也需要使用相同的名称)

全功能

/**
 * Given a class name, retrieves the corresponding class' methods that override
 * trait methods.
 *
 * @param string $class_name
 * @return \ReflectionMethod[]
 * @throws \ReflectionException
 */
function getMethodsOverriddenFromTraits(string $class_name): array
{
  $class = new \ReflectionClass($class_name);

  // Retrieve trait methods

  $trait_methods = [];
  foreach ($class->getTraits() as $trait) {
    foreach ($trait->getMethods() as $trait_method) {
      $trait_methods[$trait_method->getName()] = $trait_method;
    }
  }

  // Compute class methods that override them

  $methods_overridden = [];
  foreach ($class->getMethods() as $class_method) {
    if (array_key_exists($class_method->getName(), $trait_methods)) {
      $trait_method = $trait_methods[$class_method->getName()];
      if ($class_method->getFileName() !== $trait_method->getFileName() 
          || $class_method->getStartLine() !== $trait_method->getStartLine()) {
        $methods_overridden[] = $class_method->getName();
      }
    }
  }

  return $methods_overridden;
}

此处演示:https://3v4l.org/EcFIC

答案 1 :(得分:1)

如果这是一个更真实的代码场景,其中特征和类在单个文件中,那么这是一种可能的解决方案,它应该是您无论如何进行编码(IMHO)...

因此,我将特征代码存储在文件中(我将其称为TraitTest.php)。然后,在我的主脚本中是剩下的代码,但是当然要使用require_once 'TraitTest.php';。然后在DisplayInfo()函数中,我刚刚添加了getFileName()的值,该值显示了定义方法的文件名...

function DisplayInfo($array)
{
    $br = PHP_EOL;
    echo '<b>' . $array[0]->class . '</b>' .  $br;
    foreach ($array as $value)  {
        echo $value->getFileName()."->".$value->name . $br;
    }
    echo $br;
}

显示...

<b>demo</b>
/home/nigel/workspace2/Test/t1.php->MethodA
/home/nigel/workspace2/Test/t1.php->MethodC
/home/nigel/workspace2/Test/t1.php->MethodE
/home/nigel/workspace2/Test/TraitTest.php->MethodB
/home/nigel/workspace2/Test/TraitTest.php->MethodD

<b>TraitTest</b>
/home/nigel/workspace2/Test/TraitTest.php->MethodA
/home/nigel/workspace2/Test/TraitTest.php->MethodB
/home/nigel/workspace2/Test/TraitTest.php->MethodD
/home/nigel/workspace2/Test/TraitTest.php->MethodE

如您所见,MethodB显示为在TraitTest.php中定义。