在PHP中将调用类捕获到层次结构中 [英] Capturing the calling class up the hierarchy in PHP

查看:195
本文介绍了在PHP中将调用类捕获到层次结构中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

前置



如果一个方法调用 get_typed_ancestor()方法,需要执行 get_typed_ancestor()定义调用方法的类的名称。



通过 $ this 提取类名失败,因为它将解析为具体类。如果调用方法在抽象类中被定义为高于实例所在的具体类的抽象类,我们会得到不正确的类名。



code> instanceof static 失败的原因与上述相同。



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



到目前为止,将 __ CLASS __ 传递给 get_typed_ancestor()到目前为止唯一的解决方案,因为 __ CLASS __ 将正确解析为定义调用方法的类名,而不是调用调用方法的实例的类名。

>我在这个问题的末尾包括一个示例,显示工作的 __ CLASS __ 参数方法,失败的 static 方法。

/ p>

我已经看到几个解决方案浮动在这个杠杆 debug_backtrace() 捕获给定方法或函数的调用类;这些都是(作为我的引号可能建议)不完全解决方案,就像我关心,因为 debug_backtrace()以这种方式使用是一个黑客。



如果这个黑客是唯一的答案,那么我就会破解。



无论如何;我正在做一组类,作为一个底部到顶部可遍历树中的节点。这里是一个类层次结构,简化为简化:

 抽象类AbstractNode {} 

抽象类AbstractComplexNode extends AbstractNode {}

类SimpleNode extends AbstractNode {}

类ComplexObjectOne扩展AbstractComplexNode {}

类ComplexNodeTwo extends AbstractComplexNode {}

节点可以有任何具体节点( null / em>)作为父级。查看 AbstractNode

 抽象类AbstractNode {

protected $ _parent;

public function get_typed_ancestor(){
//这是我工作的地方
}

public function get_parent(){
return $ this-> _parent;
}

}

方法



从扩展类中的其他方法来看, get_typed_ancestor() 被调用以找到该方法所属的类类型最接近的 _parent 。这通过一个例子更好地说明;给定先前的 AbstractNode 定义:

 抽象类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 后缀为nofollow> static 无效, code> ComplexNodeOne



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

 抽象类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;
}

}

抽象类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 它可以隐式地将这些数据传递给方法(如果没有可选参数),这将是巨大的。






解决方案/失败




  • code> __ CLASS __ 从调用方法作为 get_typed_ancestor()的参数。

    可以,但不是理想的,因为我想 get_typed_ancestor()来解决调用类,而不明确通知它。


  • p>在搜索循环中,检查 if($ node-> _parent instanceof static)

    当调用类继承调用方法。它解析为调用方法的具体类,而不是定义的方法。这种失败当然也适用于 self parent


  • 使用 debug_backtrace()捕获 $ trace [1] ['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;
}

}

类SimpleNode extends AbstractNode
{

}

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

}

类ComplexNodeOne扩展AbstractComplexNode
{

}

类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);

//此调用不正确地查找ComplexNodeTwo($ node_2),跳过
// 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
//}
//}

//此调用正确地查找ComplexNodeOne($ node_4),因为它是
//正在查找AbstractComplexNode的一个实例,从
解析//传递的__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);
}
}


Preamble:

What I'm after is; if a method calls the get_typed_ancestor() method, the class name needed to perform the operations required in get_typed_ancestor() is the name of the class in which the calling method is defined.

Passing $this to extract the class name fails because it will resolve to the concrete class. If the calling method is defined in an abstract class higher up the hierarchy than the concrete class of which the instance is, we get the incorrect class name.

Looking for an instanceof static fails for the same reason as described above.

As described below, the purpose for capturing the class name in which the method is defined, is so that get_typed_ancestor() can find an instance of any class derived from the class in which the calling method is defined, not just another instance of the concrete class that initiated the call stack (hence $this and static being unsatisfactory)

So far, passing __CLASS__ to get_typed_ancestor() seems to the be the only solution thus far, as __CLASS__ will properly resolve to the class name in which the calling method is defined, not the class name of the instance invoking the calling method.


Note:

I've included at the end of this question an example showing the working __CLASS__ argument approach, and the failed static approach. If you wanna take a stab, perhaps use that as a start.


Question:

I've seen several "solutions" floating around that leverage debug_backtrace() to capture the calling class of a given method or function; these however are (as my quotation marks may suggest) not exactly solutions as far as I'm concerned, as debug_backtrace() used in this manner is a hack.

Rant aside, if this hack is the only answer, then hack I shall.

Anyways; I'm working on a set of classes that act as nodes in a bottom-to-top traversable 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 may have any concrete node (or null) as a parent. Looking at AbstractNode:

abstract class AbstractNode{

    protected $_parent;

    public function get_typed_ancestor(){
        // here's where I'm working
    }

    public function get_parent(){
        return $this->_parent;
    }

}

The method get_typed_ancestor() is where I'm at.

From other methods in the extending classes, get_typed_ancestor() is called to find the closest _parent of the class type to which that method belongs. This is better illustrated with an example; given the previous AbstractNode definition:

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 method get_typed_ancestor(), when called from the context of AbstractComplexNode::get_something(), will be looking for an object of type (or extending type) AbstractComplexNode -- in the case of this hierarchy, the possible concrete classes being ComplexNodeOne and ComplexNodeTwo.

Since AbstractComplexNode cannot be instantiated, a concrete instance such as ComplexNodeOne would be invoking get_something().

I need to highlight a point here; the search in this previous case must be for AbstractComplexNode in order to find the first instance of either ComplexNodeOne or ComplexNodeTwo. As will be explained in a moment, searching for and instanceof static will fail, as it may skip instances of sibling classes and/or their children.

The problem is, since there are situations where the calling class is abstract, and the calling method is inherited by (and thus is called from an instance of) a class such as ComplexNodeOne, searching for a parent that is an instanceofstatic doesn't work, as static is late-bound to the concrete ComplexNodeOne.

Now, I have a solution, but I don't 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 appears to work, since __CLASS__ resolves to the class name of definition. Unfortunately, I tried using __CLASS__ as a default argument to get_typed_ancestor() with no success (though that was expected)

I'm considering leaving the $class argument as an optional regardless, but if it is at all possible to "implicitly" pass this data along to the method (in absence of the optional argument) that would be great.


Solutions/Failures:

  • Passing __CLASS__ from the calling method as an argument to get_typed_ancestor().
            Works, but is not ideal as I'd like get_typed_ancestor() to resolve the calling class without being explicitly informed of it.

  • In the search loop, checking if($node->_parent instanceof static).
            Doesn't work when the calling class inherits the calling method. It resolves to the concrete class in which the method is called, not the one in which is defined. This failure of course applies also to self and parent.

  • Use debug_backtrace() to capture $trace[1]['class'] and use that for the check.
            Works, but is not ideal as it's a hack.

It's tricky discussing a hierarchical data structure and supporting class hierarchy without feeling like you're confusing your audience.


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

// 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
//          }
//        }
//      }
//    }

解决方案

For solving the problem "to capture the calling class of a given method or function", simply pass the object that creates the instance in the constructor.

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

这篇关于在PHP中将调用类捕获到层次结构中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