Ссылка на переменные из содержимого области при использовании create_function в качестве замыкания.PHP - PullRequest
10 голосов
/ 27 июня 2011

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

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

    $inFn();
}

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

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

    $inFn = function(... 

с

    $inFn = create_function(...

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

Ответы [ 3 ]

1 голос
/ 27 июня 2011

Я наткнулся на этот ответ на другой вопрос , который вдохновил меня придумать следующее.Я не провёл тщательного тестирования, и я уверен, что его можно улучшить (и необходимость вызова 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 голос
/ 28 января 2013

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

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

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

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

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

0 голосов
/ 27 июня 2011

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

...