1

Am I missing something or do closures simply not work as class methods? Take this for instance:

$foo = new stdClass();
$foo->bar = function() {
   echo '@@@';
};
$foo->bar();

Seems to give me an error of "Fatal error: Call to undefined method stdClass::bar() in /blah/blah.php on line X"

Shouldn't this instead invoke the closure that was placed in the "bar" property?

dqhendricks
  • 19,030
  • 11
  • 50
  • 83

2 Answers2

5

Yes, that is indeed correct.

The only way to call bar is:

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

Sad, but true.

Also worth noting, because of this same effect, there is no $this inside $bar call (unless you pass it as function argument named as $this).

Edit: As pointed out, the value of $this inside the closure is the same value of the scope of when the closure was created. This may mean that $this might be undefined on two occasions: when the scope was the global PHP scope or when the scope was from a static context. This, however, means that you can in theory feed the correct instance:

class Foo {
    public $prop = 'hi';
    function test(){
        $this->bar = function(){
            echo $this->prop;
        }

        $bar = $this->bar;
        $bar();
    }
}

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

Also, it seems that with some class magic, you can achieve $this->bar() as well:

class Foo {
    // ... other stuff from above ...
    public function __call($name, $args){
        $closure = $this->$name;
        call_user_func_array( $closure, $args ); // *
    }
}

[*] Beware that call_user_func_array is very slow.

Oh, and this is strictly for PHP 5.4 only. Before that, there's no $this in closures :)

Also, you can see it in action here.

Christian
  • 27,509
  • 17
  • 111
  • 155
1

Methods and fields are completely separate; in fact, you can even have a method and field of the same name:

<?php
class foo{
    function bar() { echo "hello\n"; }
}
$object = new foo;
$object->bar = 1;
$object->bar(); // echoes "hello"
?>

This explains why your syntax could not have created a "real" method.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • I didn't expect it to create a "real" method, but I did expect it to let me invoke the closure within the property. – dqhendricks Oct 25 '11 at 14:33
  • @dqhendricks: then there would be no syntactical difference between calling the method `bar` and calling the function stored in the field `bar`. So it would be ambiguous what you were doing – newacct Nov 01 '11 at 00:05
  • you are correct. this would cause confusion. this would have been a very useful feature in my particular case however, but i have found a way to implement the same functionality without it. – dqhendricks Nov 01 '11 at 01:12