Каждая функция в своем теле задает так называемую область видимости. Внутри этой области доступны только те переменные, которые были определены там же. Определенные вне функции переменные невидимы внутри нее:
<?php
$age = 5;
function generate()
{
print_r($age);
}
generate();
Этот код выдаст предупреждение PHP Notice: Undefined variable: age. Переменная $age
определена вне контекста функции и невидима внутри. Точно такое же поведение и у анонимных функций:
<?php
$age = 5;
$generate = function () {
print_r($age);
};
$generate(); // PHP Notice: Undefined variable: age
Но передача аргументов все же возможна, и делается она благодаря замыканию. С помощью этого механизма можно захватить переменные из внешнего окружения и использовать их внутри. Правда, это работает только для анонимных функций. Замыканием нередко называют и саму функцию, которая осуществила захват внешних переменных:
<?php
$age = 5;
$generate = function () use ($age) {
print_r($age);
};
$generate(); // 5
Захват переменных осуществляется с помощью ключевого слова use
и списка переменных, который идет после этого слова. Крайне важно осознать, что use
используется при определении функции, а не ее вызове.
Может возникнуть закономерный вопрос: «Зачем это делать, если переменные можно передать в сам вызов?». Попробуем разобраться:
<?php
$age = 5;
$generate = function ($age) {
print_r($age);
};
$generate($age); // 5
Замыкания полезны, когда функция определяется в одном месте, а используется в совершенно другом. Замыкание позволяет не таскать за собой гору переменных, а в некоторых ситуациях без него просто не обойтись.
Вспомните функцию without()
из пакета Funct
. Она принимает на вход массив и значение, а возвращает новый массив, полученный фильтрацией старого по переданному значению. Его реализация построена на функциях высшего порядка и подразумевает фильтрацию. Сложность возникает при описании предиката, ведь внутри анонимной функции нужно сравнивать текущее значение и переданный элемент. Замыкание позволяет решить эту задачу просто:
<?php
function without(array $items, $value)
{
$filtered = array_filter($items, function ($item) use ($value) {
return $item !== $value;
});
// Сбрасываем ключи
return array_values($filtered);
}
without([3, 4, 10, 4, 'true'], 4); // [3, 10, 'true']
Без добавления use ($value)
ничего не получится, потому что $value
не виден внутри анонимной функции.
В замыкание можно передать данные по ссылке, что позволит их менять внутри. Но делать так никогда не стоит. Подобное использование создает невероятно сложный в отладке и сопровождении код. Единственная причина, по которой можно использовать передачу по ссылке - рекурсия внутри анонимной функции. Подробнее об этом в другом курсе.
Стрелочные функции
Главная цель введения стрелочных функций — это создание более лаконичного кода. Поэтому кроме сокращенного синтаксиса, они получили автоматический захват внешних переменных. Другими словами, при использовании стрелочных функций нам не нужен use
:
<?php
$age = 5;
$generate = fn() => print_r($age);
$generate(); // 5
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.