Reputation: 2768
I'm trying to generate an array of methods in a class that override trait methods. Here is a simplified example that shows the structure of methods of a typical class and trait in my app:
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';
}
}
Per the precedence rules listed in the PHP manual:
The precedence order is that methods from the current class override Trait methods, which in turn override methods from the base class.
this is behaving as expected because the output from this code:
$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;
The methods in the class demo
that override the trait methods from tGeneric
are MethodA() and MethodE(). Is there a way to programmatically generate an array of just these methods in the class that override the methods from the trait?
I have experimented with reflection but the GetMethods()
method retrieves all methods for a class, regardless of whether they originated in the class or were obtained via the use of a trait.
This code:
$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;
}
Upvotes: 5
Views: 236
Reputation: 14927
You could make almost sure a method overrides a trait method by comparing:
ReflectionFunctionAbstract::getFileName
),ReflectionFunctionAbstract::getStartLine
)if ($class_method->getFileName() !== $trait_method->getFileName()
|| $class_method->getStartLine() !== $trait_method->getStartLine()) {
$methods_overridden[] = $class_method->getName();
}
(of course, they also need to have the same name)
/**
* 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;
}
Demo here: https://3v4l.org/EcFIC
Upvotes: 3
Reputation: 57121
One possible solution would be more applicable if it was more a real code scenario where your traits and classes where in individual files - which should be how you code anyway (IMHO)...
So I put the trait code in a file (I've called it TraitTest.php). Then in my main script is the rest of the code you have, but of course using require_once 'TraitTest.php';
. Then in the DisplayInfo()
function, I've just added the value of getFileName()
, which shows the filename where the method is defined...
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;
}
which shows...
<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
As you can see, MethodB
is showing up as being defined in TraitTest.php
.
Upvotes: 1