68

I see that the new planned features for PHP 5.4 are: traits, array dereferencing, a JsonSerializable interface and something referred to as 'closure $this support'

http://en.wikipedia.org/wiki/Php#Release_history

While the others are either immediately clear (JsonSerialiable, array dereferencing) or i looked up the specifics (traits), I am not sure what 'closure $this support' is. I have been unsuccessful googling for it or finding anything about it on php.net

Does anyone know what this is supposed to be?

If i had to guess, it would mean something like this:

$a = 10; $b = 'strrrring';
//'old' way, PHP 5.3.x
$myClosure = function($x) use($a,$b)
             {
                 if (strlen($x) <= $a) return $x;
                 else return $b;
             };

//'new' way with closure $this for PHP 5.4
$myNewClosure = function($x) use($a as $lengthCap,$b as $alternative)
                 {
                     if(strlen($x) <=  $this->lengthCap)) return $x;
                     else 
                     {
                         $this->lengthCap++;  //lengthcap is incremented for next time around
                         return $this->alternative;
                     }
                 };

The significance (even if this example is trivial) being that in the past once the closure is constructed the bound 'use' variables are fixed. With 'closure $this support' they are more like members you can mess with.

Does this sound correct and/or close and/or reasonable? Does anyone know what this 'closure $this support' means?

samayo
  • 16,163
  • 12
  • 91
  • 106
jon_darkstar
  • 16,398
  • 7
  • 29
  • 37
  • FWIW, 5.4 is not yet the *official* name of PHP trunk -- it's still internally called "5.3.99" and there is some controversy as to whether it will really be 5.4 or not (vs 6.0, which would then be unrelated to the big Unicode rewrite "6.0" branch). – Charles Apr 21 '11 at 22:35

4 Answers4

74

This was already planned for PHP 5.3, but

For PHP 5.3 $this support for Closures was removed because no consensus could be reached how to implement it in a sane fashion. This RFC describes the possible roads that can be taken to implement it in the next PHP version.

It indeed means you can refer to the object instance (live demo)

<?php
class A {
  private $value = 1;
  public function getClosure() 
  {
    return function() { return $this->value; };
  }
}

$a = new A;
$fn = $a->getClosure();
echo $fn(); // 1

For a discussion, see the PHP Wiki

and for historic interest:

hakre
  • 193,403
  • 52
  • 435
  • 836
