Preamble
That I am after me; if the method calls get_typed_ancestor() , the class name needed to perform the operations required by get_typed_ancestor() is the name of the class in which the call method is defined.
Passing $this to retrieve the class name fails because it will resolve the particular class. If the calling method is defined in an abstract class above the hierarchy than the specific instance class, we get the wrong class name.
The instanceof static search is not performed for the same reason as described above.
As described below, the purpose of capturing the name of the class in which this method is defined is that get_typed_ancestor() can find an instance of any class that is derived from the class in which the call method is defined, and not just another instance of the particular class that initiated the stack calls (hence $this and static was unsatisfactory)
Until now, passing __CLASS__ to get_typed_ancestor() seems to be the only solution so far, since __CLASS__ will correctly resolve the class name in which the invocation method is defined, rather than the instance class name, invoking the invocation method.
Note
At the end of this question, I included an example showing the working argument of the __CLASS__ argument and the failed static approach. If you want to make a punch, perhaps use this as a start.
Question
I have seen several βsolutionsβ floating around this debug_backtrace() lever to capture the calling class of a given method or function; but nonetheless (as my quotation marks may be suggested), itβs not entirely accurate, as far as I understand, since debug_backtrace() used in this way is a hack.
Turn aside if this hack is the only answer, and then hack.
Anyway; I am working on a set of classes that act as nodes in a drop-down parent tree. Here's a class hierarchy simplified for brevity:
abstract class AbstractNode{} abstract class AbstractComplexNode extends AbstractNode{} class SimpleNode extends AbstractNode{} class ComplexNodeOne extends AbstractComplexNode{} class ComplexNodeTwo extends AbstractComplexNode{}
Nodes can have any particular node (or null ) as a parent. Looking at AbstractNode :
abstract class AbstractNode{ protected $_parent; public function get_typed_ancestor(){
The get_typed_ancestor() method is where I am.
Of the other methods in the extension classes, get_typed_ancestor() is called to find the closest _parent class to which this method belongs. This is better illustrated by example; given the previous definition of AbstractNode :
abstract class AbstractComplexNode extends AbstractNode{ public function get_something(){ if(something_exists()){ return $something; } $node = $this->get_typed_ancestor(); if(null !== $node){ return $node->get_something(); } } }
The get_typed_ancestor() method, called from the AbstractComplexNode::get_something() context, will look for an object of type (or an extension type) AbstractComplexNode - in the case of this hierarchy, the possible concrete classes being ComplexNodeOne and ComplexNodeTwo .
Since AbstractComplexNode cannot be created, a specific instance, such as ComplexNodeOne , will call get_something() .
I need to highlight a point here: the search in this previous case should be for AbstractComplexNode to find the first instance of either ComplexNodeOne or ComplexNodeTwo . As will be explained in an instant, the search and instanceof static will fail, as it may skip instances of kinship classes and / or their children.
Problem is that there are situations where the calling class is abstract, and the calling method is inherited (and therefore called from the instance) of a class such as ComplexNodeOne , finding a parent that is instanceof static does not work, since static bound to a specific ComplexNodeOne .
Now I have a solution, but I do not like it:
abstract class AbstractNode{ public function get_typed_ancestor($class){ $node = $this; while(null !== $node->_parent){ if($node->_parent instanceof $class){ return $node->_parent; } $node = $node->_parent; } return null; } } abstract class AbstractComplexNode extends AbstractNode{ public function get_something(){ if(something_exists()){ return $something; } $node = $this->get_typed_ancestor(__CLASS__); if(null !== $node){ return $node->get_something(); } } }
This works because __CLASS__ resolves the definition class name. Unfortunately, I tried using __CLASS__ as the default argument to get_typed_ancestor() with no success (although expected)
I consider the possibility of leaving the $class argument as optional independently, but if it is at all possible to implicitly pass this data along with the method (in the absence of an optional argument), that would be great.
Decisions / Failures
Passing __CLASS__ from the calling method as an argument to get_typed_ancestor() .
It works, but not perfect, since I would like to get_typed_ancestor() to allow the calling class without being explicitly informed about it.
In the search loop, checking if($node->_parent instanceof static) .
Does not work when the calling class inherits the calling method. It resolves the specific class in which the method is called, and not the one in which it is defined. This rejection, of course, also applies to self and parent .
Use debug_backtrace() to capture $trace[1]['class'] and use this to check.
Works, but not perfect, how to hack.
Itβs hard to discuss the hierarchical data structure and maintain the class hierarchy without feeling like you are confusing your audience.
An example :
abstract class AbstractNode { protected $_id; protected $_parent; public function __construct($id, self $parent = null) { $this->_id = $id; if(null !== $parent) { $this->set_parent($parent); } } protected function get_typed_ancestor_by_class($class) { $node = $this; while(null !== $node->_parent) { if($node->_parent instanceof $class) { return $node->_parent; } $node = $node->_parent; } return null; } public function get_typed_ancestor_with_static() { $node = $this; while(null !== $node->_parent) { if($node->_parent instanceof static) { return $node->_parent; } $node = $node->_parent; } return null; } public function set_parent(self $parent) { $this->_parent = $parent; } } class SimpleNode extends AbstractNode { } abstract class AbstractComplexNode extends AbstractNode { public function test_method_class() { var_dump($this->get_typed_ancestor_by_class(__CLASS__)); } public function test_method_static() { var_dump($this->get_typed_ancestor_with_static()); } } class ComplexNodeOne extends AbstractComplexNode { } class ComplexNodeTwo extends AbstractComplexNode { } $node_1 = new SimpleNode(1); $node_2 = new ComplexNodeTwo(2, $node_1); $node_3 = new SimpleNode(3, $node_2); $node_4 = new ComplexNodeOne(4, $node_3); $node_5 = new SimpleNode(5, $node_4); $node_6 = new ComplexNodeTwo(6, $node_5);