Ссылка на переменные из области видимости при использовании create_function в качестве закрытия. PHP

Используя истинные замыкания, мы можем сделать,

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

    $inFn();
}

тем самым изменяя ссылку, не передавая ее при вызове $inFn.

Если мы заменим,

    $inFn = function(... 

с

    $inFn = create_function(...

есть ли (простой и чистый) способ сделать то же самое; ссылайтесь на переменную содержащейся области действия с помощью ссылки , не передавая ее явно в $inFn?

Ответ 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

Ответ 2

Похоже, вы можете просто использовать $inFn = create_function('&$ref', ...);. Вы пробовали это?

Ответ 3

существует ли (простой и чистый) способ сделать то же самое; обратитесь к переменная содержащейся области действия без ссылки передавая его в $inFn?

Простой ответ: нет.

create_function является не чем иным, как оболочкой для eval, которая создает псевдо-анонимную функцию и возвращает ее имя (т.е. 'lambda_1'). Вы не можете создавать с ним закрытие (это экземпляры внутреннего класса Closure, новая конструкция языка , полностью отличная от обычных функций PHP. Я предполагаю, что вы спрашиваете об этом, потому что вы хотите использовать закрытие * на старой версии PHP. Но так как они не существовали до 5.3, вы не можете их использовать.

Если вы можете жить с непростым, не чистым решением, посмотрите на другие ответы.

*) Возможно, это может потребовать немного разъяснений. Закрытие означает не только анонимную функцию, но и именно ваш вопрос: запоминание вызывающего контекста. Нормальная функция не может этого сделать.