【问题标题】:Referencing variables from containing scope when using create_function as a closure. PHP使用 create_function 作为闭包时从包含范围引用变量。 PHP
【发布时间】:2011-09-23 02:45:15
【问题描述】:

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

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

    $inFn();
}

因此修改引用而无需在调用中将其传递给$inFn

如果我们替换,

    $inFn = function(... 

    $inFn = create_function(...

是否有任何(简单而干净的)方法来做同样的事情?通过引用不明确地将其传递来引用包含范围的变量到$inFn?

【问题讨论】:

    标签: php reference closures


    【解决方案1】:

    我遇到了this answer to another question,这启发了我提出以下建议。我没有对它进行大量测试,我相信它可以改进(并且需要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
    

    【讨论】:

      【解决方案2】:

      是否有任何 (简单而干净) 方法来做同样的事情;参考一个 通过引用而不显式地引用包含范围的变量 将其传递给 $inFn?

      简单的回答:没有。

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

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

      *) 也许这需要一点澄清。闭包不仅仅意味着匿名函数,它们正是关于你的问题:记忆调用上下文。普通函数做不到。

      【讨论】:

        【解决方案3】:

        看来您可以只使用$inFn = create_function('&amp;$ref', ...);。你试过吗?

        【讨论】:

        • 谢谢。是的,我知道这是可以做到的,但我真的在寻找一种无需将其作为参数传递的方法。
        • 什么意思?这就是create_function 的工作原理。您打算如何使用它?
        • 例如(我目前遇到的一种情况)当create_function 函数用作preg_replace_callback 的回调时,它只会传递一个“匹配”参数。但在函数体中,我需要通过引用访问包含范围内的变量。
        • 嗯。听起来您可以使用对象方法作为回调,因此您可以访问该对象的数据。或者(尽管这可能不是一个好主意)您可以改用 $GLOBALS 数组。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-13
        • 1970-01-01
        • 2015-09-27
        相关资源
        最近更新 更多