Alana Storm
Alana Storm

Reputation: 166106

View a PHP Closure's Source

Is it possible to reflect into or otherwise view the source of a PHP closure object? That is, if I do something like this

$closure = function()
{
    return 'Hi There';
};

and then something like this

var_dump($closure);

PHP outputs

object(Closure)[14]

That is, I know the object's a closure, but I have no idea what it does.

I'm looking for a reflection method, function, or debugging extension that will allow me to dump the actual body of anonymous function.

Upvotes: 22

Views: 7290

Answers (1)

hek2mgl
hek2mgl

Reputation: 158100

What you can get from PHP is limited, using reflection you can just obtain the parameter signature of the function and the start and ending line of the source code file. I've once wrote a blog article about that: http://www.metashock.de/2013/05/dump-source-code-of-closure-in-php/ ...

It lead me to the following code, using reflection:

function closure_dump(Closure $c) {
    $str = 'function (';
    $r = new ReflectionFunction($c);
    $params = array();
    foreach($r->getParameters() as $p) {
        $s = '';
        if($p->isArray()) {
            $s .= 'array ';
        } else if($p->getClass()) {
            $s .= $p->getClass()->name . ' ';
        }
        if($p->isPassedByReference()){
            $s .= '&';
        }
        $s .= '$' . $p->name;
        if($p->isOptional()) {
            $s .= ' = ' . var_export($p->getDefaultValue(), TRUE);
        }
        $params []= $s;
    }
    $str .= implode(', ', $params);
    $str .= '){' . PHP_EOL;
    $lines = file($r->getFileName());
    for($l = $r->getStartLine(); $l < $r->getEndLine(); $l++) {
        $str .= $lines[$l];
    }
    return $str;
}

If you have the following closure:

$f = function (Closure $a, &$b = -1, array $c = array())
  use ($foo) 
{
    echo $this->name;
    echo 'test';
};

closure_dump() will give the following results:

function (Closure $a, &$b = -1, array $c = array (
)){
 use ($foo)
{
    echo $this->name;
    echo 'test';
};

You see it is imperfect (the array param). Also it will not handle some edge cases properly, especially if closures are nested or multiple inline closures will getting passed to a function in one line. The latter looks most problematic to me. Since, you get only the starting and ending line from reflection, both functions will be on that line in this case and you have no useful information to decide which one of them should get dumped. So far, I didn't found a solution for that, also I'm unsure if there is a solution.

However, in most cases, it should at least being helpful for debugging, as long as you don't rely on it. Feel free to enhance it!

Upvotes: 27

Related Questions