2012-06-11 11 views
23

Me pregunto si hay una forma de adjuntar un nuevo método a una clase en tiempo de ejecución, en php. Es decir, no en un nivel de instancia, sino directamente en la clase, de modo que todas las instancias recientemente creadas tengan este nuevo método. ¿Se puede hacer algo así con la reflexión?php create class method at runtime

Gracias

Respuesta

20

Sí, puedes.

A continuación se muestra la forma de crear el método en tiempo de ejecución en php 5.4.x.

La función anónima está representada por la clase de Cierre iniciada desde 5.3.x. Desde 5.4.x, agrega un método estático Closure::bind para vincular la función anónima a un objeto o clase en particular.

Ejemplo:

class Foo { 
    private $methods = array(); 

    public function addBar() { 
     $barFunc = function() { 
     var_dump($this->methods); 
     }; 
     $this->methods['bar'] = \Closure::bind($barFunc, $this, get_class()); 
    } 

    function __call($method, $args) { 
      if(is_callable($this->methods[$method])) 
      { 
      return call_user_func_array($this->methods[$method], $args); 
      } 
    } 
} 

$foo = new Foo; 
$foo->addBar(); 
$foo->bar(); 
+2

Impresionante. Me encantan estas nuevas características PHP – Thomas

+0

¿Cuál es el punto del método __call aquí, si \ Closure :: bind hace lo mismo, y viceversa? – jayarjo

+2

@jayarjo '\ Closure :: bind' cambia el contexto de la función anónima (en este caso, se asegura de que, dentro del cierre,' $ this' se refiere al objeto correcto). Sin embargo, no hace que esa función sea un método de '$ this'. Todavía necesitamos '__call' para permitir que el código externo llame al cierre como si fuera un método. –

3

¿Usted ha tomado un vistazo a create_function() en la documentación? También puede obtener el resultado deseado por overloading.

+0

create_function (lo que concierne a lo sé) crea funciones en el contexto global y la sobrecarga no es una solución tan limpio como me gustaría que fuera – Thomas

+0

entiendo. PHP no es, sin embargo, un lenguaje tan dinámico como sería ideal en este escenario. Creo [Reflection] (http://www.php.net/manual/en/intro.reflection.php) o [Runkit] (http://www.php.net/manual/en/intro.runkit.php) sería necesario para lograr una solución más cercana, a la que aludiste en tu pregunta. –

+0

Me encantaría una solución con reflejo, pero parece que no puedo encontrar información en ninguna parte. En cuanto a la API, no hay nada que pueda usar – Thomas

7

Jugué un poco con todo. Parece que lo único que puede hacer con ReflectionClass es reemplazar un método existente. Pero incluso eso sería indirectamente.

En realidad, no conozco ningún lenguaje basado en clases, donde existen clases dinámicas (de nuevo, mi conocimiento es bastante limitado). Lo he visto hecho solo en lenguajes basados ​​en prototipos (javascript, ruby, smalltalk). En cambio, lo que puede hacer, en PHP 5.4, es usar Closure y agregar nuevos métodos a un objeto existente .

Aquí es una clase que permitirá realizar tales perversión a cualquier objeto:

class Container 
{ 
    protected $target; 
    protected $className; 
    protected $methods = []; 

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

    public function attach($name, $method) 
    { 
     if (!$this->className) 
     { 
      $this->className = get_class($this->target); 
     } 
     $binded = Closure::bind($method, $this->target, $this->className); 
     $this->methods[$name] = $binded; 
    } 

    public function __call($name, $arguments) 
    { 
     if (array_key_exists($name, $this->methods)) 
     { 
      return call_user_func_array($this->methods[$name] , $arguments); 
     } 

     if (method_exists($this->target, $name)) 
     { 
      return call_user_func_array( 
       array($this->target, $name), 
       $arguments 
       ); 
     } 
    } 
} 

Para usar esto, usted tiene que proporcionar constructor con un objeto existente. Aquí es pequeño ejemplo de uso:

class Foo 
{ 
    private $bar = 'payload'; 
}; 
$foobar = new Foo; 
// you initial object 


$instance = new Container($foobar); 
$func = function ($param) 
{ 
    return 'Get ' . $this->bar . ' and ' . $param; 
}; 
$instance->attach('test', $func); 
// setting up the whole thing 


echo $instance->test('lorem ipsum'); 
// 'Get payload and lorem ipsum' 

No es exactamente lo que quiere, pero que yo sepa esto es lo más cerca que puede llegar.

3

Esto es posible con la extensión runkit de runkit_method_add(). Tenga cuidado al usar esto en producción sin embargo.

Ejemplo:

<?php 
class Example {} 

$e = new Example(); 

runkit_method_add(
    'Example', 
    'add', 
    '$num1, $num2', 
    'return $num1 + $num2;', 
    RUNKIT_ACC_PUBLIC 
); 

echo $e->add(12, 4); 
Cuestiones relacionadas