2010-01-17 18 views
13

Uwaga: Mam skrapla ten artykuł do mojej osoby wiki: http://wiki.chacha102.com/Lambda - CieszLambda Funkcje w PHP nie są logiczne

Mam pewne problemy z funkcjami typu Lambda w PHP.

Po pierwsze, to działa:

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

drugie, to działa:

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

trzecie, działa to:

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

Ale to nie działa:

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

I to nie działa

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

moje pytanie jest Dlaczego?, i jak mogę zapewnić, że zarówno w tym:

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

i to

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

nazywane są prawidłowo? Dodatkowe punkty, jeśli możesz wskazać dokumentację stwierdzającą, dlaczego występuje ten problem.

Odpowiedz

9

To jest ciekawe pytanie.To działa:

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

ale jak mówisz, to nie:

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

Jeśli chcesz obiekt, do którego można zadzwonić dynamicznie członków, spróbuj:

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

ale ty nie można zastąpić metod wywoływanych w ten sposób, ponieważ __call() jest wywoływana tylko dla metod, które nie istnieją lub nie są dostępne (np. są prywatne).

+0

To razem z innymi komentarzami pozwala mi ustawić klasę 'stdObj', której mogę użyć do niezawodnego tworzenia obiektów bez klasy (rodzaju). Dzięki! –

3

Funkcje i metody są traktowane inaczej w PHP. Możesz użyć metody runkit_method_add(), aby dodać metodę do klasy, ale nie wiem, jak dodać metodę do instancji.

+0

Po prostu wydaje się dziwne. Jeśli nie ma metody o nazwie 'bar', powinna ona szukać zmiennej' bar' i sprawdzić, czy można ją wywołać, więc ta linia '$ foo-> bar()' działa. –

+4

Niekoniecznie musisz się mylić. Ale silnik Zend nie jest zbudowany w ten sposób. –

1

Najbliższe można dostać się do realizacji tego byłoby za pomocą przeciążenie __call aby sprawdzić, czy nieruchomość zawiera Zamknięcie:

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

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

chociaż pamiętać o następujących list od docs:

Funkcje anonimowe są obecnie realizowane za pomocą klasy Zamknięcie. To jest szczegół implementacji i nie należy polegać na .

referencyjny:Anonymous functions

+0

Inną opcją byłoby użycie instrukcji 'is_calla'. Nie jestem pewien, czy to mi powie, czy jest to zamknięcie. –

+0

To nadal nie zadziała - jeśli istnieje deklaracja metody dla 'foo()', '__call()' nie zostanie wywołane. – leepowers

+0

Nie jestem zbytnio zaniepokojony przesłonięciem metody. Wrócę rano, ten kod może zadziałać. Po prostu musiałbym stworzyć klasę taką jak 'stdCallableObj', która działałaby jako' stdClass', gdy chcę przypisać do niej klasę zamknięcia. –

1

Ciekawe pytanie, choć nie widzę powodu, dlaczego to powinno działać:

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

miałem podobny problem z __invoke() i nie byli też w stanie go rozwiązać:

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

Rozwiązanie? Nigdy nie używaj __invoke()! : P

0

Dla mnie wydaje się błędem, a nie "dziwactwo":

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

To tam, po prostu nie sądzę, że PHP wie jak uruchomić to.

0

Rozważ to:

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

Czy chodziło Ci o $ obj-> foo() zamknięcie lub $ obj-> foo() metoda? Jest niejednoznaczny i PHP podejmuje decyzję, że masz na myśli tę metodę. Aby użyć zamknięcia, musisz rozróżnić, co masz na myśli. Jednym ze sposobów, w jaki możesz to zrobić, jest użycie zmiennej tymczasowej. Innym sposobem jest użycie call_user_func($obj->foo).

Nie znam żadnej innej łatwej drogi.

1

PO zaprezentowała już rozwiązanie:

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

tj jakąkolwiek usługę, która zawiera funkcja anon posiada swoistą niejednoznaczność ponieważ dodanie nawiasów po nazwie własności mówi PHP, które są wywołanie metody, a nie wywołanie funkcji anon w usłudze. Aby rozwiązać tę niejednoznaczność, należy dodać stopień separacji, najpierw przechowując właściwość na zmiennej lokalnej, a następnie wywołując funkcję anon.

Wystarczy spojrzeć na właściwość klasy jako właściwość zamiast na "właściwość wywoływaną jako metoda" bez względu na to, jaka jest zawartość i zakładamy, że php będzie szukał metody klasowej, jeśli wstawisz nawias po nieruchomości.