2010-06-21 15 views
5

Ostatnio w projekcie pracuję nad, musiałem przechowywać zwrotnych w statycznej tablicy członkowskim, tak jak poniżej:oddzwaniania jako części statycznej tablicy członkiem

class Example { 
    private static $_callbacks = array(
     'foo'=>array('Example','do_foo'), 
     'bar'=>array('Example','do_bar') 
    ); 
    private static function do_foo() { } 
    private static function do_bar() { } 
} 

do nich zadzwonić, Próbowałem oczywiste (może nawet naiwne) Składnia (wewnątrz klasy Example):

public static function do_callbacks() { 
    self::$_callbacks['foo'](); 
    self::$_callbacks['bar'](); 
} 

Ku mojemu zaskoczeniu, to nie działa, powodując w zawiadomieniu, że jestem uzyskiwania dostępu do zmiennej niezdefiniowanej i fatalnym błędzie twierdząc, że self::$_callbacks['foo'] musiała być na żądanie.

Następnie próbowałem call_user_func:

public static function do_callbacks() { 
    call_user_func(self::$_callbacks['foo']); 
    call_user_func(self::$_callbacks['bar']); 
} 

I udało się!

Moje pytanie brzmi:

Dlaczego muszę korzystać call_user_func jako pośrednik, a nie bezpośrednio do nich zadzwonić?

Odpowiedz

4

Nie można wywoływać wywołań zwrotnych, dodając (). Działa to tylko w PHP 5.3 z funkcjami lambda i obiektami, które implementują magię __invoke (patrz także wewnętrzna obsługa obiektu get_closure).

Po pierwsze, wbrew temu, co można powiedzieć, to nie działa:

<?php 
class Example { 
    private static $_callbacks; 
    private static function do_foo() { echo "foo"; } 
    private static function do_bar() { echo "bar"; } 

    public static function do_callbacks() { 
     self::$_callbacks['foo'] = array('Example','do_foo'); 
     self::$_callbacks['bar'] = array('Example','do_bar'); 

     self::$_callbacks['foo'](); 
     self::$_callbacks['bar'](); 
    } 
} 
Example::do_callbacks(); 

Ale to nie będzie działać, jeśli nawet był self::$_callbacks['foo'] lambda:

<?php 
class Example { 
    private static $_callbacks; 

    public static function do_callbacks() { 
     self::$_callbacks['foo'] = function() { echo "foo"; }; 

     self::$_callbacks['foo'](); 
    } 
} 

Example::do_callbacks(); 

Powodem jest parser. Powyższe kompiluje do:

 
Class Example: 
Function do_callbacks: 
(...) 
number of ops: 16 
compiled vars: !0 = $_callbacks 
line  # * op       fetch   ext return operands 
--------------------------------------------------------------------------------- 
    6  0 > EXT_NOP             
    7  1  EXT_STMT             
     2  ZEND_FETCH_CLASS           
     3  ZEND_DECLARE_LAMBDA_FUNCTION        '%00%7Bclosure%7D%2Ftmp%2Fcp9aicow0xb7fcd09b' 
     4  FETCH_W      static member  $2  '_callbacks' 
     5  ZEND_ASSIGN_DIM           $2, 'foo' 
     6  ZEND_OP_DATA            ~3, $4 
    9  7  EXT_STMT             
     8  FETCH_DIM_R          $5  !0, 'foo' 
     9  ZEND_FETCH_CLASS           
     10  ZEND_INIT_STATIC_METHOD_CALL        $6, $5 
     11  EXT_FCALL_BEGIN           
     12  DO_FCALL_BY_NAME        0   
     13  EXT_FCALL_END            
    10 14  EXT_STMT             
     15 > RETURN             null 

Nigdy nie jest pobieranie statycznego elementu (z wyjątkiem przypisania lambda). W rzeczywistości PHP kompiluje zmienną $_callbacks, która okazuje się nie istnieć w środowisku wykonawczym; stąd twój błąd. Przyznaję, że to, może nie błąd, ale przynajmniej narożny przypadek parsera. Najpierw ocenia część $_callbacks['foo'], a następnie próbuje wywołać funkcję statyczną, której nazwa wynika z tej oceny.

Podsumowując - trzymaj się call_user_func lub forward_static_call.

+0

Oh wow, masz rację: to nie działa! To naprawdę dziwne, ponieważ kiedy początkowo próbowałem dowiedzieć się, dlaczego mój oryginał nie działał, spróbowałem tego i zadziałało. W takim razie wyciągnę to z mojego pytania. –

2

Ponieważ w PHP funkcje nie są tak naprawdę typem danych, ponieważ są w bardziej funkcjonalnych językach (dlatego są określone w stosunkowo niezręcznej składni tablicy - nawet create_function() zwraca tylko wygenerowaną nazwę funkcji, a nie wskaźnik funkcji). A więc, call_user_func i jego bracia są workaroundami (hackami?), Aby umożliwić jakąś formę przekazywania funkcji/wywołania zwrotnego.

+0

To jeden z powodów. Drugi jest taki, że skrypt nie jest przetwarzany tak, jak myśli. – Artefacto

0

Jeśli używasz PHP 5.3, a wywołania zwrotne są zawsze statyczne funkcje klasy, a następnie można użyć tego kodu:

public static function do_callbacks() { 

    // Those can be defined in another function, or in the constructor. 
    // self::$_callbacks['foo'] = array('Example','do_foo'); 
    // self::$_callbacks['bar'] = array('Example','do_bar'); 

    $class = self::$_callbacks['foo'][0]; 
    $method = self::$_callbacks['foo'][1]; 
    $class::$method(); 

    $class = self::$_callbacks['bar'][0]; 
    $method = self::$_callbacks['bar'][1]; 
} 

W ten sposób, nie trzeba używać call_user_func(). To ma sens, aby użyć tej metody, jeśli wywołania zwrotne są zawsze tego samego typu (w tym przypadku były to statyczne metody klasy, której nazwa została podana jako pierwszy element tablicy).Jeśli musisz wziąć pod uwagę wszystkie możliwe rodzaje wywołań zwrotnych, które może obsługiwać telefon, wówczas lepiej jest użyć call_user_func(); Kod PHP nie może być szybszy niż kod C używany do implementacji rozszerzeń PHP.

+0

Tak, rozważałem to, ale muszę poprzeć wszystko, co 'is_callable()'. –

Powiązane problemy