Передача внешних переменных в анонимную функцию через use

Раздел: PHP -> анонимные функции

Использование 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; // 3
3

Замыкание $addToTotal использует &$total для накопления суммы. Изменения видны после вызова array_walk.

Замыкание с рекурсией (захват самого себя)

Пример
$factorial = function($n) use (&$factorial) {
    return $n <= 1 ? 1 : $n * $factorial($n - 1);
};
echo $factorial(5); // 120
120

Ключевой момент - передача $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); // 30
10
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; // 999
999

Изменение объекта через ссылку внутри замыкания отражается на исходном объекте.

Конфликт имён: переменная с тем же именем, что и в 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, что делает функцию сортировки гибкой.

use в замыканиях PHP - comments

En
Php function use (php)