43
$ php --version
PHP 5.5.4 (cli) (built: Sep 19 2013 17:10:06) 
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2013 Zend Technologies

The following code (similar to example at https://bugs.php.net/bug.php?id=49543):

class Foo
{
    public function bar()
    {
        return function() use ($this)
        {
            echo "in closure\n";
        };
    }
}

fails with:

PHP Fatal error:  Cannot use $this as lexical variable

Yet according to the PHP docs and a comment on that bug report from Rasmus Lerdorf, using $this in anonymous functions was added as of PHP 5.4. What am I doing wrong?

tjbp
  • 3,427
  • 3
  • 24
  • 35
  • Check to make sure you're using the same version of PHP from the web server. It's often different than whatever is first in your path in Bash. Use `phpinfo()`. – Brad Oct 17 '13 at 15:54
  • According to a quick test in http://3v4l.org/G28oE that’s still the same in 5.5.5 even … maybe Rasmus was just wrong on this one …? Edit: Or maybe it was _intended_ to be in 5.4 at the time he stated this (too lazy to look up know if 5.4 was already out by then), but they removed it again because it caused additional problems the way they implemented it. – CBroe Oct 17 '13 at 15:55
  • @Brad Worth mentioning, but I tested this with the cli. – tjbp Oct 17 '13 at 16:55

8 Answers8

63

So it seems $this can be used simply if it isn't specified via the "use" keyword.

The following echoes 'bar':

class Foo
{
    private $foo = 'bar';

    public function bar()
    {
        return function()
        {
            echo $this->foo;
        };
    }
}

$bar = (new Foo)->bar();

$bar();

This was reported in the php-internals mailing list and is apparently overhang from 5.3's lack of support for this functionality:

http://marc.info/?l=php-internals&m=132592886711725

tjbp
  • 3,427
  • 3
  • 24
  • 35
9

In PHP 5.3 if you are using a Closure inside of a class, the Closure will not have access to $this.

In PHP 5.4, support has been added for the usage of $this in Closures.

KernelPanic
  • 2,328
  • 7
  • 47
  • 90
coolstoner
  • 719
  • 2
  • 9
  • 20
7

I don't know the answer to your actual question (ie Why can't you do it), but I can give you a work around: Use a temporary copy of $this and use() that instead:

class Foo
{
    public function bar()
    {
        $that = $this;
        return function() use($that)
        {
            print_r($that);
        };
    }
}

I've just tested it, and this does work.

Spudley
  • 166,037
  • 39
  • 233
  • 307
  • 1
    Assigning object to variables are done via reference, so `temporary copy of $this` is actually `$that is reference of $this`. But then again, why can't we use `$this` in use statement, but we can use variables assigned by reference? – machineaddict Nov 25 '14 at 11:55
6

The issue is that including $this in the use() statement is not allowed.

However if you don't include it then it will work fine.

So the issue isn't whether the use statement is present, it's whether $this is present in the use statement.

This should work (@see https://3v4l.org/smvPt):

class Foo{
    private $a;
    function getAnon(){
        $b = 1;
        return function() use ($b) { 
            echo $b;
            echo $this->a;
        };
    }
}

This shouldn't:

class Foo{
    private $a;
    function getAnon(){
        $b = 1;
        return function() use ($this, $b) { 
            echo $b;
            echo $this->a;
        };    
    }
}

I suppose essentially $this is implicitly captured.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Tolan
  • 81
  • 1
  • 2
5

You can use this:

class Foo
{
  public function bar()
  {
    $obj = $this;
    return function() use ($obj)
    {
        //$obj->DoStuff();
        echo "in closure\n";
    };
  }
 }
4

I know, this is an old question, but maybe someone from google find this:

The reason why you get the error is, because you can't use an already defined variable name as lexical variable in the same closure.

Since in PHP 5.5 and above you can access $this inside the closure, a variable with the name $this already exists.

Here is another example, where you would get the same error:

$item = "Test 1";
$myFnc = function($item) use ($item) {
    ...
}
$myFnc("Test 2");

As you can see, the $item is already used as closure parameter, so you can't us it lexical variable.

bernhardh
  • 3,137
  • 10
  • 42
  • 77
3

It may be a bug, but there is no sense in explicit binding $this to a function anyway as it is automatically bound:

PHP documentation says

As of PHP 5.4.0, when declared in the context of a class, the current class is automatically bound to it, making $this available inside of the function's scope.

Thus, fatal error is thrown in today's version of PHP:

From PHP 7.1, these variables must not include superglobals, $this, or variables with the same name as a parameter.

1234ru
  • 692
  • 8
  • 16
1

I'm using PHP 5.4.25 and actually I am able to use class variables in a closure also with the use keyword as shown below:

class Foo
{
    private $_privateBar = 'private bar';
    protected $_protectedBar = 'protected bar';
    public $_publicBar = 'public bar';

    public function bar()
    {
        $prefix = 'I am a ';

        return function() use ($prefix) {
            echo $prefix . $this->_privateBar . "\n";
            echo $prefix . $this->_protectedBar . "\n";
            echo $prefix . $this->_publicBar . "\n";
        };
    }
}

$foo = new Foo();
$bar = $foo->bar();

$bar();

Output:

I am a private bar
I am a protected bar
I am a public bar
Francesco Casula
  • 26,184
  • 15
  • 132
  • 131
Cristiano Casciotti
  • 1,016
  • 10
  • 21
  • The issue is not that the `use` keyword implicitly prevents use of `$this` - rather why `use ($prefix, $this)`, if used in your example, would be unnecessary/invalid. – tjbp Jul 20 '14 at 11:15
  • 1
    Cristiano's answer looks to me like a will to highlight your inaccurate statement where you wrote: "So it seems this $this can be used if the "use" keyword is simply not specified". According to Cristiano's code (that I've personally tried and it works like a charm) your answer is simply wrong. You should consider flagging this one as the right answer. – Francesco Casula Jul 21 '14 at 10:26
  • Ah, I didn't realise he was responding to my answer rather than my question. Indeed that opening sentence was potentially misleading - it was specific to the original question, in which $this was the only variable being passed, and `function() use ()` is not valid. Fixed. – tjbp Aug 12 '14 at 22:51