PHP Magic __invoke для объекта-свойства класса

Рассмотрим это расположение классов — и, в частности, магическую функцию __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();
}

}

Я хочу написать такой синтаксис

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

Предпочтительный результат:
«водка мартини, шейк»

Фактический результат:
«Вызов неопределенного метода Foo :: myBar ()»

Вызов myBar () с помощью магического метода должен вернуть объект бармена, для которого вы можете вызвать любой из открытых методов бармена.

Но теперь рассмотрим это (что работает)

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

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

Так, что происходит? Мне не нравится этот обходной путь — он не гладкий.
Мне нужно, чтобы это работало так:

$ Компани> myBar ( ‘Джон’) -> makeDrink ();

Пожалуйста помоги? 🙂

0

Решение

Для достижения цепочки вы должны вернуть объект Barman в 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] = new Barman();
}

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

}

class Foo {

public $myBar;

public function __construct() {
$this->myBar = new Bar();
}
// create a function with variable name to invoke the object
public function myBar($name) {
$mybar = $this->myBar;
return $mybar($name);
}

}
1

Другие решения

Это вызвано двусмысленностью звонка, который вы пытаетесь сделать:

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

Так как myBar это свойство, интерпретатор PHP не ожидает его вызова. Это анализирует как попытку вызвать метод myBar() который не существует, и, следовательно, выдает ошибку.

Прямой способ решить эту проблему — прояснить неоднозначность для переводчика. Это можно сделать, добавив фигурные скобки вокруг свойства следующим образом:

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

Код теперь явно myBar является свойством и должно быть доступно как таковое, но оно содержит значение, которое можно вызвать, и которое вы хотите сделать этот вызов.

Вся эта тема усложняется (немного) тем фактом, что PHP 5.x и PHP 7.x ведут себя по-разному в отношении того, как они по умолчанию обрабатывают такие неоднозначности. PHP 7 изменил значения по умолчанию, чтобы исправить некоторые внутренние несоответствия в языке. В результате в таких ситуациях, когда возникает неоднозначность, если вы хотите, чтобы ваш код работал как на PHP 5.x, так и на 7.x, вы всегда должны использовать фигурные скобки, чтобы явно определить, как вы хотите, чтобы он работал, независимо от работает ли ваш код у вас без них.

Есть некоторая документация об этом изменении в примечания по обновлению PHP 7.0, хотя приведенные примеры не охватывают вашу точную ситуацию.

1

Спасибо за ответы. Я разработал симпатичный обходной путь следующим образом:

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

class Bar {
private $arr_barmen = array();
public function __construct() {
$this->arr_barmen['john'] = new Barman();
}
public function getBarman($barman_id) {
return $this->arr_barmen[$barman_id];
}
public function __invoke($barman_id) {
echo "I have been invoked \n";
return $this->arr_barmen[$barman_id];
}
}

class Foo {
private $_myBar;
public function __construct() {
$this->_myBar = new Bar();
}
// The fix
public function myBar($barman_id) {
return $this->_myBar->getBarman($barman_id);
}
}

Использование:

$company = new Foo();
$drink = $company->myBar('john')->makeDrink();
echo $drink; // vodka martini, shaken

Как это устроено?
Foo-> myBar становится приватным (Foo -> $ _ myBar);

внутри Foo мы создаем публичную функцию с именем myBar;

мы создаем функцию getBarman внутри Bar, которая вызывается из Foo-> myBar (‘john’)

Еще несколько шагов — и теперь нет никакой двусмысленности — Foo-> myBar () всегда является функцией.

ура

M

0