2011-01-03 16 views
8

ho bisogno di implementare il seguente schema in php:Il modo corretto di fare delegati o callback in PHP

class EventSubscriber 
{ 
    private $userCode; 
    public function __construct(&$userCode) { $this->userCode = &$userCode; } 
    public function Subscribe($eventHandler) { $userCode[] = $eventHandler; } 
} 

class Event 
{ 
    private $subscriber; 
    private $userCode = array(); 

    public function __construct() 
    { 
     $this->subscriber = new Subscriber($this->userCode) 
    } 

    public function Subscriber() { return $this->subscriber; } 

    public function Fire() 
    { 
     foreach ($this->userCode as $eventHandler) 
     { 
      /* Here i need to execute $eventHandler */ 
     } 
    } 
} 

class Button 
{ 
    private $eventClick; 

    public function __construct() { $this->eventClick = new Event(); } 

    public function EventClick() { return $this->eventClick->Subscriber(); } 

    public function Render() 
    { 
     if (/* Button was clicked */) $this->eventClick->Fire(); 

     return '<input type="button" />'; 
    } 
} 

class Page 
{ 
    private $button; 

    // THIS IS PRIVATE CLASS MEMBER !!! 
    private function ButtonClickedHandler($sender, $eventArgs) 
    { 
     echo "button was clicked"; 
    } 

    public function __construct() 
    { 
     $this->button = new Button(); 
     $this->button->EventClick()->Subscribe(array($this, 'ButtonClickedHandler')); 
    } 

    ... 

}  

qual è il modo corretto per farlo.

P.S.

Stavo usando call_user_func a tale scopo e credo sia stato in grado di chiamare membri della classe privata, ma dopo alcune settimane di sviluppo ho scoperto che ha smesso di funzionare. È stato un bug nel mio codice o è stato qualcos'altro che mi ha fatto pensare che 'call_user_func' è in grado di chiamare funzioni di classe privata, non lo so, ma ora sto cercando un metodo semplice, veloce ed elegante di sicuro chiamare il proprio membro di classe privato da un'altra classe. Sto cercando di chiudere in questo momento, ma ho problemi con '$ this' all'interno di chiusura ...

+1

'$ this' non è supportata in Chiusure (come indicato nel manuale) e membri privati ​​sono privati, perché l'uno, che li ha dichiarati come privati, non li vogliono essere chiamati da nessun oggetto di una classe diversa. Questa è la definizione di "membro privato";) – KingCrunch

+0

Sì, i membri privati ​​sono privati ​​perché sono privati ​​:) Ma immagina di dire al tuo pulsante 'Ehi, esegui il mio codice privato in "ButtonClickedHandler" quando clicchi' . In questo caso non vedo perché il pulsante dovrebbe rispondere "Oh, non voglio eseguire il tuo codice, perché è privato, renderlo pubblico, poi lo farò ..." Ma o non vedo un motivo per cui dovrei rendere il mio codice privato - pubblico ... Spero che tu ottenga il punto – Lu4

+4

PHP non è .Net. A differenza del burro di arachidi e del cioccolato. PHP non ha un sapore migliore con queste cose calzante in esso. Inoltre, chiunque si presenti dopo aver probabilmente buttato fuori il tuo codice e ricostruito tutto in modo sensato. – DampeS8N

risposta

4

Ad ogni modo, se qualcuno è interessato, ho trovato l'unica soluzione possibile tramite ReflectionMethod. L'utilizzo di questo metodo con Php 5.3.2 comporta una riduzione delle prestazioni ed è 2,3 volte più lento rispetto al chiamare direttamente il membro della classe, e solo 1,3 volte più lento del metodo call_user_func. Quindi nel mio caso è assolutamente accettabile. Ecco il codice se qualcuno interessato:

class EventArgs { 

} 

class EventEraser { 

    private $eventIndex; 
    private $eventErased; 
    private $eventHandlers; 

    public function __construct($eventIndex, array &$eventHandlers) { 
     $this->eventIndex = $eventIndex; 
     $this->eventHandlers = &$eventHandlers; 
    } 

