2012-03-01 19 views
9

de trabajo con una clase de librería PHP, y me gustaría para envolver la totalidad de sus funciones públicas en una subclase ... Algo a lo largo de las líneas de:PHP: Wrap todas las funciones de una clase en una subclase

class BaseClass 
{ 
    function do_something() 
    { 
     some; 
     stuff; 
    } 

    function do_something_else() 
    { 
     other; 
     stuff; 
    } 

    /* 
    * 20-or-so other functions here! 
    */ 
} 

class SubClass extends BaseClass 
{ 
    function magicalOverrideEveryone() 
    { 
     stuff-to-do-before;  // i.e. Display header 
     call_original_function(); // i.e. Display otherwise-undecorated content 
     stuff-to-do-after;   // i.e. Display footer 
    } 
} 

Al reducirlo, prefiero no tener que anular todos los métodos de superclase con el mismo código de contenedor, si hay una forma [algo elegante/limpia] de hacerlo todo en un solo lugar.

¿Esto es posible? Sospecho que estoy en tierra metaprogramming aquí, y ni siquiera sé si PHP ofrece una bestia, pero pensé que le pediría ...

+0

Para aclarar: hay solo algunos métodos de 'BaseClass' que desea anular en' SubClass', ¿no en todos ellos? –

+0

@ s992, no exactamente - Estaba buscando una forma de aplicar el mismo código contenedor a todos los métodos en la clase base. Parece que el método mágico de __call de PHP hará el trabajo con un contenedor de proxy, gracias @meagar! – mtbkrdave

+0

Gotcha, la pregunta no era exactamente clara. Si la respuesta de Meagar te ayudó, deberías aceptarlo. :) –

Respuesta

16

Puede hacerlo fácilmente con __callmagic method y una clase genérica de "proxy" que no hereda directamente de la clase base.

Aquí hay una implementación (casi) completa de una clase de proxying que envuelve el objeto que pasa. Invocará algún código "antes" y "después" alrededor de cada llamada al método.

class MyProxy { 
    function __construct($object) { 
    $this->object = $object; 
    } 

    function __call($method, $args) { 
    // Run before code here 

    // Invoke original method on our proxied object 
    call_user_func_array(array($this->object, $method), $args); 

    // Run after code here 
    } 
} 


$base = new BaseClass(); 
$proxy = new MyProxy($base); 

$proxy->doSomething(); // invoke $base->doSomething(); 

Usted, por supuesto que desee añadir un poco de control de errores, como pedir a los objetos proxy si responde al método indicado en __call y levantando un error si no lo hace. Incluso podría diseñar la clase Proxy para que sea una clase base para otros proxies. Las clases secundarias secundarias podrían implementar los métodos before y after.

La desventaja es que su "clase hija" ya no implementa BaseClass, es decir, si está usando type-hinting y quieren exigir que los únicos objetos de tipo BaseClass se pasan a una función, este enfoque fallará.

+0

Fenomenal: ¡esto es exactamente lo que estaba buscando! – mtbkrdave

+0

También ... ¡WOW, eso fue rápido! ¡Nunca subestimes el poder de Stackoverflow! – mtbkrdave

+1

Una forma de eludir el problema del tipo es observar cómo la versión de PHP de Mockito, Phockito, genera clases de Mock. https://github.com/hafriedlander/phockito/blob/master/Phockito.php#L170 –

0

que podría estar buscando el patrón decorador:

class A 
{ 
    function do1(){ 

    } 
} 
class DecoratorForA extends A 
{ 
    private A original; 
    public DecoratorForA(A original) 
    { 
     this.original = original; 
    } 
    function do1(){ //<-- override keyword in php? 
     stuffBefore(); 
     original->do1(); 
     stuffAfter();  
    } 
} 

ya que esto no es lo que quieres, ¿quizás este enlace es de ayuda? http://code.google.com/p/php-aop/

+0

oh, ahora lo leí correctamente, supongo que NO estás buscando eso. – mindandmedia

+0

Está tratando de encontrar una forma simple de aplicar generalmente el patrón decorador * sin * escribir una magnitud de código repetitivo. Acabas de dar a sus necesidades un nombre, no una solución ... – rodneyrehm

+0

Correcto, estaba buscando una aplicación de metaprogramación del patrón decorador ... ¡Gracias por tu aporte, sin embargo! – mtbkrdave

2

Si los nombres de los métodos de la subclase pueden diferir ligeramente de los nombres de los métodos originales de BaseClass, se podría escribir un contenedor genérico con __call(). Si los nombres del método deben coincidir, no veo cómo podría lograr su objetivo sin sobreescribir manualmente cada método. Tal vez podrías usar el funcall PECL para hacer esto, pero en primer lugar tendrías que poder cargar ese PECL.

Si puede hacer los métodos de BaseClass protected, el enfoque __call() en SubClass funcionará.

Si no necesita necesita para extender la clase, el enfoque de @meager está perfectamente bien. Tenga en cuenta que __call() y call_user_func_array() imponen cierta sobrecarga.

+0

Esto es algo importante de señalar, gracias. No (creo que) necesito extender la clase, así que anticipo que el enfoque de proxy funcionará. (Depende de cómo se haga el enrutamiento CodeIgniter bajo el capó) – mtbkrdave

0

Si entiendo bien, desea extender una clase, pero no permitir que se llame a ninguno de los métodos del padre. En su lugar, desea llamar a todos los métodos usted mismo en un método en la nueva clase.

Entonces, ¿por qué quieres heredar en primer lugar? Suena como si solo debieras crear un adaptador/decorador para tu BaseClass.

class SubClass 
{ 
    function magicalOverrideEveryone() 
    { 
     BaseClass bc = new BaseClass(); 
     bc.do_something(); 
     bc.do_something_else(); 
    } 
} 
Cuestiones relacionadas