2010-05-28 10 views
8

He estado buscando los métodos de reflexión php, lo que quiero hacer es inyectar algún código después de que se abra el método y antes de cualquier valor devuelto, por ejemplo quiero cambiar:modificar un método/función en el tiempo de ejecución

function foo($bar) 
{ 
    $foo = $bar ; 
    return $foo ; 
} 

e inyectar un poco de código en él como:

function foo($bar) 
{ 
    //some code here 
    $foo = $bar ; 
    //some code here 
    return $foo ; 
} 

posible?

+0

No creo que lo que quieres sea posible. Podría estar equivocado, pero en general * Las API de Reflexión * se usan para ** obtener ** información sobre clases, etc. y no para cambiar el código en tiempo de ejecución. –

+0

da más información sobre lo que estás tratando de hacer y tal vez podamos ayudar a encontrar una solución diferente – Galen

+0

Aunque estoy seguro de que hay una solución a esto, y que sabes que la necesitas, ¿estás seguro de que es lo que quieres? ¿Por qué querrías cambiar la naturaleza de una función durante el tiempo de ejecución? Una mejor opción podría ser pasar una variable que controla el flujo de funciones; o tal vez considere usar sentencias eval() para ejecutar código. Piénselo de esta manera: ¿cómo se nombra una función que no tiene un efecto predecible? – Kurucu

Respuesta

2

Mire en anonymous functions. Si puede ejecutar PHP 5.3, puede ser más acorde con lo que intenta hacer.

2

Esto no es posible, al menos no en la forma que está buscando. Como sugirió el comentario, la reflexión es para obtener información sobre clases/funciones, no modificarlas.

Hay una extensión de PHP llamada Runkit que creo que proporciona este tipo de funcionalidad - http://www.php.net/manual/en/book.runkit.php, sin embargo, esto no se instalará de manera predeterminada en la gran mayoría de los hosts que existen.

Sin embargo, puede haber una forma diferente de hacerlo. Quizás si pudiera dar más información sobre lo que está intentando hacer y por qué no puede modificar la función en cuestión, es posible que podamos proporcionar algunos indicadores. P.ej. ¿Es la función que desea modificar una función central de PHP o código en una biblioteca de terceros que no desea editar?

+0

runkit es beta en el momento en que publiqué este comentario y no compila (da error al intentar compilar en linux con php 5.3.19) – NickSoft

0

Sólo una idea:

function foo($bar, $preprocess, $postprocess){ 

    //IF CONDITION($preprocess) 
    include('/mylogic/'.$preprocess.".php"); //do some preprocessing here? 
    $foo = $bar ; 
    //IF CONDITION($postprocess) 
    include('/mylogic/'.$postprocess.".php"); //process $foo here? 
    //FINALLY return 
    return $foo; 

} 
+0

Buena idea ... aunque yo probablemente haga $ preprocess y $ postprocess en callbacks ('functionname' o array ('classname', 'methodname')) y use call_user_func para llamarlos. Tendría implicaciones de alcance, pero eso también lo haría más seguro en mi humilde opinión. – grossvogel

+0

debe hacerse para todos los métodos en mi aplicación, y quiero que permanezca limpio – mononym

4

Bueno, una forma, sería hacer todo el método llama "virtual":

class Foo { 
    protected $overrides = array(); 

    public function __call($func, $args) { 
     $func = strtolower($func); 
     if (isset($this->overrides[$func])) { 
      // We have a override for this method, call it instead! 
      array_unshift($args, $this); //Add the object to the argument list as the first 
      return call_user_func_array($this->overrides[$func], $args); 
     } elseif (is_callable(array($this, '_' . $func))) { 
      // Call an "internal" version 
      return call_user_func_array(array($this, '_' . $func), $args); 
     } else { 
      throw new BadMethodCallException('Method '.$func.' Does Not Exist'); 
     } 
    } 

    public function addOverride($name, $callback) { 
     $this->overrides[strtolower($name)] = $callback; 
    } 

    public function _doSomething($foo) { 
     echo "Foo: ". $foo; 
    } 
} 

$foo = new Foo(); 

$foo->doSomething('test'); // Foo: test 

PHP 5.2:

$f = create_function('$obj, $str', 'echo "Bar: " . $obj->_doSomething($str) . " Baz";'); 

PHP 5.3:

$f = function($obj, $str) { 
    echo "Bar: " . $obj->_doSomething($str) . " Baz"; 
} 

Todo PHP:

$foo->addOverride('doSomething', $f); 

$foo->doSomething('test'); // Bar: Foo: test Baz 

Se pasa la instancia del objeto como el primer método para la "override". Nota: Este método "anulado" no tendrá acceso a ningún miembro protegido de la clase. Así que use getters (__get, __set). Se tendrá acceso a métodos protegidos, ya que la llamada real viene de __call() ...

Nota: Tendrá que modificar todos los métodos por defecto a un prefijo de declaración '_' para que esto funcione. .. (o puede elegir otra opción de prefijo, o puede simplemente delimitarlos todos protegidos) ...

-1

Tal vez me esté perdiendo algo, pero ¿realmente quiere "inyectar" el código como dice? ¿Qué estás intentando lograr? Si simplemente quiere ejecutar un bloque de código cuando ocurre A, y otro cuando ocurre B, entonces todo lo que necesita es una lógica de programación simple, como una instrucción if().

¿Está realmente tratando de modificar una función en tiempo de ejecución? ¿O es solo un problema de lógica?

Sea más específico sobre lo que necesita hacer.

0

Puede agregar un autocargador especial antes del autocargador original, leer el archivo que cargaría su autocargador original, cambiar el contenido, guardar ese archivo en otro lugar (algunos dir de tmp por ejemplo) e incluir ese archivo modificado en lugar del original uno.

0

Yo quería hacer exactamente lo mismo, así que creé una clase de instrumentación genérica que reemplazaría la clase que estaba tratando de instrumentar y aprovechando la técnica de @ircmaxell que llamaría a la clase para instrumentarla.

<?php 

class GenericClassInstrumentor 
{ 
    private $instrumentedClass; 
    private $className; 

    public function __construct($instrumentedClass) 
    { 
     $this->instrumentedClass = $instrumentedClass; 
     $this->className = get_class($instrumentedClass); 
    } 

    public function __call($name, $arguments) 
    { 
     $start = microtime(true); 
     $result = call_user_func_array(array($this->instrumentedClass, $name), $arguments); 
     $end = microtime(true); 
     $duration = ($end - $start) * 1000; 

     // optionally log something here 
     return $result; 
    } 
} 

Supongamos que tenía una instancia de una clase como $myClass, que tenía la función foo en él, entonces usted haría algo como esto:

$myClass = new ClassWithFoo(); 
if ($instrumentationOn) { 
    $myClass = new GenericClassInstrumentor($myClass); 
} 

Calling $myClass->foo($bar) funcionaría de la misma (con el mismo advertencias como respuesta de @ircmaxell) si $instrumentationOn es true o false. Si es true simplemente haría las cosas extra de temporización.

Cuestiones relacionadas