2

Working a lot with JS I have come to love closures, so I was pleased to learn that there are closures in PHP also. However I just can't get this stuff to work, what's wrong with this piece of code?

class Foo {
    public $Bar;
    public function Foo() {
        $this->Bar = function() { echo "Hello World"; };
    }
};

$F = new Foo();
$F->Bar();

I keep getting PHP Fatal error: Call to undefined method Foo::Bar() errors.

JJJ
  • 32,902
  • 20
  • 89
  • 102
PaulK
  • 623
  • 2
  • 10
  • 22
  • 4
    (a) What you have is an anonymous function, not a closure. (b) Which PHP version are you using? This was introduced in PHP 5.3. – Felix Kling Oct 31 '11 at 10:58
  • 2
    I believe it has to be >=5.3 as there would have been a parsing error otherwise – jlb Oct 31 '11 at 10:59
  • @jlb: Right, just noticed that.. – Felix Kling Oct 31 '11 at 11:00
  • oops yeah that's what I meant then Felix ;) I'm on version 5.3.6 – PaulK Oct 31 '11 at 11:18
  • FYI: A php class can have a property and function by the same name, so trying to access a property as a function via $this->propname() doesn't adhere to the rules. – jlb Oct 31 '11 at 11:20
  • On a side note; If you're using PHP 5+, you should really name your constructor method `__construct` instead of `Foo` :-) While this still works in some cases, it would actually break if you introduce a namespace here. – jgivoni Jul 01 '12 at 14:00

4 Answers4

5

This has been discussed a lot on SO already (see e.g. this answer). This should do the trick:

$b = $f->Bar;
$b();

Yes, it is that stupid. You could use call_user_func() to put in in one line (see jlb's answer to this question), but the ugliness remains.

Community
  • 1
  • 1
middus
  • 9,103
  • 1
  • 31
  • 33
  • 1
    Nice. So much of PHP's class system feels very, say, "ad-hoc", that at least the fact that you should need such a workaround is not a surprise in itself... – Kerrek SB Oct 31 '11 at 11:04
  • 1
    @KerrekSB I agree. These two lines of code are an abomination. – middus Oct 31 '11 at 11:06
  • Yep, the two lines ARE an abomination.. try my solution :) – jlb Oct 31 '11 at 11:07
  • 1
    @jlb Which is basically the same, but in one line. Doesn't make the fact that `$f->Bar()` does not work any better. :/ – middus Oct 31 '11 at 11:08
  • @middus not quite sure what your point is.. call_user_func removes the need for the temporary variable $b – jlb Oct 31 '11 at 11:11
  • 1
    @jlb Yes, but why should you need to call another function (i.e. `call_user_func`) in order to call your own function? Because you have to work around the ridiculous shortcomings of php. This is sad. Not on your part, but on php's. – middus Oct 31 '11 at 11:13
  • Oh that's really weird. Thanks for the help :) – PaulK Oct 31 '11 at 11:13
  • @middus I'm not exactly sure how this is a shortcoming (see my comment on the question itself) – jlb Oct 31 '11 at 11:23
  • @jlb: I think the very fact that PHP distinguishes between "functions" and "user functions" is a sign that something has been "tacked on". The one-liner is probably preferable to this version, but I'm also glad to know that it's possible to call a "user function" with the usual function syntax using this workaround. Oh well. – Kerrek SB Oct 31 '11 at 11:41
  • @KerrekSB I've never distinguished between functions and user-functions.. what is the difference? – jlb Oct 31 '11 at 14:14
  • @jlb: Well, the fact that you need to invoke `call_user_func` to call a *user* function, but just round parentheses to call a "normal" function -- the fact that those two are distinct gives the whole idea a tacked on feel, non? – Kerrek SB Oct 31 '11 at 14:17
  • @KerrekSB you can call normal and user (i take it you mean user-defined..) functions via call_user_func. call_user_func's purpose is to provide an interface to dynamically determine which function to call (ie. and reduce control statements) – jlb Oct 31 '11 at 14:22
2

If you want a one-line solution to replace

$F->Bar()

try this:

call_user_func($F->Bar);
jlb
  • 19,090
  • 8
  • 34
  • 65
1

PHP has separation between methods and fields. In fact, you can have a method and a field of the same name at the same time:

class Foo {
    public $Bar;
    function Bar() { echo "hello\n"; }
};

$F = new Foo();
$F->Bar = 42;
$F->Bar(); // echoes "hello"

So you can see that, to avoid ambiguity, there must be a separate syntax between calling a method with that name, and accessing a field with that name and then calling that as a function.

If PHP had better syntax, they would support ($F->Bar)(), i.e. function call operator on any expression, but currently only variables can be "called".

newacct
  • 119,665
  • 29
  • 163
  • 224
0

PHP isn't liking the $F->Bar notation for accessing the closure.

If you change this slightly to

$t = $F->Bar();
$t();

then it works.

Phil Lello
  • 8,377
  • 2
  • 25
  • 34