Можно ли моделировать закрытия в 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; }