John Sonderson
John Sonderson

Reputation: 3388

Class assignment of anonymous function to property: calling assigned function from instance fails

I'm trying to run the $greeter function property of the $greeter instance of the Greeter class. I've read the answers from this related post but could not get them to work (the post also mentions _call, traits, stdClass, returning a function from a function (which doesn't make sense to me why this works without having to call twice), and the given solutions seem like overkill for the simple thing I'm trying to achieve). Perhaps my case is a little different. I don't understand why the parser messes up.

class Greeter {

  private $greeter;

  function __construct() {

    $this->greeter = function() {
      echo "Hello!\n";
    };

  }

  public function greet() {

    $this->greeter();

  }

}

// THIS WORKS AS EXPECTED:

$hello = function() { echo "Hi!\n"; };
$hello();

$greeter = new Greeter();

// NEITHER OF THESE WORK:

call_user_func($greeter->greet);

$greeter->greet();

$greeter['greet']();

OUTPUT:

Hi!

<br />
<b>Warning</b>:  call_user_func() expects parameter 1 to be a valid callback, no array or string given on line <b>30</b><br />
<br />
<b>Fatal error</b>:  Call to undefined method Greeter::greeter() on line <b>15</b><br />

Upvotes: 1

Views: 127

Answers (2)

Aitch
Aitch

Reputation: 1697

Welcom to funny PHP.

<?php
    class A {
        public function f() {
            echo 'hi';
        }
    }

    $a = new A();

    $a->f(); // yes
    call_user_func($a->f); // no $a->f is not a func pointer in PHP
    call_user_func([$a, 'f']); // yes [$obj, $method_string] is callable
    $c = [$a, 'f'];
    $c(); // yes it's a callable

    [$a, 'f'](); // no PHP don't like that

    $c = function() use($a) { $a->f(); };
    $c(); // yes

    function() use($a) { $a->f(); }(); // no
    (function() use($a) { $a->f(); })(); // no

    // edit: there is more fun I forgot
    $m = 'f';
    $a->$m(); // yes

    $a->{'greet'}(); // yes

Well, it's not easy to understand what PHP is doing sometimes, but there are many cases you can't write in one expression.

Same for empty($this->getNumber()) or in the old version with array dereference $this->getArray()[4].

By the way you meant the closing >> vs. > > in C++ templates which were parsed as bitshift operator, but now is fine with C++11.

Upvotes: 1

John Sonderson
John Sonderson

Reputation: 3388

OK, so this works, but why do I need to use call_user_func at all? Is it a problem with the PHP grammar, that for some reason the parser is having problems? C++ used to have a problem parsing << used with nested std::maps, and at a time it was necessary to write < < to avoid the problem. Then a trick in the grammar was introduced to fix the problem. I don't see why the same thing can't happen in the PHP grammar to make call_user_func unnecessary.

class Greeter {

  private $greeter;

  function __construct() {

    $this->greeter = function() {
      echo "Hello!\n";
    };

  }

  public function greet() {

    call_user_func($this->greeter);

  }

}

// THIS WORKS AS EXPECTED:

$hello = function() { echo "Hi!\n"; };
$hello();

$greeter = new Greeter();

// NOW THIS ALSO WORKS AS EXPECTED:

$greeter->greet();

Upvotes: 1

Related Questions