Table of Contents

Removal of $this in closures

This document describes the changes that were done in order to remove $this support from closures for PHP 5.3 beta1. This has become necessary in order to make sure that when a consensus is found on how to add $this to closures, it will be able to integrate that without BC issues.

Userland perspective

In the userland perspective, $this may not be used in closures. Also, using the static keyword in front of function is not allowed anymore. Examples:

$closure = function () { }; // still works
$closure = static function () { }; // DOES NOT WORK
class Foo {
  public $pubMember;
  private $privMembeR;
  function bar {
    $closure = function () {
      return $this; // DOES NOT WORK
    };
    $closure = static function () { // DOES NOT WORK
    };
    $closure = function () { echo "Hello World!\n"; }; // still works
    $closure = function () use ($this) { ... }; // DOES NOT WORK
    $self = $this;
    $closure = function () use ($self) { // still works
       echo $self->pubMember; // still works
       echo $self->privMember; // DOES NOT WORK
    };
  }
}

Yes, these are quite some restrictions on closures, however, this is the only possibility to ensure that support for $this can be added in any fashion that may later be required.

Internals perspective

  1. zend_closure structure: this_ptr member is gone.
  2. removed zend_get_closure() prototype in zend_closures.h (was not implemented anyway anymore since it's now done via a handler)
  3. removd scope and this_ptr parameters from zend_create_closure()
  4. removed zend_get_closure_this_ptr

Important note on get_closure handler

However, the get_closure handler prototype was NOT changed (only the implementation for closures, which always set the scope and object ptr to NULL) and still allows the handler implementation to set a scope and this_ptr because the handler is also responsible for __invoke on normal objects!

Reflection

Reflection was changed a bit in order to make sure Non-OOP-Closures are supported consistently.

getClosure() / getClosureThis() removal

Removed ReflectionFunction::getClosure() and ReflectionFunction::getClosureThis(). Removal of getClosureThis() is obvious (no $this stored in closures anymore). However, getClosure() must also be removed. This is due to the simple fact that in the previous implementation, one could do $reflectionMethod->getClosure ($object) (ReflectionMethod inherits from ReflectionFunction) and get a Closure that calls the method for the specific object, which means that a closure with bound $this was returned.

Since allowing that for this special case would perhaps inhibit the possibility of later adding JS-like $this binding to closures at all, it is best to simply remove the method and perhaps later re-add it when the type of implementation is clear.

Modified ReflectionFunction / ReflectionMethod dynamic

In the original RFC, ReflectionMethod also accepted solely $closure as a parameter to the constructor and implied _invoke as method name. Those however only gave access to the dynamic invoke method and not the closure method itself. Somewhat later, somebody added the possibility to simply use $closure as parameter for ReflectionFunction.

This is now consolidated:

  1. ReflectionFunction now accepts $closure as sole parameter
  2. ReflectionMethod now does not automatically infer _invoke as method name for single objects that were passed.

Thus, a new ReflectionFunction($closure) has access to the real user-defined closure details and new ReflectionMethod($closure, “_invoke”) has access to the internal auto-generated handler method.

new ReflectionMethod ($closure) will not work anymore. This is for consistency reasons, since if both ReflectionMethod and ReflectionFunction constructors accepted the closure directly, it would not be clear what new ReflectionParameter ($closure, 1) would do (now it is clear: use ReflectionFunction as the constructor).

isClosure

isClosure now detects a closure according to the flags (fn_flags & ZEND_ACC_CLOSURE).