在PHP中捕获调用类的层次结构

Dan*_*ugg 6 php oop inheritance class

序言:

我所追求的是; 如果方法调用该get_typed_ancestor()方法,则执行所需操作所需的类名get_typed_ancestor()定义调用方法的类的名称.

传递$this提取类名失败,因为它将解析为具体类.如果调用方法是在层次结构中定义的抽象类中定义的,而不是实例所在的具体类,则会得到不正确的类名.

instanceof static由于与上述相同的原因寻找失败.

如下所述,捕获定义方法的类名的目的是,get_typed_ancestor()可以找到从定义调用方法的类派生的任何类的实例,而不仅仅是启动的具体类的另一个实例调用堆栈(因此$thisstatic不能令人满意)

到目前为止,传递__CLASS__get_typed_ancestor()似乎在是唯一的解决方案迄今,作为__CLASS__将妥善解决在其中定义调用方法的类名,而不是实例调用调用方法的类名.


注意:

我在这个问题的最后列出了一个显示工作__CLASS__论证方法和失败static方法的例子.如果你想要刺,也许可以用它作为开始.


问题:

我已经看到几个"解决方案"围绕着杠杆debug_backtrace()来捕获给定方法或函数的调用类; 然而,就我而言,这些(正如我的引号可能暗示的那样)并不完全是解决方案,因为debug_backtrace()这种方式使用的是黑客.

抛开一边,如果这个黑客是唯一的答案,那么我将会破解.

无论如何; 我正在研究一组在底部到顶部可遍历树中充当节点的类.这是一个类层次结构,简化为简洁:

abstract class AbstractNode{}

abstract class AbstractComplexNode extends AbstractNode{}

class SimpleNode extends AbstractNode{}

class ComplexNodeOne extends AbstractComplexNode{}

class ComplexNodeTwo extends AbstractComplexNode{}
Run Code Online (Sandbox Code Playgroud)

节点可以具有任何具体节点(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;
    }

}
Run Code Online (Sandbox Code Playgroud)

这个方法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();
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

get_typed_ancestor()当从上下文调用时,该方法AbstractComplexNode::get_something()将寻找类型(或扩展类型)的对象AbstractComplexNode- 在此层次结构的情况下,可能的具体类是ComplexNodeOneComplexNodeTwo.

由于AbstractComplexNode无法实例化,因此ComplexNodeOne将调用具体实例get_something().

我需要在这里强调一点; 在此前一种情况下的搜索必须AbstractComplexNode为了找到任一ComplexNodeOne或的第一个实例ComplexNodeTwo.正如稍后将要解释的那样,搜索instanceof static并将失败,因为它可能会跳过兄弟班和/或他们的孩子.

问题是,由于存在在主叫类是抽象的情况下,并且主叫方法由继承(并且因此从实例称为)一类诸如ComplexNodeOne,搜索父是一个不工作,如迟到了混凝土.instanceofstaticstaticComplexNodeOne

现在,我有一个解决方案,但我不喜欢它:

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();
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

这似乎有效,因为__CLASS__解析为定义的类名.不幸的是,我尝试使用__CLASS__默认参数get_typed_ancestor()没有成功(虽然这是预期的)

我正在考虑将$class参数保留为可选参数,但是如果它完全可以"隐式"将这些数据传递给方法(在没有可选参数的情况下),这将是很好的.


解决方案/故障:

  • __CLASS__从调用方法作为参数传递给get_typed_ancestor().
            工作,但不理想,因为我想get_typed_ancestor()解决调用类而不明确告知它.

  • 在搜索循环中,检查if($node->_parent instanceof static).
            当调用类继承调用方法时不起作用.它解析为调用方法的具体类,而不是定义的方法.这种失败当然也适用于selfparent.

  • 使用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
//          }
//        }
//      }
//    }
Run Code Online (Sandbox Code Playgroud)

mac*_*ohn 1

为了解决“捕获给定方法或函数的调用类”的问题,只需在构造函数中传递创建实例的对象即可。

<?php

class A {
  public function caller() {
    $b = new B ($this);
    $b->bar();
  }
}

class B {
  $whoClass = '';
  public function __construct($who)
  {
    $this->whoClass = get_class($who);
  }
  public function bar($who) {
    echo get_class($this->whoClass);
  }
}
Run Code Online (Sandbox Code Playgroud)