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

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

Есть идеи?

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

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

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

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

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

Использование 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 ссылаться на свойство класса при определении тела функции с помощью 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 но импортируете, когда созданная функция выполняется, я хотел бы утверждать, что это ограничение делает закрытие лямбдой .


EDIT: Решение, предлагаемое Onno Marsman ниже, довольно прилично. Он не полностью имитирует Closures, но реализация довольно близка.

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

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

Вы имеете в виду Currying like http://en.wikipedia.org/wiki/Currying

Затем http://zaemis.blogspot.com/2009/06/currying-in-php.html

Если нет, неважно. 🙂

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

Если вам нужно захватить переменную по значению (не по ссылке), а значение представляет собой простой тип значения, например число, строку или массив из вышеперечисленного (не ссылочные типы, такие как объекты, ресурсы и функции), тогда вы можете просто вставьте его в определение функции, используя 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; }