找出在另一个类中称为方法的类

时间:2009-07-31 18:14:22

标签: php

在PHP中是否有办法找出哪个对象在另一个对象中称为什么方法。

〔实施例:

class Foo
{
  public function __construct()
  {
    $bar = new Bar();
    $bar->test();
  }
}

class Bar
{
  public function test()
  {
  }
}
$foo = new Foo();

有没有办法让我发现测试方法是从foo对象调用的?

8 个答案:

答案 0 :(得分:54)

您可以使用debug_backtrace,有点像这样:
BTW,请看一下手册页上的评论:给出了一些有用的功能和建议; - )

class Foo
{
  public function __construct()
  {
    $bar = new Bar();
    $bar->test();
  }
}

class Bar
{
  public function test()
  {
      $trace = debug_backtrace();
      if (isset($trace[1])) {
          // $trace[0] is ourself
          // $trace[1] is our caller
          // and so on...
          var_dump($trace[1]);

          echo "called by {$trace[1]['class']} :: {$trace[1]['function']}";

      }
  }
}
$foo = new Foo();

var_dump会输出:

array
  'file' => string '/home/squale/developpement/tests/temp/temp.php' (length=46)
  'line' => int 29
  'function' => string '__construct' (length=11)
  'class' => string 'Foo' (length=3)
  'object' => 
    object(Foo)[1]
  'type' => string '->' (length=2)
  'args' => 
    array
      empty

echo

called by Foo :: __construct

但是,尽管看起来很好看,但我不确定它应该在您的应用程序中用作“正常的东西”......看起来很奇怪,实际上:设计好,方法不需要知道在我看来是什么叫它。

答案 1 :(得分:16)

这是一个班轮解决方案

list(, $caller) = debug_backtrace(false, 2);

从PHP7开始,这将无法使用文档:http://php.net/manual/en/function.list.php,因为我们不能有空属性,这里有一个小更新:

list($childClass, $caller) = debug_backtrace(false, 2);

答案 2 :(得分:5)

您也可以将调用对象作为参数传递给自己

e.g。

class Foo
{
  public function __construct()
  {
    $bar = new Bar();
    $bar->test($this);
  }
}

class Bar
{
  public function test()
  {
  }
}
$foo = new Foo();

我从Erich Gamma等人的“设计模式:可重复使用的面向对象软件的元素”一书中获得了这个想法,第278页讨论了“Mediator”结构模式。

模式的要点是减少一堆对象/类之间的多对多连接数。您创建一个中介类,所有这些类都将其视为中心。这样,课程就不需要彼此了解。中介处理交互。为了让调解者了解它所跟踪的类的变化,他们可以将自己作为参数传递,或者可以使用“观察者”模式实现调解者。

2018年编辑:

我有时会使用带有上述代码的接口,如下所示:

interface someInterface // many classes may implement this interface
{
  public function giveMeBar();
}

class Foo implements someInterface
{
  public function __construct()
  {
    $bar = new Bar();
    $bar->test($this);
  }
  public function giveMeBar() {
    return 'Bar';
  }
}
class Bar
{
  public function test(someInterface $a)
  {
    echo $a->giveMeBar();
  }
}
$foo = new Foo(); // prints "Bar"

答案 3 :(得分:2)

你可以用debug backtrace来达到这个目的,虽然这看起来很像黑客。

您可以选择将参数传递给该类,并在从另一个类实例化该类时告诉它从哪个类调用。

答案 4 :(得分:2)

至少,你可以使用debug_backtrace并分析它以找到调用方法。

我认为你也应该能够使用reflection API来实现它,但是因为我使用过PHP而且我不记得究竟是怎么回事。但是,这些链接至少可以帮助您入门。

答案 5 :(得分:2)

@Pascal MARTIN: 是的,在正常的应用中,可能不需要它。但有时它可能有用。 考虑一下我自己的应用程序中的一个例子:

有一个Controller子类,可以使用Template对象来准备其输出。每个模板都有一个名称可以引用它。当Controller需要一个模板时,它会通过将该名称作为参数向TemplateManager请求它。 但是可能有许多具有该名称的模板文件用于不同的控制器。控制器用作插件,可以由不同的用户编写,因此不能控制它们使用的名称,以免相互冲突。需要模板的命名空间。 因此TemplateManager是Template对象的工厂,需要模板名称和命名空间名称来定位正确的模板源文件。此命名空间与特定Controller的类名相关。

但是,在大多数情况下,每个Controller都将使用其自己的命名空间中的模板,并且仅在极少数情况下使用其他命名空间。因此,每次在每次调用TemplateManager :: getTemplate()时指定命名空间都会很麻烦。如果命名空间是可选的并且默认为... 调用TemplateManager :: getTemplate()的控制器,那就更好了!这里是了解调用者的好地方。

当然,调用者Controller可以将自身或其名称作为参数传递,但它与传递命名空间名称并没有太大差别。它无论如何都不是可选的。

但是如果您可以知道调用者,则可以使用该信息在getTemplate()内自动默认命名空间,甚至不会打扰调用者。它不必知道getTemplate()如何在其内部处理它,以及它如何知道正确的默认命名空间。他只需知道它确实存在,并且它可以选择性地传递任何其他命名空间。

答案 6 :(得分:0)

此函数在没有debug_backtrace的情况下完成工作:

/*
usage :
some code...
getRealCallClass(__FUNCTION__);
some code...
*/

function getRealCallClass($functionName) //Parameter value must always be __FUNCTION__
{
  try
   {
     throw new exception();
   }
  catch(exception $e)
   {
     $trace = $e->getTrace();
     $bInfunction = false;
     foreach($trace as $trace_piece)
      {
          if ($trace_piece['function'] == $functionName)
           {
             if (!$bInfunction)
              $bInfunction = true;
           }
          elseif($bInfunction) //found !!!
           {
             return $trace_piece['class'];
           }
      }
   }
}

答案 7 :(得分:0)

var_dump(getClass($this)); 

在命名空间B中的方法中使用,这将为您提供从命名空间A调用命名空间B中的方法的类。