Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

View a PHP Closure's Source

Tags:

closures

php

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.

like image 651
Alan Storm Avatar asked Aug 30 '14 19:08

Alan Storm


People also ask

Does PHP have closure?

In PHP, a closure is a callable class, to which you've bound your parameters manually. It's a slight distinction but one I feel bears mentioning. Small little trick. You can use a closures in itself via reference.

What does closure mean in PHP?

A closure is an anonymous function that can access variables imported from the outside scope without using any global variables. Theoretically, a closure is a function with some arguments closed (e.g. fixed) by the environment when it is defined. Closures can work around variable scope restrictions in a clean way.

What is closure in PHP and why does it use use identifier?

The closure is a function assigned to a variable, so you can pass it around. A closure is a separate namespace, normally, you can not access variables defined outside of this namespace. There comes the use keyword: use allows you to access (use) the succeeding variables inside the closure.

Is closure an anonymous function?

Anonymous functions, also known as closures , allow the creation of functions which have no specified name. They are most useful as the value of callable parameters, but they have many other uses. Anonymous functions are implemented using the Closure class.


1 Answers

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!

like image 156
hek2mgl Avatar answered Sep 29 '22 15:09

hek2mgl