Avatar ·

Что такое замыкание (closure) в PHP и зачем нужен оператор use?

Всем привет! Я начал изучать PHP и наткнулся на тему анонимных функций и замыканий. Вроде бы с анонимными функциями разобрался, но когда дошел до замыканий, то споткнулся об оператор `use`. Вот код, который я видел в документации: ```php $message = "Hello, world!"; $greeting = function() use ($message) { echo $message; }; $greeting(); ``` Я понимаю, что это работает, но не понимаю, зачем нужен этот `use`. Почему нельзя просто обратиться к переменной `$message` внутри функции, как в обычных функциях? Например: ```php function test() { echo $message; // Почему так не работает? } ``` Также я читал, что замыкания могут "захватывать" переменные из внешней области видимости. Что это значит? И зачем вообще использовать замыкания, если можно просто передать переменную как аргумент? Объясните, пожалуйста, на пальцах, что такое замыкание в PHP и в каких случаях оно действительно нужно. Примеры кода приветствуются. Заранее спасибо! <<>>

Что такое замыкание и зачем нужен use?

Начнём с главного: замыкание (closure) — это анонимная функция, которая может использовать переменные из внешней области видимости. Оператор use — это механизм, который явно указывает, какие именно внешние переменные "захватываются" замыканием.

В PHP анонимные функции имеют свою собственную область видимости. Они не видят переменные, объявленные вне их тела, если эти переменные не переданы через use. Это сделано для безопасности и контроля: вы сами решаете, к каким внешним данным у функции есть доступ.

Почему нельзя просто использовать переменную?

В обычных функциях PHP тоже не работает обращение к глобальным переменным без ключевого слова global или суперглобальных массивов. Это особенность языка: функции изолированы от внешнего контекста. Анонимные функции — не исключение, но use даёт более гибкий и безопасный способ "импортировать" переменные.

// Пример: как НЕ работает
$name = "Иван";
$func = function() {
    echo $name; // Notice: Undefined variable: name
};
$func();

// А так — работает
$func = function() use ($name) {
    echo $name; // Иван
};
$func();

Как работает захват переменных?

Когда вы пишете use ($variable), PHP копирует значение переменной в момент создания замыкания. Это захват по значению. Если вы измените исходную переменную после создания замыкания, внутри замыкания останется старое значение. Чтобы захватить по ссылке (чтобы изменения отражались), используйте &:

$count = 0;
$increment = function() use (&$count) {
    $count++;
};

$increment();
echo $count; // 1 — работает, потому что передали по ссылке

Реальные примеры использования замыканий

  • Callback-функции: передача поведения в функции вроде array_map или array_filter.
  • Фабрики функций: создание функций с предустановленными параметрами.
  • Инкапсуляция состояния: "запоминание" значения между вызовами.

Пример: создание множителя

function createMultiplier($factor) {
    return function($number) use ($factor) {
        return $number * $factor;
    };
}

$double = createMultiplier(2);
$triple = createMultiplier(3);

echo $double(5); // 10
echo $triple(5); // 15

Здесь $factor "запоминается" внутри замыкания, хотя внешняя функция createMultiplier уже завершила работу. Это и есть суть замыкания: функция + окружение, в котором она была создана.

Пример: обработчик событий

$items = ['apple', 'banana', 'cherry'];
$searchTerm = 'an';

$filtered = array_filter($items, function($item) use ($searchTerm) {
    return strpos($item, $searchTerm) !== false;
});

print_r($filtered); // ['banana']

Итог

  • Замыкание = анонимная функция + захваченные переменные.
  • use — явный способ указать, какие переменные захватывать.
  • Захват может быть по значению (копия) или по ссылке (&).
  • Замыкания удобны для callback'ов, фабрик функций и инкапсуляции состояния.

Не бойтесь использовать use — это не магия, а просто способ сделать код понятным и безопасным. Со временем вы привыкнете и оцените эту возможность.

<<>>

Альтернативный взгляд: замыкания как объекты

В PHP замыкания — это не просто функции, а объекты класса Closure. Это значит, что у них есть методы, например bindTo() и call(), которые позволяют менять контекст выполнения.

Оператор use можно рассматривать как конструктор, который "вшивает" внешние данные в объект-функцию. Это похоже на то, как вы передаёте аргументы в конструктор класса, чтобы сохранить их в свойствах.

// Пример с bindTo
$greeting = function() {
    return "Привет, {$this->name}!";
};

$user = new stdClass();
$user->name = "Мария";

$boundGreeting = $greeting->bindTo($user);
echo $boundGreeting(); // Привет, Мария!

Но use проще и удобнее, когда нужно просто захватить переменную, а не менять контекст объекта. Выбирайте подход по ситуации:

  • use — для простого захвата переменных из текущей области видимости.
  • bindTo/call — когда нужно выполнить функцию в контексте другого объекта.

Также стоит помнить, что с PHP 7.4 появились стрелочные функции (fn), которые автоматически захватывают переменные по значению и не требуют use:

$factor = 2;
$multiply = fn($x) => $x * $factor; // $factor захвачен автоматически
echo $multiply(5); // 10

Но стрелочные функции подходят только для однострочных выражений. Для сложной логики по-прежнему используйте обычные замыкания с use.