Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading "this" and "use" arguments from a PHP closure

When you create a method that returns a closure in PHP:

class ExampleClass {
  public function test() {
    $example = 10;

    return function() use ($example) {
      return $example;
    };
  }
}

The result of print_r contains this (the class whose method created the closure) and static, which appears to be the values bound within the use () statement of the closure:

$instance = new ExampleClass();
$closure = $instance->test();

print_r($closure);

Producing:

Closure Object (
    [static] => Array (
        [example] => 10
    )
    [this] => ExampleClass Object()
)

However I cannot for the life of me work out how to capture these values. It is not possible to use any form of property accessor (e.g. $closure->static or $closure->{'static'}) without receiving the following:

PHP Fatal error: Uncaught Error: Closure object cannot have properties in XYZ.

Array access notation obviously does not work either:

PHP Fatal error: Uncaught Error: Cannot use object of type Closure as array in XYZ.

JSON encoding the object, besides this making the values useless were they objects, provides an empty JSON object {} and using the ReflectionFunction class does not provide access to these items.

The closure documentation does not provide any means of accessing these values at all either.

Outside of doing something shameful like output buffering and parsing print_r or similar, I cannot actually see a way to get these values.

Am I missing something obvious?

Note: The use-case is for implementing memoization and these values would be extremely beneficial in identifying whether or not the call matched a previous cached call.

like image 506
Marty Avatar asked Sep 11 '17 05:09

Marty


People also ask

What is the closure 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 a closure in PHP and why does it use the use identifier?

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. use is early binding. That means the variable values are COPIED upon DEFINING the closure.

How are closures established in PHP?

Basically a closure in PHP is a function that can be created without a specified name - an anonymous function. Here's a closure function created as the second parameter of array_walk() . By specifying the $v parameter as a reference one can modify each value in the original array through the closure function.

What is PHP closure class?

The Closure class ¶Class used to represent anonymous functions. Anonymous functions yield objects of this type. This class has methods that allow further control of the anonymous function after it has been created. Besides the methods listed here, this class also has an __invoke method.


1 Answers

It seems you may have overlooked some of the ReflectionFunction methods.

Take a look at the ReflectionFunction::getClosureThis() method. I tracked it down by looking through the PHP 7 source code by doing a search for the zend_get_closure_this_ptr() which is defined in zend_closures.c.

The manual currently doesn't have a lot of documentation for this function. I'm using 7.0.9; try running this code based on your example:

class ExampleClass {
  private $testProperty = 33;

  public function test() {
    $example = 10;

    return function() use ($example) {
      return $example;
    };
  }
}

$instance = new ExampleClass();
$closure = $instance->test();

print_r($closure);

$func = new ReflectionFunction($closure);
print_r($func->getClosureThis());

You should get output similar to

Closure Object
(
    [static] => Array
        (
            [example] => 10
        )

    [this] => ExampleClass Object
        (
            [testProperty:ExampleClass:private] => 33
        )

)

ExampleClass Object
(
    [testProperty:ExampleClass:private] => 33
)

Regarding the closure static variables, these are returned with ReflectionFunction::getStaticVariables():

php > var_dump($func->getStaticVariables());
array(1) {
  ["example"]=>
  int(10)
}
like image 122
Roger Gee Avatar answered Oct 11 '22 18:10

Roger Gee