Передача внешних переменных в анонимную функцию через use
Использование use в анонимных функциях PHP
В PHP замыкание (анонимная функция) не имеет доступа к переменным из внешней области видимости, если только они не переданы явно через конструкцию use. Это позволяет захватывать нужные переменные по значению или по ссылке, контролируя область видимости и побочные эффекты.
Основной синтаксис:
$message = 'Привет';
$closure = function() use ($message) {
echo $message;
};
$closure(); // Выведет 'Привет'Php function use (use в замыканиях php)
Переменная $message копируется в замыкание в момент его определения. Изменение $message после определения замыкания не повлияет на вывод.
Как передать переменную по ссылке, чтобы замыкание могло её изменять?
Используйте амперсанд перед именем переменной в use:
$count = 0;
$increment = function() use (&$count) {
$count++;
};
$increment();
$increment();
echo $count; // 2Замыкание теперь работает с той же переменной, а не с её копией. Любые изменения видны и вне замыкания.
Типичная ошибка:
Попытка передать по ссылке несуществующую переменную или передать результат функции (например, use (&time())) приведёт к синтаксической ошибке. use принимает только переменные.
Как захватить несколько внешних переменных?
Перечислите их через запятую в use:
$a = 1;
$b = 2;
$sum = function() use ($a, $b) {
return $a + $b;
};
echo $sum(); // 3Можно смешивать передачу по значению и по ссылке: use ($x, &$y).
Как захватить объект $this в замыкании до PHP 7.4?
В PHP 5.4+ $this автоматически доступен внутри анонимной функции, объявленной в контексте класса. Но если нужно явно захватить именно текущий объект (например, для сохранения в переменной или использования в статическом контексте), используйте use ($this):
class MyClass {
private $data = 'секрет';
public function getClosure() {
return function() use ($this) {
return $this->data;
};
}
}
$obj = new MyClass();
$closure = $obj->getClosure();
echo $closure(); // 'секрет'Проблема:
Если замыкание статическое (static function()), то $this недоступен, даже через use. Также нельзя передать $this по ссылке (это запрещено).
Как обойти проблему позднего связывания в цикле с использованием use?
При создании замыканий в цикле все они захватывают одну и ту же переменную по значению, которая к моменту вызова уже может быть изменена. Классический пример:
$funcs = [];
for ($i = 1; $i <= 3; $i++) {
$funcs[] = function() use ($i) { echo $i; };
}
$funcs[0](); $funcs[1](); $funcs[2](); // Вывод: 3 3 3
// Ожидалось 1 2 3Решение - захватить копию на каждой итерации, например, создав новую переменную в цикле или используя use (&$i) с передачей по ссылке и немедленным вызовом. Правильный вариант через копию:
$funcs = [];
for ($i = 1; $i <= 3; $i++) {
$j = $i;
$funcs[] = function() use ($j) { echo $j; };
}
$funcs[0](); $funcs[1](); $funcs[2](); // 1 2 3Или используя use (&$i) с немедленным вызовом, но это меняет исходную переменную.
Типичная ошибка:
Забыть, что use копирует значение в момент определения замыкания, а не в момент вызова. Это приводит к неожиданным результатам при работе с циклами и событиями.
Как использовать use с функциями высшего порядка (например, array_map, array_filter)?
Передавайте внешние данные через use:
$factor = 2;
$numbers = [1, 2, 3];
$multiplied = array_map(function($n) use ($factor) {
return $n * $factor;
}, $numbers);
print_r($multiplied); // [2, 4, 6]Это удобно, когда нужны данные из внешнего контекста, но не хочется передавать их как аргумент.
Как передать весь текущий контекст (все переменные) в замыкание?
Такой прямой возможности нет. Можно использовать compact для создания массива и захватить его через use:
$name = 'Иван';
$age = 30;
$context = compact('name', 'age');
$closure = function() use ($context) {
echo "{$context['name']}, {$context['age']} лет";
};
$closure(); // Иван, 30 летЭто удобно, когда переменных много и не хочется перечислять каждую.
Расширенные примеры использования use
Замыкание, изменяющее внешнюю переменную через ссылку
$total = 0;
$items = ['a', 'b', 'c'];
$addToTotal = function($value) use (&$total) {
$total += strlen($value);
};
array_walk($items, $addToTotal);
echo $total; // 33
Замыкание $addToTotal использует &$total для накопления суммы. Изменения видны после вызова array_walk.
Замыкание с рекурсией (захват самого себя)
$factorial = function($n) use (&$factorial) {
return $n <= 1 ? 1 : $n * $factorial($n - 1);
};
echo $factorial(5); // 120120
Ключевой момент - передача $factorial по ссылке (&$factorial), чтобы внутри замыкания можно было обратиться к этой же переменной (рекурсия). Без & переменная $factorial внутри замыкания не была бы определена.
Возврат замыкания с захватом из внешней функции
function createMultiplier($factor) {
return function($x) use ($factor) {
return $x * $factor;
};
}
$double = createMultiplier(2);
echo $double(5); // 10
$triple = createMultiplier(3);
echo $triple(10); // 3010 30
Каждое созданное замыкание захватывает своё значение $factor.
Позднее связывание с передачей по значению
$var = 'первый';
$closure = function() use ($var) {
echo $var;
};
$var = 'второй';
$closure(); // 'первый' – захватилось первоначальное значениепервый
Если нужно видеть последнее значение, используйте ссылку:
$var = 'первый';
$closure = function() use (&$var) {
echo $var;
};
$var = 'второй';
$closure(); // 'второй'второй
Использование use с array_reduce для работы с внешним аккумулятором
$prefix = 'Результат: ';
$numbers = [10, 20, 30];
$sum = array_reduce($numbers, function($carry, $item) use ($prefix) {
echo $prefix . $carry . "\n";
return $carry + $item;
}, 0);
echo "Сумма: $sum";Результат: 0 Результат: 10 Результат: 30 Сумма: 60
Здесь $prefix захвачен по значению и используется внутри callback-функции, переданной в array_reduce.
Захват объектов по ссылке – влияние на оригинал
$obj = new stdClass();
$obj->value = 1;
$modifier = function() use (&$obj) {
$obj->value = 999;
};
$modifier();
echo $obj->value; // 999999
Изменение объекта через ссылку внутри замыкания отражается на исходном объекте.
Конфликт имён: переменная с тем же именем, что и в use
$name = 'глобальное';
$closure = function($name) use ($name) {
echo $name; // выведет значение, переданное как аргумент
};
$closure('локальное'); // 'локальное'локальное
Аргумент функции имеет приоритет над захваченной переменной. Параметр $name затеняет захваченное значение.
Замыкание как callback для usort с внешним параметром
$order = 'desc';
$array = [3, 1, 2];
usort($array, function($a, $b) use ($order) {
if ($order === 'desc') {
return $b - $a;
}
return $a - $b;
});
print_r($array); // [3, 2, 1]Array
(
[0] => 3
[1] => 2
[2] => 1
)Параметр сортировки ($order) передаётся через use, что делает функцию сортировки гибкой.