91

The PHP manual states

It is not possible to use $this from anonymous function before PHP 5.4.0

on the anonymous functions page. But I have found I can make it work by assigning $this to a variable and passing the variable to a use statement at the function definition.

$CI = $this;
$callback = function () use ($CI) {
    $CI->public_method();
};

Is this a good practice?
Is there a better way to access $this inside an anonymous function using PHP 5.3?

Just a student
  • 10,560
  • 2
  • 41
  • 69
steampowered
  • 11,809
  • 12
  • 78
  • 98
  • 1
    Just a minor forum convention - it is usually better to accept an answer than to edit a question to reflect your preferred answer. Mainly this is so that responses still make sense in perpetuity, but also of course to give credit for a correct answer. – halfer Dec 05 '11 at 23:08
  • 4
    Beware that `$CI = $this;` and `$CI =& $this;` **aren't** actually identical. Maybe for your purposes, but they're not the same. Try `$CI = 'bla'; var_dump($this);` with both versions to see the difference. – Rudie Dec 06 '11 at 13:51
  • 1
    @Rudie I'm adding the [documentation](http://php.net/manual/en/language.oop5.references.php) for your comment – steampowered Dec 06 '11 at 19:08
  • @steampowered There's a good example/article online somewhere about this, but I couldn't find it =) Sorry. Just try it if you don't see the difference. It's obvious then. – Rudie Dec 07 '11 at 10:35
  • See also: ["Accessing private variables from within a closure"](http://stackoverflow.com/q/3722394/90527), [Accessing private/protected properties of an object in anonymous function in PHP](http://stackoverflow.com/q/6386733/90527) – outis Feb 13 '12 at 12:30

5 Answers5

70

It will fail when you try to call a protected or private method on it, because using it that way counts as calling from the outside. There is no way to work around this in 5.3 as far as I know, but come PHP 5.4, it will work as expected, out of the box:

class Hello {

    private $message = "Hello world\n";

    public function createClosure() {
        return function() {
            echo $this->message;
        };
    }

}
$hello = new Hello();
$helloPrinter = $hello->createClosure();
$helloPrinter(); // outputs "Hello world"

Even more, you will be able to change what $this points to at runtime, for anonymus functions (closure rebinding):

class Hello {

    private $message = "Hello world\n";

    public function createClosure() {
        return function() {
            echo $this->message;
        };
    }

}

class Bye {

    private $message = "Bye world\n";

}

$hello = new Hello();
$helloPrinter = $hello->createClosure();

$bye = new Bye();
$byePrinter = $helloPrinter->bindTo($bye, $bye);
$byePrinter(); // outputs "Bye world"

Effectively, anonymus functions will have a bindTo() method, where the first parameter can be used to specify what $this points to, and the second parameter controls what should the visibility level be. If you omit the second parameter, the visibility will be like calling from the "outside", eg. only public properties can be accessed. Also make note of the way bindTo works, it does not modify the original function, it returns a new one.

K. Norbert
  • 10,494
  • 5
  • 49
  • 48
  • 1
    Marking your answer correct, but just to clarify for other readers: the convention used in the question will work for public methods using the object which is referencing `$this`. – steampowered Dec 08 '11 at 21:40
  • 5
    [Non-public methods](http://stackoverflow.com/a/6386863/90527) can be accessed using reflection. Inefficient and a little evil, but it works. – outis Feb 13 '12 at 12:29
11

Don't always rely on PHP to pass objects by reference, when you are assigning a reference itself, the behavior is not the same as in most OO languages where the original pointer is modified.

your example:

$CI = $this;
$callback = function () use ($CI) {
$CI->public_method();
};

should be:

$CI = $this;
$callback = function () use (&$CI) {
$CI->public_method();
};

NOTE THE REFERENCE "&" and $CI should be assigned after final calls on it has been done, again else you might have unpredictable output, in PHP accessing a reference is not always the same as accessing the original class - if that makes sense.

http://php.net/manual/en/language.references.pass.php

7

That is the normal way it was done.
b.t.w, try to remove the & it should work without this, as objects pass by ref any way.

Itay Moav -Malimovka
  • 52,579
  • 61
  • 190
  • 278
1

That seems alright if your passing by reference it's the correct way to do it. If your using PHP 5 you don't need the & symbol before $this as it will always pass by reference regardless.

fire
  • 21,383
  • 17
  • 79
  • 114
1

This is fine. I should think you could do this also:

$CI = $this;

... since assignations involving objects will always copy references, not whole objects.

halfer
  • 19,824
  • 17
  • 99
  • 186