Лямбда-функции в PHP не логичны - PullRequest
13 голосов
/ 17 января 2010

Примечание : я сжал эту статью в своей личной вики: http://wiki.chacha102.com/Lambda - Наслаждайтесь

У меня проблемы с функциями стиля лямбда в PHP.

Во-первых, это работает:

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

Во-вторых, это работает:

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

В-третьих, это работает:

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

Но это не работает:

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

И это не работает

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

Мой вопрос Почему? , и как я могу заверить, что оба:

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

а это

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

правильно называются? Дополнительные баллы, если вы можете указать на документацию с указанием причины возникновения этой проблемы.

Ответы [ 7 ]

9 голосов
/ 17 января 2010

Это интересный вопрос. Это работает:

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

но, как вы говорите, это не так:

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

Если вам нужен объект, к которому вы можете динамически вызывать членов, попробуйте:

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

Но вы не можете заменить вызываемые методы таким образом, потому что __call() вызывается только для методов, которые либо не существуют, либо недоступны (например, они закрыты).

3 голосов
/ 17 января 2010

Функции и методы трактуются по-разному в PHP.Вы можете использовать runkit_method_add() для добавления метода в класс, но я не знаю, как добавить метод к экземпляру.

1 голос
/ 27 февраля 2011

ОП уже представил решение:

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

То есть, любому свойству, которое содержит функцию anon, присуща неоднозначность, поскольку добавление скобок после имени свойства говорит PHP, что вы вызываете метод, а не вызываете функцию anon в свойстве. Чтобы устранить эту неоднозначность, вы должны добавить степень разделения, сначала сохраняя свойство в локальной переменной, а затем вызывая функцию anon.

Вам просто нужно смотреть на свойство класса как на свойство, а не как «свойство, вызываемое как метод», независимо от его содержимого, и предполагать, что php будет искать метод реального класса, если вы поставите скобки после собственность.

1 голос
/ 17 января 2010

Интересный вопрос, хотя я не вижу причин, почему это должно работать:

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

У меня была похожая проблема с __invoke(), и я также не смог ее решить:

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

Решение?Никогда не используйте __invoke()!: P

1 голос
/ 17 января 2010

Самое близкое, что вы могли бы сделать, это сделать, используя перегрузку __call, чтобы проверить, содержит ли свойство замыкание:

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

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

Хотя имейте в виду следующее примечание из документов:

В настоящее время анонимные функции реализовано с использованием класса Closure. Это деталь реализации и не следует полагаться.

Ссылка: Анонимные функции

0 голосов
/ 03 июня 2010

Учтите это:

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

Вы имеете в виду $ obj-> foo () закрытие или $ obj-> foo () метод? Это неоднозначно, и PHP принимает решение, что вы имеете в виду метод. Чтобы использовать замыкание, вы должны понять, что вы имеете в виду. Один из способов сделать это - использовать временную переменную, как вы это сделали. Другой способ - использовать call_user_func($obj->foo).

Я не знаю другого легкого пути.

0 голосов
/ 03 июня 2010

Мне кажется, что это ошибка, а не «причуда»:

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

Это там, я просто не думаю, что PHP знает, как его запустить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...