Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP Magic __invoke upon object-property of a class

Consider this class arrangement - and in particular the magic function __invoke:

class Barman {  
    public function __construct() {
        // .. .constructor stuff - whatever
    }

    public function makeDrink() {
        return "vodka martini, shaken";
    }   
}

class Bar { 
    private $arr_barmen = array();

    public function __construct() {
        $this->arr_barmen['john'] = new Barman();
    }

   public function __invoke($barman_id) {
      echo "I have been invoked";
      return $this->arr_barmen[$barman_id];
   }

   public function aBarFunc($param) {
      return "yes it worked ," .$param;
   }
}

class Foo {

    public $myBar;

    public function __construct() {
        $this->myBar = new Bar();
    }

}

I want to write syntax like this

$company = new Foo();
$company->myBar('john')->makeDrink();

Preferred result: "vodka martini, shaken"

Actual result:
"Call to undefined method Foo::myBar()"

Invoking myBar() with the magic method should return a barman Object upon which you can call any of the barman's public methods

But now consider this (which does work)

$company = new Foo();
$myBar = $company->myBar;
$drink = $myBar('john')->makeDrink();
echo $drink;

// Result:
// I have been invoked 
// vodka martini, shaken

So what's going on? I don't like that workaround - it's not sleek. I need it to work this way:

$company->myBar('john')->makeDrink();

Please help? :-)

like image 855
Maz Avatar asked Dec 10 '25 17:12

Maz


2 Answers

I believe you can just add braces around it:

$company = new Foo();
$drink = ($company->myBar)('john')->makeDrink();
echo $drink; // vodka martini, shaken
like image 177
tmas Avatar answered Dec 12 '25 10:12

tmas


This is being caused by an ambiguity in the call you're trying to make:

$company->myBar('john')->makeDrink();

Because myBar is a property, the PHP interpreter isn't expecting it to be callable. It is parsing it as an attempt to call a method called myBar() which doesn't exist, and is thus throwing the error.

The direct way to resolve this is to clarify the ambiguity for the interpreter. You do this by adding curly braces around the property, as follows:

$company->{myBar}('john')->makeDrink();

The code is now explicit that myBar is a property and should be accessed as such, but that it contains a value that is callable and that you wish to make that call.

This whole topic is complicated (slightly) by the fact that PHP 5.x and PHP 7.x behave differently with regard to how they default to handling these kinds of ambiguity. PHP 7 changed the defaults in order to correct some internal inconsistencies within the language. The result is that in situations like this where you have an ambiguity, if you want your code to work across both PHP 5.x and 7.x, you should always use the braces to explicitly define how you want it to work, regardless of whether your code works for you without them.

There is some documentation about this change in the PHP 7.0 upgrade notes, although the examples given don't cover your exact situation.

like image 26
Spudley Avatar answered Dec 12 '25 10:12

Spudley



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!