簡體   English   中英

在PHP中捕獲調用類的層次結構

[英]Capturing the calling class up the hierarchy in PHP

序言

我所追求的是; 如果方法調用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{}

節點可以具有任何具體節點( 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();
        }
    }

}

當從AbstractComplexNode::get_something()的上下文調用時,方法get_typed_ancestor()將查找類型( 或擴展類型 )的對象AbstractComplexNode - 在此層次結構的情況下,可能的具體類是ComplexNodeOneComplexNodeTwo

由於AbstractComplexNode無法實例化,因此ComplexNodeOne等具體實例將調用get_something()

我需要在這里強調一點; 此前一種情況下的搜索必須AbstractComplexNode才能找到ComplexNodeOneComplexNodeTwo的第一個實例。 正如稍后將要解釋的那樣,搜索和instanceof static將失敗,因為它可能會跳過兄弟類和/或其子代的實例。

問題是,因為有些情況下調用類是抽象的,並且調用方法是由一個類(例如ComplexNodeOne )繼承( 因此從一個實例調用 ),所以搜索作為static instanceof的父類不會work,因為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() ,但並不理想,因為我希望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
//          }
//        }
//      }
//    }

要解決“ 捕獲給定方法或函數的調用類 ”的問題,只需在構造函數中傳遞創建實例的對象。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM