php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #68792 Can't declare closure using $this within static method (context)
Submitted: 2015-01-10 17:43 UTC Modified: 2015-05-06 16:27 UTC
From: llmll at gmx dot de Assigned: nikic (profile)
Status: Closed Package: Scripting Engine problem
PHP Version: 5.5.20 OS: any
Private report: No CVE-ID: None
 [2015-01-10 17:43 UTC] llmll at gmx dot de
Description:
------------
The problem arises, when you try to declare a Closure (anonymous function) from within a static method. The compiler breaks on the $this keyword which must not occur in a static method. However, $this is only used in the closure body and should be ignored, therfore.

Test script:
---------------
class Test {
public static function start() {
    $closure = function() {
        echo $this->foo;
    };
    $object = new \StdClass;
    $object->foo = "Hello World";
    $closure->bindTo($object)->__invoke();
}
}
Test::start();

Expected result:
----------------
should echo "Hello World"

Actual result:
--------------
Fatal error: Using $this when not in object context

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-01-10 23:00 UTC] requinix@php.net
-Status: Open +Status: Duplicate
 [2015-01-10 23:00 UTC] requinix@php.net
You missed out on a warning
  Warning: Cannot bind an instance to a static closure in...
making this a duplicate of bug #64761.
 [2015-01-10 23:08 UTC] llmll at gmx dot de
Thanks for your attention. However, the warning is wrong, since the closure is *not* static. It is merely defined in a static method. This shouldn't trigger the warning. The containing element should not have any impact on the closure itself.

Removing the "static" keyword from the enclosing method, makes it work. Adding static, produces the error again. This is clearly incosistent und unexpected, besides it is not documented.

class Test {
public /*static*/ function start() {
    $closure = function() {
        echo $this->foo;
    };
    $object = new \StdClass;
    $object->foo = "Hello World";
    $closure->bindTo($object)->__invoke();
}
}

(new Test)->start();
 [2015-01-12 15:49 UTC] levim@php.net
In any case, your example does not work on any version of PHP I am aware of. In versions  5.4.0 - 5.6.3, I get:

Warning: Cannot bind an instance to a static closure in %s on line 10

Fatal error: Using $this when not in object context in %s on line 6

If you are going to convince me this is a bug then I need proof it ever worked.
 [2015-01-12 16:00 UTC] llmll at gmx dot de
You have to try the second example from my comment. There is a working snippet. Re-enable the commented "static" and it fails. That must not happen, as the static only tags the enclosing method, not the closure itself.

I really begin to wonder if you ever accept something is wrong, bogus, inconsistent or simply stupid. Claiming that a clear bug happens the same way as last year doesn't make it any better for anybody.
 [2015-01-12 21:31 UTC] levim@php.net
> I really begin to wonder if you ever accept something is wrong, bogus, inconsistent or simply stupid. 

You are being hostile. I am the person who made closures in static methods implicitly static as well, and I did so because of a bug report. So yes, I do accept things as bugs. Please be less accusatory in the future.
 [2015-01-12 21:57 UTC] llmll at gmx dot de
I really mean no offense, but this fix of yours is against all practical logic. Someone wanted static closures? Well, closures are static by definition. Atomic, independent pieces of code which can run in any context, thats the whole point of having closures.

Convieniently, PHP allows to bind a closure to some object instance during runtime, which simplifies the use of $this. static:: then should point to the executing class context if available, we know that as late static binding.

What could ever be the reaseon to make a closure depend on the declaring context? That really gives me gray hair. I humbly think this is absolutely wrong and should be re-fixed. However, thank you for reading and spending thoughts on this matter.
 [2015-01-13 02:52 UTC] levim@php.net
> I really mean no offense, but this fix of yours is against all practical logic.

This is still being hostile; please stop.

As for static closures: they already existed. A static closure in PHP means that you don't capture the `$this` context. Again, I did not add this feature.

> What could ever be the reason to make a closure depend on the declaring context?

Look at this case (http://3v4l.org/105nh):

<?php

class A {
    static function foo() {
        $f = function() {
            return static::class;
        };
        return $f();
    }
}

class B extends A {}

var_dump(B::foo());

?>

Before I patched it the result was `string(1) "A"` instead of `string(1) "B"`. Is this any less valid that the code you want to work? I'm not suggesting they can't both be supported, but as it stands it how things are.
 [2015-01-13 02:53 UTC] levim@php.net
-Assigned To: +Assigned To: levim
 [2015-01-13 07:46 UTC] llmll at gmx dot de
Don't take it personal. I will try to present the facts for why your patch fixes the right thing the wrong way. 

static:: implements late static binding, so it always returns the class name of where it is referenced - regardless of the calling context, whether it is from an instance A$->foo() or a class method A::foo(). 

class A {
  public function foo() {
    echo static::class;
  }
}

class B extends A {}

B::foo(); //prints "B"
(new B)->foo(); // prints "B"

Your patch obviously fixed the late static binding problem for closures. But it introduced a wrong behaviour by misinterpreting the static keyword from the containing method. It should work without inspecting it.
 [2015-05-06 15:36 UTC] nikic@php.net
-Status: Duplicate +Status: Open -Assigned To: levim +Assigned To: nikic
 [2015-05-06 16:27 UTC] nikic@php.net
-Status: Assigned +Status: Closed
 [2015-05-06 16:27 UTC] nikic@php.net
This will be supported in PHP 7.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Mar 29 08:01:27 2024 UTC