在PHP中是否有办法找出哪个对象在另一个对象中称为什么方法。
〔实施例:
class Foo
{
public function __construct()
{
$bar = new Bar();
$bar->test();
}
}
class Bar
{
public function test()
{
}
}
$foo = new Foo();
有没有办法让我发现测试方法是从foo对象调用的?
答案 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中的方法的类。