Wywołanie zamknięcia przypisanego bezpośrednio do Właściwości obiektu

Chciałbym mieć możliwość wywołania zamknięcia, które przypisuję bezpośrednio do Właściwości obiektu, bez ponownego przypisywania zamknięcia do zmiennej, a następnie wywoływania go. Czy to możliwe?

Poniższy kod nie działa i powoduje Fatal error: Call to undefined method stdClass::callback().

$obj = new stdClass();
$obj->callback = function() {
    print "HelloWorld!";
};
$obj->callback();
Author: hakre, 2010-12-26

11 answers

Od PHP7 możesz zrobić

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

Lub użyć Closure::call () , choć to nie działa na StdClass.


Przed PHP7 musisz zaimplementować metodę magic __call, Aby przechwycić połączenie i wywołać callback (co nie jest oczywiście możliwe dla StdClass, ponieważ nie możesz dodać metody __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');

Zauważ, że nie możesz tego zrobić

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

W ciele __call, ponieważ wywołałoby to __call w nieskończonej pętli.

 74
Author: Gordon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-10 10:42:48

Możesz to zrobić, wywołując _ _ invoke na zamknięciu, ponieważ jest to magiczna metoda, której obiekty używają do zachowania się jak Funkcje:

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

Oczywiście to nie zadziała, jeśli wywołanie zwrotne jest tablicą lub ciągiem znaków (które mogą być również poprawnymi wywołaniami zwrotnymi w PHP) - tylko dla zamknięć i innych obiektów z zachowaniem __invoke.

 97
Author: Brilliand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-28 00:42:46

Od PHP 7 można wykonać następujące czynności:

($obj->callback)();
 15
Author: Korikulum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-29 19:34:41

Wydaje się, że możliwe jest użycie call_user_func().

call_user_func($obj->callback);
Ale nie eleganckie.... To, co mówi @ Gordon, to chyba jedyny sposób.
 7
Author: Pekka 웃,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-26 20:57:57

Cóż, jeśli naprawdę. nalegaj. Innym obejściem byłoby:

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

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

$obj['callback']();
Ale to nie jest najlepsza składnia.

Jednak parser PHP zawsze traktuje T_OBJECT_OPERATOR, IDENTIFIER, ( jako wywołanie metody. Wydaje się, że nie ma obejścia problemu, aby -> ominąć tabelę metod i uzyskać dostęp do atrybutów.

 7
Author: mario,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-26 22:21:52

Wiem, że jest to stare, ale myślę, że cechy dobrze radzą sobie z tym problemem, jeśli używasz PHP 5.4 +

Najpierw Utwórz cechę, która sprawia, że właściwości mogą być wywołane:

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

Wtedy możesz użyć tej cechy w swoich klasach:

class CallableStdClass extends stdClass {
    use CallableProperty;
}

Teraz możesz definiować właściwości za pomocą funkcji anonimowych i wywoływać je bezpośrednio:

$foo = new CallableStdClass();
$foo->add = function ($a, $b) { return $a + $b; };
$foo->add(2, 2); // 4
 5
Author: SteveK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-28 20:22:43

Od PHP 7 zamknięcie można wywołać za pomocą call() "metoda": {]}

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

Od PHP 7 możliwe jest również wykonywanie operacji na dowolnych wyrażeniach (...) (jak wyjaśniono przez Korikulum):

($obj->callback)();

Inne popularne PHP 5 to:

  • Używając magicznej metody __invoke() (Jak wyjaśniono przez )

    $obj->callback->__invoke();
    
  • Korzystanie z call_user_func() function

    call_user_func($obj->callback);
    
  • Użycie zmiennej pośredniej w wyrażeniu

    ($_ = $obj->callback) && $_();
    
Każda droga ma swoje plusy i minusy, ale najbardziej radykalne i ostateczne rozwiązanie nadal pozostaje rozwiązaniem przedstawionym przez Gordona.
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();
 5
Author: Daniele Orlando,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:25

Oto inny sposób na pomyślne wywołanie właściwości obiektu jako zamknięcia.
Jeśli nie chcesz zmieniać obiektu core użyj tego :

$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();
 3
Author: Mohamad Rostami,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-13 14:14:39

Dobrze, należy podkreślić, że przechowywanie zamknięcia w zmiennej i wywołanie zmiennej jest rzeczywiście (dziwnie) szybsze, w zależności od ilości wywołania, staje się to dość dużo, z xdebug (tak bardzo precyzyjny pomiar), mówimy o 1,5 (czynnik, używając zmiennej, zamiast bezpośrednio wywoływać __invoke. więc zamiast tego, po prostu przechowywać zamknięcie w varible i nazwać go.

 2
Author: Kmtdk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-21 15:28:57

Oto inna alternatywa oparta na zaakceptowanej odpowiedzi, ale rozszerzająca stdClass bezpośrednio:

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

Przykład użycia:

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

Prawdopodobnie lepiej będzie używać call_user_func lub __invoke.

 1
Author: Mahn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-08 15:18:11

Jeśli używasz PHP w wersji 5.4 lub wyższej, możesz powiązać wywołanie z zakresem obiektu, aby wywołać niestandardowe zachowanie. Na przykład, jeśli masz mieć następującą konfigurację..

function run_method($object, Closure $method)
{
    $prop = uniqid();
    $object->$prop = \Closure::bind($method, $object, $object);
    $object->$prop->__invoke();
    unset($object->$prop);
}
A Ty operowałeś na takich zajęciach..
class Foo
{
    private $value;
    public function getValue()
    {
        return $this->value;
    }
}

Możesz uruchomić własną logikę tak, jakbyś operował z zakresu swojego obiektu

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

echo $foo->getValue(); // prints "something else"
 0
Author: Lee Davis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-05 15:53:11