如何在PHP类中使用匿名函数?

6
我如何在这个类中调用$greet?我使用的是PHP 5.5.4。
<?PHP   
class Model
{
    public $greet = function($name)
    {
        printf("Hello %s\r\n", $name);
    };
}

$test = new Model();
$test->greet('World');
$test->greet('PHP');
?>

Parse error: syntax error, unexpected '$greet' (T_VARIABLE), expecting function (T_FUNCTION)

也尝试了这个,

$test = new Model();
call_user_func($test->greet('World'));
call_user_func($test->greet('PHP')) 

匿名函数在类外部可以正常工作(直接从manual复制)。
<?php
$greet = function($name)
{
    printf("Hello %s\r\n", $name);
};

$greet('World');
$greet('PHP');
?>

编辑:我在我的调用中删除了美元符号(当答案开始出现时,我注意到了这一点)。这并没有帮助。

call_user_func($test->greet('World'));
call_user_func($test->greet('PHP'));

编辑:

class Model
{
    public $greet;
    function __construct()
    {
        $this->greet = function($name)
        {
            printf("Hello %s\r\n", $name);
        };
    }
}

$test = new Model();
$test->greet('johnny');

现在我明白了,
Fatal error: Call to undefined method Model::greet() 
2个回答

8

当您调用 greet 时,PHP将其视为一个函数而不是属性。在PHP中,您可以为属性和方法使用相同的名称,因此区分很重要。

PHP 7+

在PHP7中,由于统一变量语法,不再需要使用__call()方法来调用绑定到属性的闭包。这将允许您像算术运算一样在任何代码周围添加括号。

class Model
{
    public $greet;
    function __construct()
    {
        $this->greet = function($name)
        {
            printf("Hello %s\r\n", $name);
        };
    }
}

$test = new Model();
($test->greet)('johnny');

PHP 5

作为一种解决方法,您可以使用__call()魔术方法。它可以捕获对未定义的greet方法的调用。

class Model
{
    public $greet;
    function __construct()
    {
        $this->greet = function($name)
        {
            printf("Hello %s\r\n", $name);
        };
    }

    function __call($method, $args)
    {
        if (isset($this->$method) && $this->$method instanceof \Closure) {
            return call_user_func_array($this->$method, $args);
        }

        trigger_error("Call to undefined method " . get_called_class() . '::' . $method, E_USER_ERROR);
    }
}

$test = new Model();
$test->greet('johnny');

为什么你必须在构造函数或其他方法中分配函数?如果不这样做,它就无法工作(对我来说)。 - johnny
你是指第一个例子中的“解析错误(Parse error)”吗?在类定义中使用function关键字意味着创建一个方法,而不是匿名函数。 - Arnold Daniels
不,我的意思是当你将匿名函数分配给变量时。它必须在函数(或构造函数)中完成。它不能仅仅是一个成员变量。 - johnny
1
@johnny 这是一个两阶段的过程。PHP 代码首先由 Zend 引擎 解释,然后再执行。类定义(包括方法和属性)由解释器处理,而创建对象、匿名函数等则在代码执行时完成。 - Arnold Daniels
1
@johnny 在PHP7中,__call方法将不再需要。目前PHP7处于alpha版本。我已经相应地更新了答案。 - Arnold Daniels

3

您不能使用表达式的结果来初始化对象变量。只允许使用静态/常量值。例如:

class foo {
   public $bar = 1+1; // illegal - cannot use an expression
   public $baz = 2; // valid. '2' is a constant
}

您需要执行以下步骤:

class foo {
   public $bar;
   function __construct() {
        $this->bar = function() { .... }
   }
}

按照这个答案的说法,要实际调用闭包:

$x = new foo()
$x->bar->__invoke();

我该如何在类外调用它?我是否需要像通常处理普通变量那样将其包装在另一个返回值的函数中?我尝试了各种方法,但它们都不起作用。 - johnny
如果在构造函数中构建闭包,则 $barnew 调用返回时将“指向”该闭包。因此,$x = new foo(); $x->bar(); 应按预期工作。 - Marc B
请问您能告诉我在我的上面的修改中做错了什么吗?编辑:抱歉,我忘记加入编辑内容了! - johnny
谢谢。这似乎是很麻烦的事情,也许我需要重新考虑是否使用它。例如,在Javascript中它看起来完全不同。 - johnny
1
是的,嗯,欢迎来到 PHP。在这里,一切都是虚构的,一致性并不重要。 - Marc B
如果您可以编辑您的答案并放入该链接,我可以将其标记为已回答。非常感谢您的帮助。 - johnny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接