使用create_function作为闭包时,如何从包含范围引用变量。PHP

10

使用真正的闭包,我们可以做到:

function foo(&$ref)
{
    $inFn = function() use (&$ref)
    {   
        $ref = 42; 
    };  

    $inFn();
}

因此可以修改引用而无需在调用$inFn时传递它。

如果我们替换,

    $inFn = function(... 

随着

    $inFn = create_function(...

有没有(简单明了的)方法来做同样的事情:在不显式传递到$inFn中的情况下,通过引用引用包含范围的变量?

3个回答

1
我看到了另一个问题的这个答案,它启发了我想出了以下解决方案。我没有进行过大量测试,我相信它可以改进(并且需要exec调用是一种遗憾),但它似乎解决了我的问题。
class create_closure
{
    private 
        $_cb  = null,
        $_use = array();

    public function __construct(array $use, $closure_args, $closure_body)
    {   
        $use_args = implode(array_keys($use), ',');

        $this->_cb = create_function(
            $use_args.($use_args==='' OR $closure_args==='' ? '' : ',').$closure_args,
            $closure_body
        );  

        $this->_use = array_values($use);

    }   

    public static function callback(array $use, $closure_args, $closure_body)
    {   
        $inst = new self($use, $closure_args, $closure_body);
        return array($inst, 'exec');
    }   

    public function exec()
    {   
        return call_user_func_array(
                $this->_cb,
                array_merge($this->_use, func_get_args())
        );  
    }   
}

你可以像这样使用它:

function foo(&$ref)
{
    $inFn = new create_closure(
        array('$ref'=>&$ref), 
        '', 
        '$ref=42;'
    );
    $inFn->exec();
}

$x = 23;
echo 'Before, $x = ', $x, '<br>';
foo($x);
echo 'After,  $x = ', $x, '<br>';

这将返回:

Before, $x = 23
After, $x = 42

或者像这样:

function bar()
{           
    $x = 0;
    echo 'x is ', $x, '<br>';

    $z = preg_replace_callback(
        '#,#',
        create_closure::callback(
            array('$x'=>&$x),
            '$matches',
            'return ++$x;
            '
        ),
        'a,b,c,d'
    );

    echo 'z is ', $z, '<br>';
    echo 'x is ', $x, '<br>';
}       

bar(); 

这将返回:

x is 0
z is a1b2c3d
x is 3

1

有没有一种(简单而干净的)方法可以做同样的事情,即引用包含范围的变量,而无需显式地将其传递到$inFn中?

简单的答案是:没有。

create_function只是一个包装器,用于创建伪匿名函数并返回其名称(即'lambda_1'),它本质上是一个eval。您不能使用它创建闭包(这些是内部Closure类的实例,是全新的语言结构,与普通PHP函数完全不同)。我猜您之所以问这个问题,是因为您想在旧版本的PHP上使用闭包*。但是由于它们在5.3之前不存在,因此您无法使用它们。

如果您可以接受非简单、非干净的解决方案,请查看其他答案。

*)也许这需要一点澄清。闭包不仅意味着匿名函数,它们确切地涉及到您的问题:记忆调用上下文。普通函数无法做到这一点。


0

看起来你可以直接使用$inFn = create_function('&$ref', ...);。你试过了吗?


1
谢谢。是的,我知道这可以做到,但我真的想找一种不必将其作为参数传递的方法来实现它。 - tjm
你是什么意思?这就是 create_function 的工作方式。你打算如何使用它? - jtbandes
2
举个例子(目前我发现自己处于其中之一的情况),当create_function函数被用作回调函数传递给preg_replace_callback时,它只会传递一个“matches”参数。但是在函数体内,我需要通过引用访问包含范围内的变量。 - tjm
2
嗯,听起来你可以使用对象方法作为回调函数,这样你就可以访问该对象的数据。或者(虽然这可能不是一个好主意),你可以使用 $GLOBALS 数组。 - jtbandes

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