2010-02-05 14 views
9

¿Es posible simular cierres en PHP 5.2.x sin utilizar globales? Podría pensar en una forma de pasar las variables deseadas como parámetros adicionales al cierre, pero eso no parece una buena práctica.¿Es posible simular cierres en PHP 5.2.x sin usar globales?

¿Alguna idea?

+0

Es realmente triste que PHP no sea compatible con una construcción tan impresionante. – ChaosPandion

+4

@ ChaosPandion pero lo hace desde PHP5.3 hasta – Gordon

+0

, sí, pero tiene que listar las variables cerradas explícitamente. "Cierres con muletas", por así decirlo. Sin embargo, es mejor que nada. – user187291

Respuesta

7

Interesante pregunta. Yo diría que que no es posible en absoluto, pero vamos a ver

Citando IBM - What's new in PHP5.3, Part 2

Un cierre es una función que se evalúa en su propio entorno, que tiene una o más variables encuadernadas que pueden ser accedido cuando se llama a la función.

y además (énfasis mío)

Variables a ser importado del ambiente exterior se especifican en la cláusula de uso de la definición de la función de cierre. Por defecto, se pasan por el valor, lo que significa que si actualizamos el valor pasado dentro de la definición de la función de cierre, no se actualizará el valor exterior.

Usando global pasaría por referencia y aunque es posible enlazar las variables por referencia con un cierre mediante el uso de & en la cláusula use, ya es una desviación del comportamiento 5,3 predeterminada.

$var = 'yes'; 
$fn = create_function('', 'global $var; $var = "no";'); 
$fn(); 
echo $var; // outputs no 

Puede copiar la variable global para usarla como valor, por ej.

$var = 'yes'; 
$fn = create_function('', 'global $var; $tmp = $var; $tmp = "no";'); 
$fn(); 
echo $var; // outputs yes 

Además, el valor de la variable global (cuando se utiliza create_function) no será evaluado (ligado) cuando se crea la función, pero cuando la función se ejecuta

$var = 'yes'; 
$fn = create_function('', 'global $var; $tmp = $var; return $tmp;'); 
$var = 'maybe'; 
echo $fn(); // outputs maybe 

$var = 'yes'; 
$fn = function() use ($var) { return $var; }; 
$var = 'maybe'; 
echo $fn(); // outputs yes 

También es importante

Cuando se define dentro de un objeto, una cosa útil es que el cierre tiene acceso completo al objeto a través de $ this variable, sin la necesidad de importarlo explícitamente. * Aunque creo que esto se abandonó en PHP5.3 última

Esto es imposible con la palabra clave global y que tampoco puede simplemente usar $this. No hay forma de hacer referencia a una propiedad de una clase al definir el cuerpo de la función con create_function.

class A { 

    protected $prop = 'it works'; 

    public function test() 
    { 
     $fn = create_function('', 'echo $this->prop;'); 
     return $fn; 
    } 
} 

$a = new A; 
$fn = $a->test(); 
$fn(); 

dará lugar a

Fatal error: Using $this when not in object context 

Para resumir esto
Si bien se puede crear una función de importación de una variable del ámbito mundial, no se puede no se puede crear uno usando variables de otro ámbito. Y como técnicamente no es vinculante al usar create_function, pero importando cuando se ejecuta la función creada, me gustaría argumentar que esta limitación hace que el cierre sea lambda.


EDITAR: La solución ofrecida por Onno Marsman a continuación es bastante decente. No simula completamente Closures, pero la implementación es bastante cercana.

+2

No hay palabras para describir lo horrible que es esto. (No es tu respuesta.) – ChaosPandion

+1

¡Gran respuesta! Pulgares hacia arriba. – aefxx

3

Mi solución: http://techblog.triptic.nl/simulating-closures-in-php-versions-prior-to-php-5-3/

Sin embargo, sí pasan las variables dentro de un objeto para el cierre como primer argumento.

+0

Muy buen esfuerzo, aunque tener que llamar a call() técnicamente lo descalifica como un Cierre en mi opinión (bueno, al menos algo). También encuentro que tengo que pasar la instancia de Cierre para que las variables cerradas sean un poco incómodas y call_user_func_array debería regresar. Pero aún así, muy agradable. ¿Lo modeló después de la clase de Cierre en 5.3? – Gordon

+0

Primero pensé en esta solución antes de darme cuenta de que incluso existía una clase de cierre interno en php 5.3. No me gusta la llamada a call() y usaría el método __invoke magic, solo si no estuviera disponible hasta php 5.3 ... –

+0

Gordon: ¿Qué quiere decir con "call_user_func_array should return"? –

1

Existen algunos casos especiales donde puede hacerlo.

Si necesita capturar una variable por valor (no por referencia), y el valor es un tipo de valor simple como un número, cadena o matriz de los anteriores (no tipos de referencia como objetos y recursos y funciones), a continuación, sólo tiene que introducirlo en la definición de función usando var_export():

$var = array(1, 3); 
$f = create_function('', 
    '$var=' . var_export($var,true) . '; return $var;'); 

Si necesita capturar la variable por referencia con el fin de mantener el estado mutable a través de llamadas de la función, pero no es necesario tener la cambios reflejados en el alcance original donde se creó (por ejemplo, para crear un acumulador, pero los cambios en la suma no necesitan cambiar la variable de suma en el ámbito de creación), entonces puede insertarlo de manera similar, pero como una variable estática:

function make_accumulator($sum) { 
    $f = create_function('$x', 
     'static $var=' . var_export($var,true) . '; return $var += $x;'); 
    return $f; 
} 
Cuestiones relacionadas