2010-06-10 17 views
13

Estoy usando WordPress como un CMS, y quiero extender una de sus clases sin tener que heredar de otra clase; es decir, simplemente quiero "add" más métodos para dicha clase:¿Cómo agregar un método a una clase existente en PHP?

class A { 

    function do_a() { 
     echo 'a'; 
    } 
} 

entonces:

function insert_this_function_into_class_A() { 
    echo 'b'; 
} 

(de alguna manera de la inserción de este último en una clase)

y:

A::insert_this_function_into_class_A(); # b 

¿Es esto posible incluso en PHP tenaz?

+0

"PHP tenaz"? – Artefacto

+0

¿Está editando el código fuente de clase una opción? –

+0

El título fue engañoso; "extender" se refiere a la herencia. Lo he cambiado para reflejar mejor tu pregunta. – Artefacto

Respuesta

17

Si sólo necesita acceder a la API pública de la clase, se puede utilizar un Decorator:

class SomeClassDecorator 
{ 
    protected $_instance; 

    public function myMethod() { 
     return strtoupper($this->_instance->someMethod()); 
    } 

    public function __construct(SomeClass $instance) { 
     $this->_instance = $instance; 
    } 

    public function __call($method, $args) { 
     return call_user_func_array(array($this->_instance, $method), $args); 
    } 

    public function __get($key) { 
     return $this->_instance->$key; 
    } 

    public function __set($key, $val) { 
     return $this->_instance->$key = $val; 
    } 

    // can implement additional (magic) methods here ... 
} 

luego envolver la instancia de SomeClass:

$decorator = new SomeClassDecorator(new SomeClass); 

$decorator->foo = 'bar';  // sets $foo in SomeClass instance 
echo $decorator->foo;   // returns 'bar' 
echo $decorator->someMethod(); // forwards call to SomeClass instance 
echo $decorator->myMethod(); // calls my custom methods in Decorator 

Si se necesita tener acceso para la API protected, debe usar la herencia. Si necesita acceder a la API private, debe modificar los archivos de la clase. Si bien el enfoque de herencia está bien, la modificación de los archivos de clase puede ocasionar problemas al actualizar (perderá los parches realizados). Pero ambos son más factibles que usar runkit.

+1

¡Genial !. Esto funciona no solo para agregar nuevos métodos a una clase, sino también para sobrescribirlos cuando 'extends' no es una opción. – juanra

3

Puede usar the runkit extension para esto, pero en realidad debería considerar la herencia regular.

Ver runkit_method_add.

+0

... mientras que Runkit no es más que un juguete. – johannes

+1

@johannes Algunas veces es útil cuando se depura. Pero ese es el motivo de mi recomendación, que, sin embargo, no me impide contestar la pregunta. – Artefacto

+1

Observe la gran advertencia roja "EXPERIMENTAL" en la página de manual?No recomiendo para ningún uso de producción ... – selfawaresoup

2

Si la clase en cuestión implementa __call magic, entonces es posible y bastante fácil. Si quiere saber cómo funciona esto, le sugiero que lea Extending objects with new methods at runtime.

+0

Posible, pero agrega un montón de código desagradable que puede ser difícil de mantener más adelante. Especialmente "$ func = $ foo-> baz; $ func();" tiene un posible error escrito por todas partes. :) – selfawaresoup

+0

Sé que es difícil de mantener, sería un desastre depurar y ciertamente no recomendaría su uso, pero * es * posible: p. – wimvds

-2

No, no se puede cambiar dinámicamente una clase durante el tiempo de ejecución en PHP.

Esto se puede hacer por cualquiera extendiendo la clase utilizando la herencia normal:

class Fancy extends NotSoFancy 
{ 
    public function whatMakesItFancy() //can also be private/protected of course 
    { 
     //  
    } 
} 

O puede editar los archivos de origen de Wordpress.

Prefiero la forma de herencia. Es mucho más fácil de manejar a largo plazo.

4

Una forma actualizada para 2014 que se ajusta al alcance.

public function __call($method, $arguments) { 
    return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments); 
} 

Ej:

class stdObject { 
    public function __call($method, $arguments) { 
     return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments); 
    } 
} 

$obj = new stdObject(); 
$obj->test = function() { 
    echo "<pre>" . print_r($this, true) . "</pre>"; 
}; 
$obj->test(); 
+1

Esto funciona muy bien para cualquier nombre de clase. En $ obj-> test también puede hacer $ this-> someClassMethod() y $ this-> privateVarName. También puede crear funciones dentro de las funciones miembro que pueden acceder a los métodos y variables de la clase. –

Cuestiones relacionadas