2

I have experienced something that I think it's a bug, but I'm not sure. So I come here to ask to the people who know more about this than me, is this a PHP bug? Look at the following lines:

<h1>Tests</h1>

<?php
class Foo
  {
  public function __invoke()
    {
    return 'Called correctly';
    }
  }

class Boo
  {
  public function __construct()
    {
    $this->Foo = new Foo();
    }
  }

$Foo = new Foo();
echo $Foo();

echo "<br><br><hr><br><br>";

$Boo = new Boo();
echo $Boo->Foo();

According to the specification, since The __invoke() method is called when a script tries to call an object as a function, I should obtain this:

Called correctly


Called correctly

But the last echo is not executed. Instead, I get this:

PHP Fatal error: Call to undefined method Boo::Foo() in [...]/index.php on line 26

Is this a bug or an expected behaviour? If it's a bug, some help about how to submit it would be highly appreciated.

EDIT based on Jon answer to show further the ugly behaviour

class Doo
  {
  public function __construct()
    {
    // The Foo class is the same as the one defined before

    $Test = new Foo();
    echo $Test();      // Echos correctly

    echo "<br><br><hr><br><br>";

    $this->Foo = $Test;
    echo $this->Foo();  // Error. Heisenbehaviour?
    }
  }

$Boo = new Doo();
Francisco Presencia
  • 8,732
  • 6
  • 46
  • 90

2 Answers2

1

It is not a bug, and it is not limited to Foo being an object that defines __invoke. You would get the same error with any callable:

class Foo
{
    private $bar = 'rand';

    public function __construct()
    {
        $this->bar(); // won't work
    }  
}

You need to either write this as two lines, or use call_user_func:

// #1
$Boo = new Boo();
$Foo = $Boo->Foo;
$Foo();

// #2
$Boo = new Boo();
call_user_func($Boo->Foo);
Jon
  • 428,835
  • 81
  • 738
  • 806
  • But `bar` IS only a property. According to the link to the documentation I put, the object `Foo` within `Boo` will call its own method `__invoke()` if called as a function. There's nothing that makes `bar` behave also as a function... – Francisco Presencia Apr 08 '13 at 21:12
  • And that's why the workarounds work. But think about the repercussions of what you are suggesting: when should the compiler treat this construct as you propose to? Obviously not when there is a method `Bar::Foo`, so that leaves only the case where there is no such method. You are suggesting that the same syntax could end up doing one of two different things depending on the presence or absence of a method? That doesn't sound like a good idea. Also, what should happen if there is neither a property nor a method? Should the compiler invoke `__call` or `__get`? – Jon Apr 08 '13 at 21:15
  • I know it's not a good idea, but is it better than the behaviour of the edit I just made? An object that changes behaviour depending on the place it's stored? – Francisco Presencia Apr 08 '13 at 21:23
  • @FrankPresenciaFandos: I agree that it is ugly, but there are two ways to parse this and the compiler just has to choose one or the other. Would you prefer it if there were a proper method `Foo()` and it was not called because there is also a property named `Foo`? – Jon Apr 08 '13 at 21:25
  • Definitely not... okay, then it's not a bug, just a really ugly working feature which is not clear in the specifications. – Francisco Presencia Apr 08 '13 at 21:29
1

See also class variable functions

It has exactly the same problem. The PHP lexer always recognizes the syntax variable T_OBJECT_OPERATOR T_STRING function_call_parameter_list as a method call.

Community
  • 1
  • 1
bwoebi
  • 23,637
  • 5
  • 58
  • 79