7

I have a function that is being passed a Closure. I want to find out the name of the method that closure is derived from. When I call print_r, it outputs this:

Closure Object
(
  [static] => Array
    (
      [listener] => Event_Subscriber_Calq@vendor_product_created
      [container] => Illuminate\Foundation\Application Object
...

How do I acess that listener value? I tried ->static, ::$static, getStatic(), I can't think of any way to get the value.

Currently, my plan is to use output buffering to capture the output from a var_dump. I can't use print_r for this, because the closure contains a reference to and object that references itself, and print_r takes ages to handle the recursion. I also can't use var_export, because it does not include the value I want in the output. So, this is my solution:

ob_start();
var_dump($closure);
$data = ob_get_clean();
$data = preg_replace('#^([^\n]*\n){4}#', '', $data);
$data = preg_replace('#\n.*#', '', $data);
$data = preg_replace('#.*string.[0-9]+. "(.*)".*#', '\1', $data);
list($class, $method) = explode('@', $data);

Which is horrible. Is there another way to do this? Maybe using reflection?

Benubird
  • 18,551
  • 27
  • 90
  • 141
  • what is the origin of data? where'd you got this anyway? maybe your object has its getter method – Kevin May 06 '15 at 11:44
  • It's actually coming from laravel, which does extend the core php closure class, but it does not add any new methods. – Benubird May 06 '15 at 11:46
  • *"...the name of the method that closure is derived from..."* - you mean the name of the method that passed the Closure? Or the name of the method that executed/called the Closure? – Quasdunk May 06 '15 at 11:49
  • Have you tried to convert this array from associative to numeric? http://php.net/manual/en/function.array-values.php – Gabriel Moretti May 06 '15 at 11:54
  • @Quasdunk a closure is normally a reference to an anonymous function, however it can also be a reference to a named function, which is what I believe I have here... although looking closer, I think it is more likely I am trying to retrieve data from the local scope of the closure... not sure that even makes sense. Well, whatever it is, I am trying to get the value of $obj->static->listener – Benubird May 06 '15 at 11:55
  • @GabrielMoretti Yes, it rejects it because a closure is an object, not an array. – Benubird May 06 '15 at 11:55
  • and what you get from get_object_vars()? @Benubird – Gabriel Moretti May 06 '15 at 11:57
  • @GabrielMoretti get_object_vars returns an empty array – Benubird May 06 '15 at 13:04
  • @Benubird why don't you back up a step or two and tell what it is you're trying to accomplish. I see you're working with Laravel. What are you trying to do? It is very likely that there is a canonical way in which to approach your larger objective. – Rob_vH Jun 24 '15 at 02:24
  • @Rob_vH I think I was trying to have one controller call another route without using a redirect, and without knowing the name of the other controller, only the name of the route, and also not knowing whether the route was actually a controller or a closure. I think there were some more complications I don't recall now, because what I just wrote doesn't quite match this question - it was two months ago, I don't recall the details. – Benubird Jun 24 '15 at 07:29

2 Answers2

8

I know this post is old, but in case someone is looking for info, you need to use ReflectionFunction:

$r = new ReflectionFunction($closure);
var_dump($r, $r->getStaticVariables(), $r->getParameters());

Regards, Alex

okandas
  • 11,570
  • 2
  • 15
  • 17
Alex S
  • 147
  • 2
  • 3
  • Thanks! This was exactly what I was looking for - I needed to get the class names of the listeners for a given event. – guice Sep 28 '20 at 20:19
0

In a recent project, I decided on a declarative approach by using a wrapper class. The class allows setting a freeform string describing the callback's origin, and can be used as a direct replacement for the closure, as it implements the __invoke() method.

Example:

use ClosureTools;

$closure = new NamedClosure(
    function() {
        // do something
    }, 
    'Descriptive text of the closure'
);

// Call the closure
$closure();

To access information on the closure:

if($closure instanceof NamedClosure) {
    $origin = $closure->getOrigin();
}

Since the origin is a freeform string, it can be set to whatever is useful to identify the closure depending on the use case.

Here is the class skeleton:

<?php

declare(strict_types=1);

namespace ClosureTools;

use Closure;

class NamedClosure
{
    /**
     * @var Closure
     */
    private $closure;

    /**
     * @var string
     */
    private $origin;

    /**
     * @param Closure $closure
     * @param string $origin
     */
    public function __construct(Closure $closure, string $origin)
    {
        $this->closure = $closure;
        $this->origin = $origin;
    }

    /**
     * @return string
     */
    public function getOrigin() : string
    {
        return $this->origin;
    }

    public function __invoke()
    {
        return call_user_func($this->closure, func_get_args());
    }
}
AeonOfTime
  • 956
  • 7
  • 10