Захват вызывающего класса вверх по иерархии в PHP

Преамбула :

Что я после есть; если метод вызывает метод 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
//          }
//        }
//      }
//    }

6
задан Dan Lugg 9 December 2011 в 03:25
поделиться