Преамбула :
Что я после есть; если метод вызывает метод get_typed_ancestor ()
, имя класса, необходимое для выполнения операций, требуемых в get_typed_ancestor ()
, является именем класса, в котором вызывающий метод - определено .
Передача $ this
для извлечения имени класса завершается ошибкой, поскольку оно разрешается в конкретный класс. Если вызывающий метод определен в абстрактном классе выше по иерархии, чем конкретный класс, в котором находится экземпляр, мы получаем неправильное имя класса.
Не удается найти instanceof static
по той же причине, что описана выше.
Как описано ниже, цель захвата имени класса, в котором определен метод, заключается в том, чтобы get_typed_ancestor ()
мог найти экземпляр любого класса, производного от класса, в котором вызывающий метод определен, а не просто еще один экземпляр конкретного класса, который инициировал стек вызовов (, следовательно, $ this
и static
неудовлетворительно )
Пока что прохождение __ CLASS __
- get_typed_ancestor ()
кажется единственным решением на данный момент, поскольку __ CLASS __
будет правильно преобразовываться в имя класса, в котором определен вызывающий метод, а не имя класса экземпляра, вызывающего вызывающий метод.
Примечание :
Я включил в конце этого вопроса пример, показывающий рабочий подход аргумента __ CLASS __
и неудачный статический
подход. Если вы хотите нанести удар, возможно, используйте это как начало.
Вопрос :
Я видел несколько «решений», плавающих вокруг этого использования debug_backtrace ()
для захвата вызывающего класса данного метода или функции; однако это (, поскольку мои кавычки могут предложить ) не совсем решения, насколько я понимаю, поскольку debug_backtrace ()
, используемый таким образом, является взломом.
Если отбросить эту хитрость, то я сделаю это.
В любом случае; Я работаю над набором классов, которые действуют как узлы в просматриваемом дереве снизу вверх.Вот упрощенная для краткости иерархия классов:
abstract class AbstractNode{}
abstract class AbstractComplexNode extends AbstractNode{}
class SimpleNode extends AbstractNode{}
class ComplexNodeOne extends AbstractComplexNode{}
class ComplexNodeTwo extends AbstractComplexNode{}
Узлы могут иметь любой конкретный узел ( или null
) в качестве родительского. Глядя на AbstractNode
:
abstract class AbstractNode{
protected $_parent;
public function get_typed_ancestor(){
// here's where I'm working
}
public function get_parent(){
return $this->_parent;
}
}
Я нахожусь в методе get_typed_ancestor ()
.
Из других методов в расширяющихся классах вызывается get_typed_ancestor ()
, чтобы найти ближайший _parent
типа класса, к которому принадлежит этот метод. Это лучше проиллюстрировать на примере; учитывая предыдущее определение 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();
}
}
}
Метод get_typed_ancestor ()
при вызове из контекста AbstractComplexNode :: get_something ()
будет искать объект типа ( или расширяющийся тип ) AbstractComplexNode
- в случае этой иерархии возможными конкретными классами являются ComplexNodeOne
и ComplexNodeTwo
].
Поскольку AbstractComplexNode
не может быть создан, конкретный экземпляр, такой как ComplexNodeOne
, будет вызывать get_something ()
.
Мне нужно выделить здесь один момент: поиск в предыдущем случае должен быть для AbstractComplexNode
, чтобы найти первый экземпляр ComplexNodeOne
или ComplexNodeTwo
. Как будет объяснено чуть позже, поиск и instanceof static
завершится ошибкой, так как он может пропустить экземпляры родственных классов и / или их потомков.
Проблема заключается в том, что существуют ситуации, когда вызывающий класс является абстрактным, а вызывающий метод наследуется ( и, таким образом, вызывается из экземпляра ) таким классом, как как ComplexNodeOne
, поиск родителя, который является instanceof
static
, не работает, поскольку static
поздно привязан к бетону ComplexNodeOne
.
Теперь у меня есть решение, но оно мне не нравится:
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();
}
}
}
Кажется, это работает, поскольку __ CLASS __
преобразуется в имя класса определения. К сожалению, я безуспешно пытался использовать __ CLASS __
в качестве аргумента по умолчанию для get_typed_ancestor ()
( хотя это ожидалось )
Я собираюсь оставить аргумент $ class
как необязательный независимо, но если вообще возможно «неявно» передать эти данные методу ( в отсутствие необязательного аргумента ), это было бы здорово .
Решения / ошибки :
Передача __ CLASS __
из вызывающего метода в качестве аргумента get_typed_ancestor ()
.
Работает, но не идеально, поскольку я бы хотел, чтобы get_typed_ancestor ()
разрешил вызывающий класс, не будучи об этом явно уведомлен.
В цикле поиска проверка if ($ node -> _ parent instanceof static)
.
Не работает, когда вызывающий класс наследует вызывающий метод. Он преобразуется в конкретный класс, в котором вызывается метод, а не в тот, в котором определен. Этот сбой, конечно же, относится также к себе
и родителю
.
Используйте debug_backtrace ()
, чтобы захватить $ trace [1] ['class']
и использовать его для проверки.
Работает, но не идеально, так как это взлом.
Сложно обсуждать иерархическую структуру данных и поддерживающую иерархию классов, не чувствуя, что вы сбиваете с толку свою аудиторию.
Пример :
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);
// this call incorrectly finds ComplexNodeTwo ($node_2), skipping
// the instance of ComplexNodeOne ($node_4)
$node_6->test_method_static();
// object(ComplexNodeTwo)#2 (2) {
// ["_id":protected]=>
// int(2)
// ["_parent":protected]=>
// object(SimpleNode)#1 (2) {
// ["_id":protected]=>
// int(1)
// ["_parent":protected]=>
// NULL
// }
// }
// this call correctly finds ComplexNodeOne ($node_4) since it's
// looking for an instance of AbstractComplexNode, resolved from
// the passed __CLASS__
$node_6->test_method_class();
// object(ComplexNodeOne)#4 (2) {
// ["_id":protected]=>
// int(4)
// ["_parent":protected]=>
// object(SimpleNode)#3 (2) {
// ["_id":protected]=>
// int(3)
// ["_parent":protected]=>
// object(ComplexNodeTwo)#2 (2) {
// ["_id":protected]=>
// int(2)
// ["_parent":protected]=>
// object(SimpleNode)#1 (2) {
// ["_id":protected]=>
// int(1)
// ["_parent":protected]=>
// NULL
// }
// }
// }
// }