Можно ли имитировать замыкания в PHP 5.2.x без использования глобальных переменных? - PullRequest
9 голосов
/ 05 февраля 2010

Можно ли имитировать замыкания в PHP 5.2.x без использования глобальных переменных? Я мог бы придумать способ передать желаемые переменные в качестве дополнительных параметров замыканию, но это не похоже на лучшую практику.

Есть идеи?

Ответы [ 4 ]

7 голосов
/ 05 февраля 2010

Интересный вопрос. Я бы сказал, это вообще невозможно , но давайте посмотрим

Цитирование IBM - Что нового в PHP5.3, часть 2

Замыкание - это функция, которая оценивается в ее собственной среде, которая имеет одну или несколько связанных переменных, к которым можно обращаться при вызове функции.

и далее (выделено мое)

Переменные, которые должны быть импортированы из внешней среды, указаны в разделе использования определения функции замыкания. По умолчанию они передаются по значению , что означает, что если мы обновим значение, переданное в определении функции замыкания, оно не будет обновлять внешнее значение.

Использование global будет проходить по ссылке, и хотя можно связать переменные по ссылке с закрытием, используя & в предложении use, это уже отклонение от поведения по умолчанию 5.3.

$var = 'yes';
$fn  = create_function('', 'global $var; $var = "no";');
$fn();
echo $var; // outputs no

Вы можете скопировать глобальную переменную, чтобы использовать ее по значению, например,

$var = 'yes';
$fn  = create_function('', 'global $var; $tmp = $var; $tmp = "no";');
$fn();
echo $var; // outputs yes

Кроме того, значение глобальной переменной (при использовании create_function) не будет оцениваться (привязываться) при создании функции, но при ее запуске

$var = 'yes';
$fn  = create_function('', 'global $var; $tmp = $var; return $tmp;');
$var = 'maybe';
echo $fn(); // outputs maybe

$var = 'yes';
$fn  = function() use ($var) { return $var; };
$var = 'maybe';
echo $fn(); // outputs yes

Также важно

При определении внутри объекта одна удобная вещь заключается в том, что замыкание имеет полный доступ к объекту через переменную $ this без необходимости явного импорта. * Хотя я думаю, что это было отброшено в финальной версии PHP5.3

Это невозможно с ключевым словом global, и вы также не можете просто использовать $this. При определении тела функции с помощью create_function.

невозможно сослаться на свойство из класса.
class A {

    protected $prop = 'it works';

    public function test()
    {
        $fn = create_function('', 'echo $this->prop;');
        return $fn;
    }
}

$a = new A;
$fn = $a->test();
$fn();

приведет к

Fatal error: Using $this when not in object context

Подводя итог
Хотя вы можете создать функцию, импортирующую переменную из глобальной области, вы не можете создать функцию, используя переменные из другой области. И поскольку вы технически не привязываетесь при использовании create_function, а импортируете при выполнении созданной функции, я бы хотел возразить , что это ограничение делает закрытие лямбда .


РЕДАКТИРОВАТЬ: Решение, предложенное Onno Marsman ниже довольно прилично, хотя. Он не полностью имитирует замыкания, но реализация довольно близка.

3 голосов
/ 01 апреля 2010

Мое решение: http://techblog.triptic.nl/simulating-closures-in-php-versions-prior-to-php-5-3/

Однако в качестве первого аргумента он передает переменные в объекте замыканию.

2 голосов
/ 05 февраля 2010

Вы имеете в виду Карри как http://en.wikipedia.org/wiki/Currying

Тогда http://zaemis.blogspot.com/2009/06/currying-in-php.html

Если нет, не бери в голову. : -)

1 голос
/ 28 октября 2011

В некоторых особых случаях это можно сделать.

Если вам нужно захватить переменную по значению (а не по ссылке), и значение представляет собой простой тип значения, такой как число, строка илимассив выше (не ссылочные типы, такие как объекты, ресурсы и функции), то вы можете просто вставить его в определение функции, используя var_export():

$var = array(1, 3);
$f = create_function('',
    '$var=' . var_export($var,true) . '; return $var;');

Если вам нужно захватить переменную по ссылке вДля того чтобы поддерживать изменяемое состояние при вызовах функции, но вам не нужно отражать изменения в исходной области видимости, в которой она была создана (например, для создания аккумулятора, но изменения в сумме не нужно изменять суммупеременная в области создания), то вы можете вставить ее аналогичным образом, но в качестве статической переменной:

function make_accumulator($sum) {
    $f = create_function('$x',
        'static $var=' . var_export($var,true) . '; return $var += $x;');
    return $f;
}
...