Podrías usar __call. Pero no utilices __call. Cambiar dinámicamente el comportamiento de un objeto es una forma muy fácil de hacer que su código sea ilegible y no se pueda mantener.
Muy, muy inteligente, pero torpe en un proyecto del mundo real, en mi opinión. (y +1 para contrarrestar votantes negativos anónimos)
Pekka
2
@pekka - pero ¿cómo, exactamente, es kludgy? Claro, no estoy diciendo que sea un experto en extender un objeto durante el tiempo de ejecución en PHP, pero honestamente no puedo decir que vea mucho mal en él. (tal vez solo tengo mal gusto)
karim79
16
También agregaría una verificación is_callable en ese caso (para que no intente llamar accidentalmente a una cadena). Y también arroje una BadMethodCallException () si no puede encontrar un método, por lo que no lo devuelve y cree que regresó con éxito. Además, convierta el primer parámetro de la función en el objeto en sí, y luego haga una array_unshift($args, $this);antes de la llamada al método, de modo que la función obtenga una referencia al objeto sin necesidad explícita de vincularlo ...
ircmaxell
3
Creo que la gente está perdiendo el punto, el requisito original era usar un objeto sin clases.
thomas-peter
1
De hecho, sugeriría que elimine por completo la prueba isset () porque si esa función no está definida, probablemente no quiera regresar en silencio.
Usar simplemente __call para permitir agregar nuevos métodos en tiempo de ejecución tiene el mayor inconveniente de que esos métodos no pueden usar la referencia de instancia $ this. Todo funciona muy bien, hasta que los métodos agregados no usen $ this en el código.
Actualización: el enfoque que se muestra aquí tiene una deficiencia importante: la nueva función no es un miembro completamente calificado de la clase; $thisno está presente en el método cuando se invoca de esta manera. Esto significa que tendría que pasar el objeto a la función como parámetro si desea trabajar con datos o funciones de la instancia del objeto. Además, usted no será capaz de acceder privateo de protectedlos miembros de la clase a partir de estas funciones.
¡Buena pregunta e inteligente idea usando las nuevas funciones anónimas!
Curiosamente, esto funciona: Reemplazar
$me->doSomething();// Doesn't work
por call_user_func en la función en sí :
call_user_func($me->doSomething);// Works!
lo que no funciona es la forma "correcta":
call_user_func(array($me,"doSomething"));// Doesn't work
si se llama de esa manera, PHP requiere que el método se declare en la definición de la clase.
¿Es esta una private/ public/ protectedtema visibilidad?
Actualización: No. Es imposible llamar a la función de la forma normal incluso desde dentro de la clase, por lo que esto no es un problema de visibilidad. Pasar la función real a call_user_func()es la única forma en que puedo hacer que esto funcione.
Para ver cómo hacer esto con eval, puede echar un vistazo a mi micro-framework PHP, Halcyon, que está disponible en github . Es lo suficientemente pequeño como para poder resolverlo sin ningún problema: concéntrese en la clase HalcyonClassMunger.
Supongo (y también mi código) que está ejecutando PHP <5.3.0 y, por lo tanto, necesita trucos y trucos para que esto funcione.
ivans
Preguntar si alguien quisiera comentar sobre el
voto negativo
Probablemente lo sea, hay algunas votaciones negativas masivas por aquí. ¿Pero tal vez quieras desarrollar un poco lo que hiciste? Como puede ver, esta es una pregunta interesante sin una respuesta definitiva todavía.
Pekka
1
Sin la __callsolución, puede usar bindTo(PHP> = 5.4), para llamar al método con un $thislímite $measí:
$me =new stdClass;// Property for proving that the method has access to the above object:
$me->message ="I\'ve done something";
$me->doSomething =function(){
echo $this->message;};
call_user_func($me->doSomething->bindTo($me));// "I've done something"
Alternativamente, puede asignar la función vinculada a una variable y luego llamarla sin call_user_func:
La única otra forma es mediante el uso de classkit , una extensión experimental de php. (también en la publicación)
Sí, es posible agregar un método a una clase PHP después de su definición. Quieres usar classkit, que es una extensión "experimental". Sin embargo, parece que esta extensión no está habilitada de forma predeterminada, por lo que depende de si puede compilar un binario PHP personalizado o cargar archivos DLL PHP si está en Windows (por ejemplo, Dreamhost permite binarios PHP personalizados, y son bastante fáciles de configurar ).
La respuesta karim79 funciona, sin embargo, almacena funciones anónimas dentro de las propiedades del método y sus declaraciones pueden sobrescribir las propiedades existentes del mismo nombre o no funciona si la propiedad existente está privatecausando un error fatal en el objeto inutilizable.Creo que almacenarlos en una matriz separada y usar setter es una solución más limpia. El setter inyector de método se puede agregar a cada objeto automáticamente usando rasgos .
PD: Por supuesto, este es un truco que no debe usarse ya que viola el principio abierto cerrado de SOLID.
classMyClass{//create array to store all injected methodsprivate $anon_methods = array();//create setter to fill array with injected methods on runtimepublicfunction inject_method($name, $method){
$this->anon_methods[$name]= $method;}//runs when calling non existent or private methods from object instancepublicfunction __call($name, $args){if( key_exists($name, $this->anon_methods)){
call_user_func_array($this->anon_methods[$name], $args);}}}
$MyClass =newMyClass;//method one
$print_txt =function($text){
echo $text . PHP_EOL;};
$MyClass->inject_method("print_txt", $print_txt);//method
$add_numbers =function($n, $e){
echo "$n + $e = ".($n + $e);};
$MyClass->inject_method("add_numbers", $add_numbers);//Use injected methods
$MyClass->print_txt("Hello World");
$MyClass->add_numbers(5,10);
Respuestas:
Puede aprovechar
__call
para esto:fuente
array_unshift($args, $this);
antes de la llamada al método, de modo que la función obtenga una referencia al objeto sin necesidad explícita de vincularlo ...Con PHP 7 puedes usar clases anónimas
PHP RFC: clases anónimas
fuente
Usar simplemente __call para permitir agregar nuevos métodos en tiempo de ejecución tiene el mayor inconveniente de que esos métodos no pueden usar la referencia de instancia $ this. Todo funciona muy bien, hasta que los métodos agregados no usen $ this en el código.
Para resolver este problema, puede reescribir el patrón de esta manera
De esta manera, puede agregar nuevos métodos que pueden usar la referencia de instancia
fuente
¡Buena pregunta e inteligente idea usando las nuevas funciones anónimas!
Curiosamente, esto funciona: Reemplazar
por call_user_func en la función en sí :
lo que no funciona es la forma "correcta":
si se llama de esa manera, PHP requiere que el método se declare en la definición de la clase.
¿Es esta una
private
/public
/protected
tema visibilidad?Actualización: No. Es imposible llamar a la función de la forma normal incluso desde dentro de la clase, por lo que esto no es un problema de visibilidad. Pasar la función real a
call_user_func()
es la única forma en que puedo hacer que esto funcione.fuente
también puede guardar funciones en una matriz:
fuente
Para ver cómo hacer esto con eval, puede echar un vistazo a mi micro-framework PHP, Halcyon, que está disponible en github . Es lo suficientemente pequeño como para poder resolverlo sin ningún problema: concéntrese en la clase HalcyonClassMunger.
fuente
Sin la
__call
solución, puede usarbindTo
(PHP> = 5.4), para llamar al método con un$this
límite$me
así:El guión completo podría verse así:
Alternativamente, puede asignar la función vinculada a una variable y luego llamarla sin
call_user_func
:fuente
Hay una publicación similar en stackoverflow que aclara que esto solo se puede lograr mediante la implementación de ciertos patrones de diseño.
La única otra forma es mediante el uso de classkit , una extensión experimental de php. (también en la publicación)
fuente
La respuesta karim79 funciona, sin embargo, almacena funciones anónimas dentro de las propiedades del método y sus declaraciones pueden sobrescribir las propiedades existentes del mismo nombre o no funciona si la propiedad existente está
private
causando un error fatal en el objeto inutilizable.Creo que almacenarlos en una matriz separada y usar setter es una solución más limpia. El setter inyector de método se puede agregar a cada objeto automáticamente usando rasgos .PD: Por supuesto, este es un truco que no debe usarse ya que viola el principio abierto cerrado de SOLID.
fuente