Вызов замыкания, назначенный непосредственно свойству объекта

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

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

$obj = new stdClass();
$obj->callback = function() {
    print "HelloWorld!";
};
$obj->callback();
89 голосов | спросил Kendall Hopkins 26 SunEurope/Moscow2010-12-26T23:40:03+03:00Europe/Moscow12bEurope/MoscowSun, 26 Dec 2010 23:40:03 +0300 2010, 23:40:03

10 ответов


0

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

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

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


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

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 в бесконечном цикле.

ответил Gordon 26 SunEurope/Moscow2010-12-26T23:55:45+03:00Europe/Moscow12bEurope/MoscowSun, 26 Dec 2010 23:55:45 +0300 2010, 23:55:45
0

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

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

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

ответил Brilliand 28 FriEurope/Moscow2012-12-28T04:42:46+04:00Europe/Moscow12bEurope/MoscowFri, 28 Dec 2012 04:42:46 +0400 2012, 04:42:46
0

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

($obj->callback)();
ответил Korikulum 29 J000000Wednesday15 2015, 22:34:41
0

Это представляется возможным при использовании call_user_func().

call_user_func($obj->callback);

не элегантно, хотя .... То, что говорит @ Гордон, вероятно, единственный путь.

ответил Pekka 웃 26 SunEurope/Moscow2010-12-26T23:57:57+03:00Europe/Moscow12bEurope/MoscowSun, 26 Dec 2010 23:57:57 +0300 2010, 23:57:57
0

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

$obj = new ArrayObject(array(),2);

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

$obj['callback']();

Но это не самый хороший синтаксис.

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

ответил mario 27 MonEurope/Moscow2010-12-27T01:21:52+03:00Europe/Moscow12bEurope/MoscowMon, 27 Dec 2010 01:21:52 +0300 2010, 01:21:52
0

Я знаю, что это старый, но я думаю, что Traits прекрасно справятся с этой проблемой, если вы используете 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
ответил SteveK 29 AM000000120000004331 2014, 00:22:43
0

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

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

UPDATE:

$obj = new AnyObject(); // with or without __invoke() method
$obj->callback = function() {
     print "HelloWorld!";
};
$callback = $obj->callback;  
$callback();
ответил Mohamad Rostami 24 PM00000060000004431 2014, 18:59:44
0

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

ответил Kmtdk 21 PMpSun, 21 Apr 2013 19:28:57 +040028Sunday 2013, 19:28:57
0

Вот еще одна альтернатива, основанная на принятом ответе, но с непосредственным расширением 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 хотя.

ответил Mahn 8 J000000Tuesday14 2014, 19:18:11
0

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

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"
ответил Lee Davis 5 PM00000070000001131 2013, 19:53:11

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132