2011-01-03 11 views
8

necesito para poner en práctica el siguiente patrón en php:La forma correcta de hacer las devoluciones de llamada o delegados en 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')); 
    } 

    ... 

}  

¿cuál es la forma correcta de hacerlo.

P.S.

Estaba usando call_user_func para ese propósito y créanlo o no, fue capaz de llamar a miembros privados de la clase, pero después de algunas semanas de desarrollo descubrí que dejó de funcionar. ¿Fue un error en mi código o fue alguna otra cosa que me hizo pensar que 'call_user_func' puede llamar a funciones privadas de clase, no sé, pero ahora estoy buscando un método simple, rápido y elegante de seguridad llamar a un miembro privado de la clase de otra clase. Estoy buscando cierres en este momento, pero tengo problemas con '$ this' dentro de cierre ...

+1

'$ this' no es compatible con cierres (como se menciona en el manual) y los miembros privados son privados, ya que el uno, que se los declaró como privados, no quiere que se los llame desde cualquier objeto de una clase diferente. Esta es la definición de "miembro privado";) – KingCrunch

+0

Sí, los miembros privados son privados porque son privados :) Pero imagine que está diciendo su botón 'Oye, ejecute mi código privado en' ButtonClickedHandler 'cuando le hagan clic' . En este caso, no veo por qué el botón debería responder 'Oh, no quiero ejecutar tu código, porque es privado, hazlo público, luego lo haré ...' Pero o bien no veo una razón por la cual debería hacer mi código privado - público ... Espero que entiendas el punto – Lu4

+4

PHP no es .Net. A diferencia de la mantequilla de maní y el chocolate. PHP no tendrá mejor sabor con estas cosas calzadas. Además, cualquiera que venga después de ti probablemente arrojará tu código y lo reconstruirá de una manera sensata. – DampeS8N

Respuesta

4

De todos modos, si alguien está interesado, he encontrado la única solución posible a través de ReflectionMethod. El uso de este método con Php 5.3.2 proporciona una penalización en el rendimiento y es 2,3 veces más lento que llamar al miembro de la clase directamente, y solo 1.3 veces más lento que el método call_user_func. Entonces, en mi caso, es absolutamente aceptable. Aquí está el código si alguien está interesado:

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

Las devoluciones de llamada en PHP no son como las devoluciones de llamada en la mayoría de los demás idiomas. Los lenguajes típicos representan devoluciones de llamada como punteros, mientras que PHP los representa como cadenas. No hay "magia" entre la cadena o la sintaxis array() y la llamada. call_user_func(array($obj, 'str')) es sintácticamente el mismo que $obj->str(). Si str es privado, la llamada fallará.

Simplemente debe hacer que su controlador de eventos sea público. Esto tiene un significado semántico válido, es decir, "destinado a ser llamado desde fuera de mi clase".

Esta elección aplicación tiene otros efectos secundarios interesantes, por ejemplo:

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

Gracias zildjohn01, en realidad estaba usando este enfoque antes, lo interesante es cómo php maneja el contexto, porque call_user_func parece estar trabajando con métodos de clase privados dentro de la clase. Pero deja de trabajar afuera. En cuanto a hacer que mis manejadores de eventos sean públicos, creo que esto es pura maldad en términos de OOP, y estaba buscando una forma de llamar a los miembros privados de la clase fuera de clase, y esto es posible en PHP con la ayuda de ReflectionMethod, mira el post a continuación . De todos modos, gracias por molestarte en responder. – Lu4

Cuestiones relacionadas