Функции обратного вызова (callback) и замыкания

10 июля 2018
Для реализации событийной модели и создания обработчиков (callback-функций) удобно использовать анонимные функции (также известные как замыкания). При передаче в функцию аргумента-замыкания этот параметр автоматически преобразуются техникой языка в объект класса Closure, который принадлежит к псевдотипу данных callable. Данный псевдотип обозначает значение, которое может быть вызвано как функция. К таким значениям относится строка с названием пользовательской функции, массив с парой значений (имя класса и название метода), анонимная функция, объект с реализованным методом __invoke. Проверка принадлежности аргумента к псевдотипу callable выполняется с помощью функции is_callable

Документация по анонимным функциям https://www.php.net/manual/ru/functions.anonymous.php
Документация по is_callable https://www.php.net/manual/ru/function.is-callable.php

Пример кода с функцией добавления пользователя. Если добавление проходит успешно, то выполняется функция обратного вызова (в этом частном примере указана анонимная функция):
// Добавляет пользователя в систему
// @param array $fields - массив с информацией о пользователе
// @params callable $event - событие, срабатывающий в случае успешного добавления пользователя
// @return bool - true, если пользователь успешно добвлен и false в противном случае
function add_user(array $fields, callable $event=NULL)
{
	// добавление пользователя: начало
	// ...
	// добавление пользователя: конец

	//Если установлена функция обратного вызова, то вызываем её
	if(is_callable($event)) $event();

	return true;
}

add_user(array(
	'NAME' => 'Владимир',
	'GENDER' => 'm',
	'COUNTRY' => 'Россия',
), function(){
	echo 'Вы успешно зарегистрированы';
});

Конструкция use

Иногда нужно использовать переменные, объявленные за пределами определения функции, в родительской области видимости.
Например, следующий код ничего не выведет, т.к переменные $m_hello и $m_user не доступны внутри области видимости функции
$m_hello = 'Здравствуйте, ';
$m_user = 'пользователь';

$a = function()
{
	echo $m_hello.$m_user;
};

/*
Функция ничего не выведет, т.к переменные $m_hello и $m_user
в теле функции не заданы, а за пределами тела их не видно
*/
$a();

Что бы переменные из родительской области видимости были видны внутри замыкания, нужно использовать конструкцию use. Но надо учитывать, что значения переменных будут взяты по состоянию на момент определения функции, а не её вызова. Это значит, что если значение переменной изменится после определения функции, то внутри функции будет использовано значение переменной, которые было на момент определения замыкания
$m_hello = 'Здравствуйте, ';
$m_user = 'пользователь';

$a = function() use ($m_hello, $m_user)
{
	echo $m_hello.$m_user;
};

//Изменяем одну переменную
$m_hello = 'Hello, ';

$a(); /* Выведет 'Здравствуйте, пользователь' */