Gordon
  • 312,688
  • 75
  • 539
  • 559
  • oooh, i was going in totally the wrong direction. you mean '$this' being the instance of `Foo` right? i thought of '$this' as being the closure itself. would `use` still be necessary? – jon_darkstar Apr 20 '11 at 17:23
  • 3
    @jon no, using `use` is not even possible. You cannot use $this as lexical variable in any PHP version to date. I updated with the example from the Wiki and a link to a codepad showing the results with the current PHP trunk. – Gordon Apr 20 '11 at 17:26
  • 1
    thanks this is great. btw - the `public $closure` doesnt do anything and just kind of distracts from your point, which you otherwies demonstrate very well – jon_darkstar Apr 20 '11 at 18:11
  • i edited that out, hope you dont mind. links are great btw, ive never really looked at those wiki pages before – jon_darkstar Apr 20 '11 at 18:33
  • 1
    @Gordon - I think jon meant `use` in the general case--not specific to `$this`. AFAIK, `use` will still be necessary to access local variables. I'm looking forward to dropping the `use ($self)` trick. :) – David Harkness Jun 30 '11 at 18:14
  • the problem about the use($self) trick is that it works for public methods only, what we really wanted is to be able to access all members, after all we are talking to $this... it will take a little while until everyone starts using php 5.4, some servers still use 5.2 – SparK Oct 19 '11 at 11:11
  • For clarity, with reference of the provided RFC link (https://wiki.php.net/rfc/closures/object-extension) the implementation in PHP 5.4a+ most closely resembles approach **A**, **B**, **C**, or **D**? I'm assuming **A**, however if there are any caveats that provide the dynamic rebinding behavior prescribed by **D**, that would be fantastic. – Dan Lugg Oct 25 '11 at 16:08
  • 1
    @Bracketworks I am not 100% sure but people more involved with the decision told me they think it's A. – Gordon Oct 25 '11 at 16:45
  • Thanks @Gordon - That's what I figured. Hopefully the PHP docs will be updated soon, to include something definitive. – Dan Lugg Oct 25 '11 at 18:39
  • Actually, `$closure->bindTo($object);` appears to perform the task of rebinding manually, and should be a fantastic addition to PHP. – Dan Lugg Oct 25 '11 at 18:56
53

One thing that Gordon missed is re-binding of $this. While what he described is the default behaviour, it is possible to re-bind it.

Example

class A {
    public $foo = 'foo';
    private $bar = 'bar';

    public function getClosure() {
        return function ($prop) {
            return $this->$prop;
        };
    }
}

class B {
    public $foo = 'baz';
    private $bar = 'bazinga';
}

$a = new A();
$f = $a->getClosure();
var_dump($f('foo')); // prints foo
var_dump($f('bar')); // works! prints bar

$b = new B();
$f2 = $f->bindTo($b);
var_dump($f2('foo')); // prints baz
var_dump($f2('bar')); // error

$f3 = $f->bindTo($b, $b);
var_dump($f3('bar')); // works! prints bazinga

The closures bindTo instance method (alternatively use the static Closure::bind) will return a new closure with $this re-bound to the value given. The scope is set by passing the second argument, this will determine visibility of private and protected members, when accessed from within the closure.

nickb
  • 59,313
  • 13
  • 108
  • 143
igorw
  • 27,759
  • 5
  • 78
  • 90
22

Building on @Gordon's answer it is posible to mimic some hacky aspects of closure-$this in PHP 5.3.

<?php
class A
{
    public $value = 12;
    public function getClosure()
    {
        $self = $this;
        return function() use($self)
        {
            return $self->value;
        };
    }
}

$a = new A;
$fn = $a->getClosure();
echo $fn(); // 12
Xeoncross
  • 55,620
  • 80
  • 262
  • 364
  • 10
    Yes, but a notable difference is that you can't access any private or protected properties or methods on `$self` from within the closure. – jgivoni Jun 28 '12 at 21:37
  • 3
    +1 haha, found this answer a year later and it solved my problem. ;) – Xeoncross Jan 20 '13 at 02:47
  • @jgivoni does you means, it is not possible to access private or protected variable in PHP 5.3? – Shiro Dec 24 '13 at 04:57
  • 2
    @Shiro, yes. This workaround for 5.3 only works with public properties. – jgivoni Feb 10 '14 at 10:46
  • @Pacerier, I am not aware of any workaround for accessing non-public properties within closures in PHP 5.3. Best advice is to upgrade to 5.4 or later. – jgivoni Dec 11 '14 at 09:35
2

Just building on the other answers here, I think this example will demonstrate what is possible PHP 5.4+:

<?php

class Mailer {
    public    $publicVar    = 'Goodbye ';
    protected $protectedVar = 'Josie ';
    private   $privateVar   = 'I love CORNFLAKES';

    public function mail($t, $closure) {
        var_dump($t, $closure());
    }
}

class SendsMail {
    public    $publicVar    = 'Hello ';
    protected $protectedVar = 'Martin ';
    private   $privateVar   = 'I love EGGS';

    public function aMailingMethod() {
        $mailer = new Mailer();
        $mailer->mail(
            $this->publicVar . $this->protectedVar . $this->privateVar,
            function() {
                 return $this->publicVar . $this->protectedVar . $this->privateVar;
            }
        );
    }
}

$sendsMail = new SendsMail();
$sendsMail->aMailingMethod();

// prints:
// string(24) "Hello Martin I love EGGS"
// string(24) "Hello Martin I love EGGS"

see: https://eval.in/private/3183e0949dd2db

Harry Mustoe-Playfair
  • 1,369
  • 16
  • 29