    public function RemoveEventHandler() { 
     if (!$this->eventErased) { 
      unset($this->eventHandlers[$this->eventIndex]); 

      $this->eventErased = true; 
     } 
    } 

} 

class EventSubscriber { 

    private $eventIndex; 
    private $eventHandlers; 

    public function __construct(array &$eventHandlers) { 
     $this->eventIndex = 0; 
     $this->eventHandlers = &$eventHandlers; 
    } 

    public function AddEventHandler(EventHandler $eventHandler) { 
     $this->eventHandlers[$this->eventIndex++] = $eventHandler; 
    } 

    public function AddRemovableEventHandler(EventHandler $eventHandler) { 
     $this->eventHandlers[$this->eventIndex] = $eventHandler; 

     $result = new EventEraser($this->eventIndex++, $this->eventHandlers); 

     return $result; 
    } 

} 

class EventHandler { 

    private $owner; 
    private $method; 

    public function __construct($owner, $methodName) { 
     $this->owner = $owner; 
     $this->method = new \ReflectionMethod($owner, $methodName); 
     $this->method->setAccessible(true); 
    } 

    public function Invoke($sender, $eventArgs) { 
     $this->method->invoke($this->owner, $sender, $eventArgs); 
    } 

} 

class Event { 

    private $unlocked = true; 
    private $eventReceiver; 
    private $eventHandlers; 
    private $recursionAllowed = true; 

    public function __construct() { 
     $this->eventHandlers = array(); 
    } 

    public function GetUnlocked() { 
     return $this->unlocked; 
    } 

    public function SetUnlocked($value) { 
     $this->unlocked = $value; 
    } 

    public function FireEventHandlers($sender, $eventArgs) { 
     if ($this->unlocked) { 
      //защита от рекурсии 
      if ($this->recursionAllowed) { 
       $this->recursionAllowed = false; 

       foreach ($this->eventHandlers as $eventHandler) { 
        $eventHandler->Invoke($sender, $eventArgs); 
       } 

       $this->recursionAllowed = true; 
      } 
     } 
    } 

    public function Subscriber() { 
     if ($this->eventReceiver == null) { 
      $this->eventReceiver = new EventSubscriber($this->eventHandlers); 
     } 

     return $this->eventReceiver; 
    } 

} 
4

callback in PHP non sono come le richiamate nella maggior parte delle altre lingue. I linguaggi tipici rappresentano i callback come puntatori, mentre PHP li rappresenta come stringhe. Non c'è "magia" tra la stringa o la sintassi array() e la chiamata. call_user_func(array($obj, 'str')) è sintatticamente lo stesso di $obj->str(). Se str è privato, la chiamata avrà esito negativo.

Dovresti semplicemente rendere pubblico il gestore di eventi. Questo ha un significato semantico valido, cioè "destinato ad essere chiamato al di fuori della mia classe".

Questa scelta implementazione ha altri effetti collaterali interessanti, ad esempio:

class Food { 
    static function getCallback() { 
     return 'self::func'; 
    } 

    static function func() {} 

    static function go() { 
     call_user_func(self::getCallback()); // Calls the intended function 
    } 
} 

class Barf { 
    static function go() { 
     call_user_func(Food::getCallback()); // 'self' is interpreted as 'Barf', so: 
    }           // Error -- no function 'func' in 'Barf' 
} 
+0

Grazie a zildjohn01, in realtà stavo già usando questo approccio, la cosa interessante di come php gestisce il contesto, perché call_user_func sembra funzionare con metodi di classe privati ​​all'interno della classe stessa. Ma smette di lavorare fuori. Per quanto riguarda rendere pubblico il mio eventoHandlers, penso che questo sia puro male in termini di OOP, e stavo cercando un modo per chiamare i membri della classe privata al di fuori della classe stessa, e questo è possibile in PHP con l'aiuto di ReflectionMethod, guarda il post qui sotto . Comunque grazie per il disturbo di rispondere. – Lu4

Problemi correlati