Is there a way in PHP to find out what object called what method in another object.
有没有办法在PHP中找出哪个对象称为另一个对象中的哪个方法。
Exmaple:
〔实施例:
class Foo
{
public function __construct()
{
$bar = new Bar();
$bar->test();
}
}
class Bar
{
public function test()
{
}
}
$foo = new Foo();
Would there be a way for me to find out that the test method was called from the foo object?
有没有办法让我发现测试方法是从foo对象调用的?
8 个解决方案
#1
50
you could use debug_backtrace
, a bit like this :
BTW, take a look at the comments on the manual page : there are some useful functions and advices given ;-)
你可以使用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();
The var_dump
would output :
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
and the echo
:
和回声:
called by Foo :: __construct
But, as nice as it might look like, I am not sure it should be used as a "normal thing" in your application... Seems odd, actually : with a good design, a method should not need to know what called it, in my opinion.
但是,尽管它可能看起来很好,我不确定它应该在您的应用程序中用作“正常的东西”......看起来很奇怪,实际上:有一个好的设计,一个方法不应该知道它叫什么, 我的想法是。
#2
14
Here is one liner solution
这是一个班轮解决方案
list(, $caller) = debug_backtrace(false, 2);
As of PHP7 this won't work based on the docs: http://php.net/manual/en/function.list.php as we cannot have empty properties, here is a small update:
从PHP7开始,这将无法根据文档工作:http://php.net/manual/en/function.list.php,因为我们不能有空属性,这里有一个小更新:
list($childClass, $caller) = debug_backtrace(false, 2);
#3
5
You could also have the calling object pass itself as an argument
您也可以将调用对象作为参数传递给自己
e.g.
例如
class Foo
{
public function __construct()
{
$bar = new Bar();
$bar->test($this);
}
}
class Bar
{
public function test()
{
}
}
$foo = new Foo();
I got this idea from the book "Design Patterns: elements of reusable object-oriented software" by Erich Gamma, et al, on page 278 in the discussion on the "Mediator" structural pattern.
我从Erich Gamma等人在第278页讨论“Mediator”结构模式的“设计模式:可重用的面向对象软件的元素”一书中得到了这个想法。
The point of the pattern is to reduce the number of many-to-many connections between a bunch of objects/classes. You create a mediator class that all those classes treat as a hub. That way the classes don't need to know about each other. The mediator handles the interactions. For the mediator to be informed of changes in the classes it tracks, they can pass themselves as arguments, or the mediator can be implemented using the "Observer" pattern.
模式的要点是减少一堆对象/类之间的多对多连接数。您创建一个中介类,所有这些类都将其视为中心。这样,课程就不需要彼此了解。中介处理交互。为了让调解者了解它所跟踪的类的变化,他们可以将自己作为参数传递,或者可以使用“观察者”模式实现调解者。
2018 EDIT:
2018年编辑:
I sometimes use interfaces with the above code, like this:
我有时会使用上面代码的接口,如下所示:
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"
#4
2
You can probably achieve this with a debug backtrace, though this seems kind of hackish.
你可以通过调试回溯来实现这一点,尽管这看起来有点像hackish。
Your alternative option is to pass a parameter to that class and tell it where it is being called from, when you instantiate the class from within another.
您可以选择将参数传递给该类,并在从另一个类实例化该类时告诉它从哪个类中调用它。
#5
2
At the very least, you could use debug_backtrace and analyze that to find the calling method.
至少,您可以使用debug_backtrace并分析它以查找调用方法。
I think you should also be able to do it using the reflection API, but it's been too long since I've used PHP and I don't remember exactly how. The links should at least get you started, however.
我认为你也应该能够使用反射API来实现它,但是因为我已经使用过PHP而且我不记得究竟是怎么回事。但是,这些链接至少可以帮助您入门。
#6
2
@Pascal MARTIN: Yes, in normal applicacions it's probably not needed. But sometimes it could be useful. Consider an example from my own app:
@Pascal MARTIN:是的,在正常的应用中,可能不需要它。但有时它可能有用。考虑我自己的应用程序中的示例:
There's a Controller subclass which can use a Template object to prepare its output. Every template has a name to refer it to. When a Controller needs a Template, it asks the TemplateManager for it by giving that name as a parameter. But there could be many template files with that name for different Controllers. Controlers are used as plugins, and may be written by different users, so the names used by them can't be controlled to no collide with each other. Namespaces for templates are needed. So TemplateManager, which is a factory for Template objects, needs the template name and the namespace name to locate the proper template source file. This namespace is related to the particular Controller's class name.
有一个Controller子类,可以使用Template对象来准备其输出。每个模板都有一个名称可以引用它。当Controller需要一个模板时,它会通过将该名称作为参数向TemplateManager请求它。但是可能有许多具有该名称的模板文件用于不同的控制器。控制器用作插件,可以由不同的用户编写,因此不能控制它们使用的名称,以免相互冲突。需要模板的命名空间。因此,TemplateManager是Template对象的工厂,需要模板名称和命名空间名称来定位正确的模板源文件。此命名空间与特定Controller的类名相关。
But, in most cases, each Controller will be using templates from its own namespace and only in rare cases from other namespaces. So specifying the namespace in each call to TemplateManager::getTemplate() each time would be a mess. It's better if namespace is optional and defaults to... the Controller which calls the TemplateManager::getTemplate()! And here's a good place for knowing the caller.
但是,在大多数情况下,每个Controller将使用来自其自己的命名空间的模板,并且仅在极少数情况下来自其他命名空间。因此,每次在每次调用TemplateManager :: getTemplate()时指定命名空间都会很麻烦。如果命名空间是可选的并且默认为...调用TemplateManager :: getTemplate()的Controller,那就更好了!这里是了解来电者的好地方。
Of course the caller Controller could pass itself or its name as a parameter, but it doesn't really differ much from passing the namespace name. It couldn't be optional in either way.
当然,调用者Controller可以将自身或其名称作为参数传递,但它与传递命名空间名称并没有太大差别。它无论如何都不是可选的。
But if you can know the caller, you can use that information to default the namespace automatically inside the getTemplate(), without even bothering the caller. It doesn't have to know how getTemplate() is handling it in its inside and how does it know the proper default namespace. He only needs to know that it does, and that it can pass any other namespace optionally if it really needs to.
但是如果您可以知道调用者,则可以使用该信息在getTemplate()内自动默认命名空间,甚至不会打扰调用者。它不必知道getTemplate()如何在其内部处理它以及它如何知道正确的默认命名空间。他只需知道它确实存在,并且它可以选择性地传递任何其他命名空间,如果真的需要的话。
#7
0
This function does the job without debug_backtrace :
此函数在没有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'];
}
}
}
}
#8
0
var_dump(getClass($this));
Used in a method in namespace B this will give you the class that called a method in namespace B from namespace A.
在命名空间B中的方法中使用,这将为您提供从命名空间A调用命名空间B中的方法的类。
#1
50
you could use debug_backtrace
, a bit like this :
BTW, take a look at the comments on the manual page : there are some useful functions and advices given ;-)
你可以使用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();
The var_dump
would output :
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
and the echo
:
和回声:
called by Foo :: __construct
But, as nice as it might look like, I am not sure it should be used as a "normal thing" in your application... Seems odd, actually : with a good design, a method should not need to know what called it, in my opinion.
但是,尽管它可能看起来很好,我不确定它应该在您的应用程序中用作“正常的东西”......看起来很奇怪,实际上:有一个好的设计,一个方法不应该知道它叫什么, 我的想法是。
#2
14
Here is one liner solution
这是一个班轮解决方案
list(, $caller) = debug_backtrace(false, 2);
As of PHP7 this won't work based on the docs: http://php.net/manual/en/function.list.php as we cannot have empty properties, here is a small update:
从PHP7开始,这将无法根据文档工作:http://php.net/manual/en/function.list.php,因为我们不能有空属性,这里有一个小更新:
list($childClass, $caller) = debug_backtrace(false, 2);
#3
5
You could also have the calling object pass itself as an argument
您也可以将调用对象作为参数传递给自己
e.g.
例如
class Foo
{
public function __construct()
{
$bar = new Bar();
$bar->test($this);
}
}
class Bar
{
public function test()
{
}
}
$foo = new Foo();
I got this idea from the book "Design Patterns: elements of reusable object-oriented software" by Erich Gamma, et al, on page 278 in the discussion on the "Mediator" structural pattern.
我从Erich Gamma等人在第278页讨论“Mediator”结构模式的“设计模式:可重用的面向对象软件的元素”一书中得到了这个想法。
The point of the pattern is to reduce the number of many-to-many connections between a bunch of objects/classes. You create a mediator class that all those classes treat as a hub. That way the classes don't need to know about each other. The mediator handles the interactions. For the mediator to be informed of changes in the classes it tracks, they can pass themselves as arguments, or the mediator can be implemented using the "Observer" pattern.
模式的要点是减少一堆对象/类之间的多对多连接数。您创建一个中介类,所有这些类都将其视为中心。这样,课程就不需要彼此了解。中介处理交互。为了让调解者了解它所跟踪的类的变化,他们可以将自己作为参数传递,或者可以使用“观察者”模式实现调解者。
2018 EDIT:
2018年编辑:
I sometimes use interfaces with the above code, like this:
我有时会使用上面代码的接口,如下所示:
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"
#4
2
You can probably achieve this with a debug backtrace, though this seems kind of hackish.
你可以通过调试回溯来实现这一点,尽管这看起来有点像hackish。
Your alternative option is to pass a parameter to that class and tell it where it is being called from, when you instantiate the class from within another.
您可以选择将参数传递给该类,并在从另一个类实例化该类时告诉它从哪个类中调用它。
#5
2
At the very least, you could use debug_backtrace and analyze that to find the calling method.
至少,您可以使用debug_backtrace并分析它以查找调用方法。
I think you should also be able to do it using the reflection API, but it's been too long since I've used PHP and I don't remember exactly how. The links should at least get you started, however.
我认为你也应该能够使用反射API来实现它,但是因为我已经使用过PHP而且我不记得究竟是怎么回事。但是,这些链接至少可以帮助您入门。
#6
2
@Pascal MARTIN: Yes, in normal applicacions it's probably not needed. But sometimes it could be useful. Consider an example from my own app:
@Pascal MARTIN:是的,在正常的应用中,可能不需要它。但有时它可能有用。考虑我自己的应用程序中的示例:
There's a Controller subclass which can use a Template object to prepare its output. Every template has a name to refer it to. When a Controller needs a Template, it asks the TemplateManager for it by giving that name as a parameter. But there could be many template files with that name for different Controllers. Controlers are used as plugins, and may be written by different users, so the names used by them can't be controlled to no collide with each other. Namespaces for templates are needed. So TemplateManager, which is a factory for Template objects, needs the template name and the namespace name to locate the proper template source file. This namespace is related to the particular Controller's class name.
有一个Controller子类,可以使用Template对象来准备其输出。每个模板都有一个名称可以引用它。当Controller需要一个模板时,它会通过将该名称作为参数向TemplateManager请求它。但是可能有许多具有该名称的模板文件用于不同的控制器。控制器用作插件,可以由不同的用户编写,因此不能控制它们使用的名称,以免相互冲突。需要模板的命名空间。因此,TemplateManager是Template对象的工厂,需要模板名称和命名空间名称来定位正确的模板源文件。此命名空间与特定Controller的类名相关。
But, in most cases, each Controller will be using templates from its own namespace and only in rare cases from other namespaces. So specifying the namespace in each call to TemplateManager::getTemplate() each time would be a mess. It's better if namespace is optional and defaults to... the Controller which calls the TemplateManager::getTemplate()! And here's a good place for knowing the caller.
但是,在大多数情况下,每个Controller将使用来自其自己的命名空间的模板,并且仅在极少数情况下来自其他命名空间。因此,每次在每次调用TemplateManager :: getTemplate()时指定命名空间都会很麻烦。如果命名空间是可选的并且默认为...调用TemplateManager :: getTemplate()的Controller,那就更好了!这里是了解来电者的好地方。
Of course the caller Controller could pass itself or its name as a parameter, but it doesn't really differ much from passing the namespace name. It couldn't be optional in either way.
当然,调用者Controller可以将自身或其名称作为参数传递,但它与传递命名空间名称并没有太大差别。它无论如何都不是可选的。
But if you can know the caller, you can use that information to default the namespace automatically inside the getTemplate(), without even bothering the caller. It doesn't have to know how getTemplate() is handling it in its inside and how does it know the proper default namespace. He only needs to know that it does, and that it can pass any other namespace optionally if it really needs to.
但是如果您可以知道调用者,则可以使用该信息在getTemplate()内自动默认命名空间,甚至不会打扰调用者。它不必知道getTemplate()如何在其内部处理它以及它如何知道正确的默认命名空间。他只需知道它确实存在,并且它可以选择性地传递任何其他命名空间,如果真的需要的话。
#7
0
This function does the job without debug_backtrace :
此函数在没有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'];
}
}
}
}
#8
0
var_dump(getClass($this));
Used in a method in namespace B this will give you the class that called a method in namespace B from namespace A.
在命名空间B中的方法中使用,这将为您提供从命名空间A调用命名空间B中的方法的类。