[ACCEPTED]-Find out which class called a method in another class-php

Accepted answer
Score: 63

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

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 6 output :

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 5 look like, I am not sure it should be used 4 as a "normal thing" in your application... Seems 3 odd, actually : with a good design, a method 2 should not need to know what called it, in 1 my opinion.

Score: 21

Here is one liner solution

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

As of PHP7 this 2 won't work based on the docs: http://php.net/manual/en/function.list.php as we cannot 1 have empty properties, here is a small update:

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

You could also have the calling object pass 17 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 16 from the book "Design Patterns: elements 15 of reusable object-oriented software" by 14 Erich Gamma, et al, on page 278 in the discussion 13 on the "Mediator" structural pattern.

The 12 point of the pattern is to reduce the number 11 of many-to-many connections between a bunch 10 of objects/classes. You create a mediator 9 class that all those classes treat as a 8 hub. That way the classes don't need to 7 know about each other. The mediator handles 6 the interactions. For the mediator to be 5 informed of changes in the classes it tracks, they 4 can pass themselves as arguments, or the 3 mediator can be implemented using the "Observer" pattern.

2018 2 EDIT:

I sometimes use interfaces with the 1 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"
Score: 2

You can probably achieve this with a debug backtrace, though 4 this seems kind of hackish.

Your alternative 3 option is to pass a parameter to that class 2 and tell it where it is being called from, when 1 you instantiate the class from within another.

Score: 2

At the very least, you could use debug_backtrace and analyze 5 that to find the calling method.

I think 4 you should also be able to do it using the 3 reflection API, but it's been too long since I've used 2 PHP and I don't remember exactly how. The 1 links should at least get you started, however.

Score: 2

@Pascal MARTIN: Yes, in normal applicacions 40 it's probably not needed. But sometimes 39 it could be useful. Consider an example 38 from my own app:

There's a Controller subclass 37 which can use a Template object to prepare 36 its output. Every template has a name to 35 refer it to. When a Controller needs a Template, it 34 asks the TemplateManager for it by giving 33 that name as a parameter. But there could 32 be many template files with that name for 31 different Controllers. Controlers are used 30 as plugins, and may be written by different 29 users, so the names used by them can't be 28 controlled to no collide with each other. Namespaces 27 for templates are needed. So TemplateManager, which 26 is a factory for Template objects, needs 25 the template name and the namespace name 24 to locate the proper template source file. This 23 namespace is related to the particular Controller's 22 class name.

But, in most cases, each Controller 21 will be using templates from its own namespace 20 and only in rare cases from other namespaces. So 19 specifying the namespace in each call to 18 TemplateManager::getTemplate() each time 17 would be a mess. It's better if namespace 16 is optional and defaults to... the Controller which calls the TemplateManager::getTemplate()! And here's 15 a good place for knowing the caller.

Of course 14 the caller Controller could pass itself 13 or its name as a parameter, but it doesn't 12 really differ much from passing the namespace 11 name. It couldn't be optional in either 10 way.

But if you can know the caller, you 9 can use that information to default the 8 namespace automatically inside the getTemplate(), without 7 even bothering the caller. It doesn't have 6 to know how getTemplate() is handling it 5 in its inside and how does it know the proper 4 default namespace. He only needs to know 3 that it does, and that it can pass any other 2 namespace optionally if it really needs 1 to.

Score: 0

This function does the job without debug_backtrace 1 :

/*
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'];
           }
      }
   }
}
Score: 0
var_dump(getClass($this)); 

Used in a method in namespace B this will 2 give you the class that called a method 1 in namespace B from namespace A.

More Related questions