Закрытие вызова, присвоенное объекту напрямую

Я хотел бы иметь возможность вызвать замыкание, которое я назначаю прямому свойству объекта, не переназначая замыкание переменной, а затем вызываю ее. Это возможно?

Код ниже не работает и вызывает Fatal error: Call to undefined method stdClass::callback() .

 $obj = new stdClass(); $obj->callback = function() { print "HelloWorld!"; }; $obj->callback(); 

Solutions Collecting From Web of "Закрытие вызова, присвоенное объекту напрямую"

Начиная с PHP7, вы можете сделать

 $obj = new StdClass; $obj->fn = function($arg) { return "Hello $arg"; }; echo ($obj->fn)('World'); 

или используйте Closure :: call () , хотя это не работает на StdClass .


Перед PHP7 вам нужно будет внедрить метод __call для перехвата вызова и вызвать обратный вызов (что, конечно, невозможно для StdClass , потому что вы не можете добавить метод __call )

 class Foo { public function __call($method, $args) { if(is_callable(array($this, $method))) { return call_user_func_array($this->$method, $args); } // else throw exception } } $foo = new Foo; $foo->cb = function($who) { return "Hello $who"; }; echo $foo->cb('World'); 

Обратите внимание, что вы не можете

 return call_user_func_array(array($this, $method), $args); 

в теле __call , потому что это вызовет __call в бесконечном цикле.

Вы можете сделать это, вызвав __invoke при закрытии, так как это магический метод, который объекты используют для работы как функции:

 $obj = new stdClass(); $obj->callback = function() { print "HelloWorld!"; }; $obj->callback->__invoke(); 

Конечно, это не сработает, если обратный вызов является массивом или строкой (которая также может быть действительным обратным вызовом в PHP) – только для замыканий и других объектов с поведением __invoke.

Начиная с PHP 7 вы можете сделать следующее:

 ($obj->callback)(); 

Кажется, что возможно использовать call_user_func() .

 call_user_func($obj->callback); 

не изящно, хотя …. То, что говорит @Gordon, вероятно, единственный способ пойти.

Ну, если вы действительно настаиваете. Другим обходным решением будет следующее:

 $obj = new ArrayObject(array(),2); $obj->callback = function() { print "HelloWorld!"; }; $obj['callback'](); 

Но это не самый приятный синтаксис.

Тем не менее, парсер PHP всегда рассматривает T_OBJECT_OPERATOR , IDENTIFIER ( как вызов метода. Кажется, что обходной путь для создания -> обход таблицы методов и доступ к атрибутам.

Я знаю, что это старо, но я думаю, что черты прекрасно справляются с этой проблемой, если вы используете PHP 5.4+

Во-первых, создайте свойство, которое делает свойства вызываемыми:

 trait CallableProperty { public function __call($method, $args) { if (property_exists($this, $method) && is_callable($this->$method)) { return call_user_func_array($this->$method, $args); } } } 

Затем вы можете использовать эту черту в своих классах:

 class CallableStdClass extends stdClass { use CallableProperty; } 

Теперь вы можете определить свойства через анонимные функции и вызвать их напрямую:

 $foo = new CallableStdClass(); $foo->add = function ($a, $b) { return $a + $b; }; $foo->add(2, 2); // 4 

Начиная с PHP 7 замыкание можно вызвать с помощью метода call() :

 $obj->callback->call($obj); 

Поскольку PHP 7 также может выполнять операции над произвольными (...) выражениями (как объяснил Корикулум ):

 ($obj->callback)(); 

Другими распространенными подходами PHP 5 являются:

  • используя магический метод __invoke() (как объяснил Бриллианд )

     $obj->callback->__invoke(); 
  • с помощью функции call_user_func()

     call_user_func($obj->callback); 
  • используя промежуточную переменную в выражении

     ($_ = $obj->callback) && $_(); 

Каждый путь имеет свои плюсы и минусы, но наиболее радикальным и окончательным решением по-прежнему остается тот, который был представлен Гордоном .

 class stdKlass { public function __call($method, $arguments) { // is_callable([$this, $method]) // returns always true when __call() is defined. // is_callable($this->$method) // triggers a "PHP Notice: Undefined property" in case of missing property. if (isset($this->$method) && is_callable($this->$method)) { return call_user_func($this->$method, ...$arguments); } // throw exception } } $obj = new stdKlass(); $obj->callback = function() { print "HelloWorld!"; }; $obj->callback(); 

Вот еще один способ успешного вызова свойств объекта как закрытия.
Если вы не хотите менять основной объект, используйте это:

 $obj = new AnyObject(); // with or without __invoke() method $obj->callback = function() { return function () { print "HelloWorld!"; }; }; $obj->callback(); 

ОБНОВИТЬ:

 $obj = new AnyObject(); // with or without __invoke() method $obj->callback = function() { print "HelloWorld!"; }; $callback = $obj->callback; $callback(); 

Нужно подчеркнуть, что сохранение замыкания в переменной и вызов varible на самом деле (wierdly) быстрее, в зависимости от суммы вызова, он становится довольно много, с xdebug (так очень точное измерение), мы говорим о 1,5 (коэффициент, используя varible, вместо прямого вызова __invoke, поэтому вместо этого просто сохраните закрытие в varible и вызовите его.

Если вы используете PHP 5.4 или выше, вы можете привязать вызываемый к области вашего объекта вызов вызываемого пользовательского поведения. Так, например, если бы у вас была следующая настройка.

 function run_method($object, Closure $method) { $prop = uniqid(); $object->$prop = \Closure::bind($method, $object, $object); $object->$prop->__invoke(); unset($object->$prop); } с function run_method($object, Closure $method) { $prop = uniqid(); $object->$prop = \Closure::bind($method, $object, $object); $object->$prop->__invoke(); unset($object->$prop); } 

И вы работали в таком классе.

 class Foo { private $value; public function getValue() { return $this->value; } } 

Вы можете запустить свою собственную логику, как если бы вы работали в рамках вашего объекта

 $foo = new Foo(); run_method($foo, function(){ $this->value = 'something else'; }); echo $foo->getValue(); // prints "something else" 

Вот еще одна альтернатива, основанная на принятом ответе, но прямо распространяющая stdClass:

 class stdClassExt extends stdClass { public function __call($method, $args) { if (isset($this->$method)) { $func = $this->$method; return call_user_func_array($func, $args); } } } 

Пример использования:

 $foo = new stdClassExt; $foo->blub = 42; $foo->whooho = function () { return 1; }; echo $foo->whooho(); 

Скорее всего, вам лучше использовать call_user_func или __invoke .