How to inspect a closure in php?
Asked Answered
F

2

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?

Farmhand answered 6/5, 2015 at 11:35 Comment(10)
what is the origin of data? where'd you got this anyway? maybe your object has its getter methodMel
It's actually coming from laravel, which does extend the core php closure class, but it does not add any new methods.Farmhand
"...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?Haemophilia
Have you tried to convert this array from associative to numeric? php.net/manual/en/function.array-values.phpHadley
@Haemophilia 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->listenerFarmhand
@GabrielMoretti Yes, it rejects it because a closure is an object, not an array.Farmhand
and what you get from get_object_vars()? @FarmhandHadley
@GabrielMoretti get_object_vars returns an empty arrayFarmhand
@Farmhand 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.Fatten
@Fatten 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.Farmhand
M
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

Maggot answered 8/12, 2017 at 11:18 Comment(1)
Thanks! This was exactly what I was looking for - I needed to get the class names of the listeners for a given event.Doughman
P
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());
    }
}
Payola answered 29/4, 2021 at 14:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.