Reading “this” and “use” arguments from a PHP clos

2020-03-23 18:19发布

问题:

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.

回答1:

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)
}