Лямбда-функции в PHP не логичны
Примечание. Я сконструировал эту статью в своей персоне wiki: http://wiki.chacha102.com/Lambda - Наслаждайтесь
У меня возникают проблемы с функциями стиля Lambda в PHP.
Во-первых, This Works:
$foo = function(){ echo "bar"; };
$foo();
Во-вторых, This Works:
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 bar instead of Foo.
Мой вопрос Почему?, и как я могу заверить, что оба этого:
$foo->bar = function(){ echo "test"; };
$foo->bar();
и этот
$foo = new Bar;
$foo->bar();
называются правильно? Дополнительные баллы, если вы можете указать документацию о том, почему эта проблема возникает.
Ответы
Ответ 1
Это интересный вопрос. Это работает:
$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()
вызывается только для методов, которые либо не существуют, либо недоступны (например, они являются частными).
Ответ 2
Функции и методы обрабатываются по-разному в PHP. Вы можете использовать runkit_method_add()
, чтобы добавить метод в класс, но я не знаю, как добавить метод к экземпляру.
Ответ 3
Ближе всего вы можете это сделать, используя перегрузку __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. Это деталь реализации и не следует полагаться.
Ссылка: Анонимные функции
Ответ 4
Интересный вопрос, хотя я не вижу причин, почему это должно работать:
class Bar{
public function foo(){
echo "Bar";
}
$foo = new Bar;
$foo->foo = function(){ echo "foo"; };
$foo->foo(); // echo 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
Ответ 5
OP уже представил решение:
$foo = new stdClass;
$foo->bar = function(){ echo "bar"; };
$test = $foo->bar;
$test();
I.e., любое свойство, содержащее функцию anon, имеет присущую неоднозначность, поскольку добавление скобок после имени свойства сообщает PHP, что вы вызываете метод, а не вызываете функцию anon в свойстве. Чтобы устранить эту двусмысленность, вы должны добавить степень разделения, сначала сохраните свойство в локальной переменной, а затем вызовите функцию anon.
Вам просто нужно посмотреть свойство класса как свойство, а не как "свойство, вызываемое как метод", независимо от его содержимого, и предположить, что php будет искать метод реального класса, если вы поместите скобки после свойство.
Ответ 6
Для меня это похоже на ошибку, а не на "quirk":
<?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 знает, как его запустить.
Ответ 7
Рассмотрим это:
<?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)
.
Я не знаю другого простого способа.