2009-09-15 21 views
7

Quiero ser capaz de envolver una función de PHP por otra función, pero dejando intacta su lista original de nombre/parámetro.PHP equivalente para un decorador de python?

Por ejemplo:

function A() { 
    print "inside A()\n"; 
} 

function Wrap_A() { 
    print "Calling A()\n"; 
    A(); 
    print "Finished calling A()\n"; 
} 

// <--- Do some magic here (effectively "A = Wrap_A") 

A(); 

Salida:

Calling A() 
inside A() 
Finished calling A() 
+0

Su ejemplo de Wrap_A es un poco engañoso para alguien que no sabe cómo funciona el decorador de Python, ya que su implementación de Wrap_A explícitamente hace referencia a A. La idea de decorador es que puede decorar cualquier función con ella, pero en su ejemplo claramente no podría usar 'Wrap_A' para envolver alguna otra función' B'. ¿Te importaría si edité tu pregunta para que sea una representación más precisa de qué función es la decoración? –

Respuesta

6

Aparentemente runkit podría help you.

Además, siempre puede hacerlo de la manera OO. Pon la diversión original en una clase y el decorador en una clase extendida. Crear una instancia e ir.

+0

Gracias, 'runkit' es exactamente lo que estaba buscando. – Fragsworth

+1

¿Cómo lo usarías tan fácil y genéricamente como en python? Aquí agregas la decoración justo encima de la definición de la función (construcciones similares vistas en .NET y Java). – Michael

1

tal vez usted está buscando call_user_func_array:

function wrapA() { 
    $args = func_get_args(); 
    return call_user_func_array('A', $args); 
} 

a partir de PHP 5.3 incluso se podría decir:

return call_user_func_array('A', func_get_args()); 

después de editar su pregunta, diría, no, esto no es posible, pero hay algunas maneras, vea esta pregunta: how to implement a decorator in PHP?

+0

Esto no está relacionado en absoluto con lo que estoy buscando. Edité mi pregunta para agregar una aclaración. – Fragsworth

1

No se puede hacer esto con funciones en PHP . En otros lenguajes dinámicos, como Perl y Ruby, puede redefinir funciones definidas previamente, pero PHP arroja un error fatal cuando intenta hacerlo.

En 5.3, se puede crear un anonymous function y almacenarlo en una variable:

<?php 
    $my_function = function($args, ...) { ... }; 
    $copy_of_my_function = $my_function; 
    $my_function = function($arg, ...) { /* Do something with the copy */ }; 
?> 

Alternativamente, se puede utilizar el modelo tradicional decorador y/o una fábrica y el trabajo con las clases en su lugar.

1

Aquí está mi método de imitar a los decoradores de python en php.

function call_decorator ($decorator, $function, $args, $kwargs) { 

    // Call the decorator and pass the function to it 
    $decorator($function, $args, $kwargs); 
} 

function testing ($args, $kwargs) { 
    echo PHP_EOL . 'test 1234' . PHP_EOL; 
} 

function wrap_testing ($func, $args, $kwargs) { 

    // Before call on passed function 
    echo 'Before testing'; 

    // Call the passed function 
    $func($args, $kwargs); 

    // After call on passed function 
    echo 'After testing'; 
} 

// Run test 
call_decorator('wrap_testing', 'testing'); 

Salida:

Before testing 
testing 1234 
After testing 

Con esta aplicación también se puede hacer algo como esto con una función anónima:

// Run new test 
call_decorator('wrap_testing', function($args, $kwargs) { 
    echo PHP_EOL . 'Hello!' . PHP_EOL; 
}); 

Salida:

Before testing 
Hello! 
After testing 

Y por último se incluso puedes hacer algo cosa como esta, si te gusta.

// Run test 
call_decorator(function ($func, $args, $kwargs) { 
    echo 'Hello '; 
    $func($args, $kwargs); 
}, function($args, $kwargs) { 
    echo 'World!'; 
}); 

Salida:

Hello World! 

Con esta construcción anterior, se puede pasar variables a la función interna o envoltorio, si es necesario.Aquí es que la implementación de una función interna anónima:

$test_val = 'I am accessible!'; 

call_decorator('wrap_testing', function($args, $kwargs){ 
    echo $args[0]; 
}, array($test_val)); 

Se trabajará exactamente lo mismo sin una función anónima:

function test ($args, $kwargs) { 
    echo $kwargs['test']; 
} 

$test_var = 'Hello again!'; 

call_decorator('wrap_testing', 'test', array(), array('test' => $test_var)); 

Por último, si se necesita modificar la variable en el interior o bien la envoltura o el wrappie, solo necesitas pasar la variable por referencia.

Sin referencia:

$test_var = 'testing this'; 
call_decorator(function($func, $args, $kwargs) { 
    $func($args, $kwargs); 
}, function($args, $kwargs) { 
    $args[0] = 'I changed!'; 
}, array($test_var)); 

de salida:

testing this 

con referencia:

$test_var = 'testing this'; 
call_decorator(function($func, $args, $kwargs) { 
    $func($args, $kwargs); 
}, function($args, $kwargs) { 
    $args[0] = 'I changed!'; 

// Reference the variable here 
}, array(&$test_var)); 

de salida:

I changed! 

Eso es todo lo que tengo por ahora, es bastante útil en muchos casos, e incluso puede envolverlos varias veces si lo desea.

Cuestiones relacionadas