2010-01-17 16 views
13

Nota: Ho condensato questo articolo nella mia persona wiki: http://wiki.chacha102.com/Lambda - Godetevifunzioni lambda in PHP non sono logiche

Sto avendo alcuni problemi con funzioni di stile Lambda in PHP.

In primo luogo, questo funziona:

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

In secondo luogo, questo funziona:

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

In terzo luogo, questo funziona:

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

Ma, questo non funziona:

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

E, questo non funziona

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

la mia domanda è Perché?, e come posso assicurare che sia questo:

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

e questo

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

sono chiamati in modo corretto? Punti extra se è possibile indicare la documentazione indicando perché si verifica questo problema.

risposta

9

Questa è una domanda interessante.Questo funziona:

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

ma come dici tu questo non lo fa:

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

Se si desidera un oggetto a cui è possibile chiamare i membri in modo dinamico, provate:

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)); 

Ma voi non è possibile sostituire i metodi callable in questo modo perché __call() viene chiamato solo per metodi che non esistono o non sono accessibili (ad esempio sono privati).

+0

Questo insieme ad altri commenti mi permette di impostare una classe 'stdObj' che posso usare per creare in modo affidabile un oggetto senza una classe (tipo di). Grazie! –

3

Funzioni e metodi sono trattati in modo diverso in PHP. È possibile utilizzare runkit_method_add() per aggiungere un metodo a una classe, ma non conosco alcun modo per aggiungere un metodo a un'istanza.

+0

Sembra strano. Se non c'è un metodo con il nome di 'bar', dovrebbe cercare la variabile' bar' e vedere se è callable, quindi questa riga '$ foo-> bar()' funziona. –

+4

Non sei necessariamente sbagliato. Ma il motore Zend non è costruito in questo modo. –

1

Il più vicino si potrebbe arrivare a fare questo accadere sarebbe utilizzando il __call di sovraccarico per verificare se una proprietà contiene una chiusura:

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

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

Anche se tenere a mente la seguente nota dalla documentazione:

Le funzioni anonime sono attualmente implementate utilizzando la classe Closure. Questo è un dettaglio di implementazione e non dovrebbe essere invocato.

Riferimento:Anonymous functions

+0

Un'altra opzione dovrebbe usare l'istruzione 'is_callable'. Non sono sicuro se questo mi dirà se è una chiusura però. –

+0

Questo non funzionerà ancora - se c'è una dichiarazione di metodo per 'foo()' il '__call()' non sarà chiamato. – leepowers

+0

Non sono troppo preoccupato di ignorare un metodo. Tornerò la mattina, questo codice potrebbe funzionare. Dovrei semplicemente creare una classe come 'stdCallableObj' che dovrebbe fungere da' stdClass' quando voglio assegnare delle chiusure ad esso. –

1

Domanda interessante, anche se non vedo alcun motivo per cui questo dovrebbe funzionare:

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

Ho avuto un problema simile con __invoke(), e non ho anche stati in grado di risolverlo:

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')? 

Soluzione? Non usare mai __invoke()! : P

0

A me sembra come un insetto piuttosto che un "vezzo":

<?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 

è lì, io non credo che PHP sappia correre esso.

0

Considerate questo:

<?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()); 

vuoi dire $ obj-> foo() la chiusura o $ obj-> foo() il metodo? È ambiguo e PHP prende la decisione che intendi il metodo. Per usare la chiusura, devi disambiguare ciò che intendi. Un modo per farlo è utilizzando una variabile temporanea come hai fatto tu. Un altro modo è utilizzare call_user_func($obj->foo).

Non conosco nessun altro modo semplice.

1

Il PO ha già presentato la soluzione:

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

Vale a dire, qualsiasi proprietà che contiene una funzione di anon ha un ambiguità, perché l'aggiunta di parentesi dopo il nome della proprietà dice PHP che si sta chiamando un metodo, e non invocare una funzione anon in una proprietà. Per risolvere questa ambiguità, è necessario aggiungere un grado di separazione memorizzando prima la proprietà in una variabile locale e quindi richiamando la funzione anon.

Devi solo guardare la proprietà della classe come una proprietà invece che come "una proprietà richiamabile come metodo" indipendentemente dal suo contenuto e supponiamo che php stia cercando un metodo di classe reale se inserisci una parentesi dopo la proprietà.

Problemi correlati