2010-01-17 15 views
13

Nota: He condensado este artículo en mi persona wiki: http://wiki.chacha102.com/Lambda - Disfrutefunciones lambda en PHP no son lógicas

que estoy teniendo con funciones de estilo Lambda en PHP con algunas dificultades.

En primer lugar, esto funciona:

$foo = function(){ echo "bar"; }; 
$foo(); 

En segundo lugar, esto funciona:

class Bar{ 
    public function foo(){ 
     echo "Bar"; 
    } 

En tercer lugar, esto funciona:

$foo = new stdClass; 
$foo->bar = function(){ echo "bar"; }; 
$test = $foo->bar; 
$test(); 

embargo, esto no funciona:

$foo = new stdClass; 
$foo->bar = function(){ echo "bar"; }; 
$foo->bar(); 

Y, esto no funciona

class Bar{ 
    public function foo(){ 
     echo "Bar"; 
    } 
$foo = new Bar; 
$foo->foo = function(){ echo "foo"; }; 
$foo->foo(); // echo's bar instead of Foo. 

Mi pregunta es ¿Por qué?, y cómo puedo asegurar que tanto esto:

$foo->bar = function(){ echo "test"; }; 
$foo->bar(); 

y esto

$foo = new Bar; 
$foo->bar(); 

son llamados correctamente? Puntos extra si puede señalar documentación que indique por qué ocurre este problema.

Respuesta

9

Ésta es una pregunta interesante.Esto funciona:

$a = new stdClass; 
$a->foo = function() { echo "bar"; }; 
$b = $a->foo; 
$b(); // echos bars 

sino como se dice esto no es así:

$a = new stdClass; 
$a->foo = function() { echo "bar"; }; 
$a->foo(); 

Si desea un objeto al que se le puede llamar de forma dinámica miembros, probar:

class A { 
    public function __call($func, $args) { 
    $f = $this->$func; 
    if (is_callable($f)) { 
     return call_user_func_array($f, $args); 
    } 
    } 
} 

$a = new A; 
$a->foo = function() { echo "bar\n"; }; 
$a->foo2 = function($args) { print_r($args); }; 
$a->foo(); 
$a->foo2(array(1 => 2, 3 => 4)); 

Pero no puede reemplazar los métodos invocables de esta forma porque solo se llama a __call() para métodos que no existen o que no son accesibles (p. ej., son privados).

+0

Esto junto con otros comentarios me permiten configurar una clase 'stdObj' que pueda usar para crear de manera confiable un objeto sin una clase (tipo de). ¡Gracias! –

3

Las funciones y los métodos se tratan de manera diferente en PHP. Puede usar runkit_method_add() para agregar un método a una clase, pero no conozco ninguna forma de agregar un método a una instancia.

+0

Parece extraño. Si no hay un método con el nombre de 'barra', debería buscar la' barra' variable y ver si se puede llamar, así que esta línea '$ foo-> barra()' funciona. –

+4

No es necesariamente incorrecto. Pero el motor Zend no está construido de esa manera. –

1

Lo más cerca que se podía llegar a hacer que esto suceda sería mediante el uso de la sobrecarga __call para comprobar si una propiedad contiene un cierre:

class what { 
    function __call($name, $args) { 
    $f= $this->$name; 
    if ($f instanceof Closure) { 
     $f(); 
    } 
    } 
} 

$foo = new what(); 
$foo->bar = function(){ echo "bar"; }; 
$foo->bar(); 

oso Aunque en cuenta la siguiente nota de los documentos:

Las funciones anónimas actualmente son implementadas usando la clase Closure. Este es un detalle de implementación y no se debe confiar en .

Referencia:Anonymous functions

+0

Otra opción sería usar la instrucción 'is_callable'. No estoy seguro de si eso me dirá si es un cierre. –

+0

Esto todavía no funciona: si hay una declaración de método para 'foo()' no se llamará a '__call()'. – leepowers

+0

No estoy demasiado preocupado por anular un método. Volveré por la mañana, este código podría funcionar. Tendría que crear una clase como 'stdCallableObj' que actuaría como' stdClass' cuando yo quiera asignarle cierres. –

1

pregunta interesante aunque veo ninguna razón por la que esto debería funcionar:

class Bar{ 
    public function foo(){ 
     echo "Bar"; 
    } 
$foo = new Bar; 
$foo->foo = function(){ echo "foo"; }; 
$foo->foo(); // echo's bar instead of Foo. 

He tenido un problema similar con __invoke(), y yo también no haber sido capaz de resolverlo:

class base { 
    function __construct() { 
     $this->sub = new sub(); 
    } 

    function __call($m, $a) { 
    } 
} 

class sub { 
    function __invoke($a) { 
    } 
} 

$b = new base(); 
$b->sub('test'); // should trigger base::__call('sub', 'test') or sub::__invoke('test')? 

¿Solución? ¡Nunca use __invoke()! : P

0

A mí me parece como un insecto en lugar de un "capricho":

<?php 
$t = new stdClass; 
$t->a = function() { echo "123"; }; 
var_dump($t); 
echo "<br><br>"; 
$x = (array)$t; 
var_dump($x); 
echo "<br><br>"; 
$x["a"](); 
echo "<br><br>"; 
?> 

object(stdClass)#1 (1) { ["a"]=> object(Closure)#2 (0) { } } 

array(1) { ["a"]=> object(Closure)#2 (0) { } } 

123 

Es allí, yo no creo que PHP sabe cómo ejecutar eso.

0

Considera:

<?php 

class A { 

    public $foo 

    public function foo() { 
     return 'The method foo'; 
    } 

} 

$obj = new A; 
$obj->foo = function() { return 'The closure foo'; }; 

var_dump($obj->foo()); 

Qué quiere decir $ obj-> foo() el cierre o $ obj-> foo() el método? Es ambiguo y PHP toma la decisión de que te refieras al método. Para usar el cierre, debes desambiguar a qué te refieres. Una forma de hacerlo es usar una variable temporal como lo has hecho. Otra forma es usar call_user_func($obj->foo).

No conozco otra manera fácil.

1

El PO ya ha presentado la solución:

$foo = new stdClass; 
$foo->bar = function(){ echo "bar"; }; 
$test = $foo->bar; 
$test(); 

Es decir, cualquier propiedad que contiene una función anon tiene una ambigüedad inherente debido a la adición de paréntesis después del nombre de la propiedad le dice a PHP que está llamando a un método, y no invocando una función anon en una propiedad. Para resolver esta ambigüedad, debe agregar un grado de separación almacenando primero la propiedad en una variable local y luego invocando la función anon.

Solo tiene que mirar la propiedad de la clase como una propiedad en lugar de como "una propiedad invocable como método" independientemente de sus contenidos y suponga que php va a buscar un método de clase real si coloca paréntesis después de la propiedad.

Cuestiones relacionadas