Справочники, инструменты, документация

PHP: Замыкания или анонимные функции

Замыкания или анонимные функции в PHP — это обычные функции, но без имени. Давайте рассмотрим пример такой функции:
function(){
    echo "Hello, World!";
};

В этом примере есть анонимная функция, но нет никакого смысла. Возникает вопрос — как использовать такие функции? Следующий пример поможет разобраться в этом:

$closure = function(){
    echo "Hello, World!";
};

$closure();

В этом примере мы присвоили переменной анонимную функцию, потом запустили выполнение этой функции синтаксисом $closure();, то есть мы к имени переменной добавили круглые скобки, как при вызове функции. Обратите внимание, при этом знак $ в имени переменной мы не удаляли.

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

Как на практике используются замыкания

Обычно анонимные функции или замыкания в PHP используются чтобы передать их в качестве параметров другой функции. В PHP есть несколько встроенных функций, которые в качестве аргумента принимают замыкание, но об этом будет написано ниже.

Давайте ещё усложним наш пример.

function doStuff($closure){
    $closure();
}

$closure = function(){
    echo "Hello, World!";
};

doStuff($closure);

Тут мы создали обычную функцию doStuff() и передали ей как параметр нашу анонимную функцию из переменной $closure, а потом запустили её в теле функции doStuff().

В этом примере в функции doStuff() не хватает проверки переменной $closure на тип. Для того чтобы функция doStuff() работала корректно, в параметре обязательно должна содержаться анонимная функция.

Функция is_callable()

Анонимные функции в PHP реализованы с помощью встроенного класса Closure (PHP 5 >= 5.3.0, PHP 7). То есть каждая анонимная функция является объектом этого класса.

Объекты класса Closure также называют псевдотипом Callbacks / Callables. Принадлежность переменной к этому типу данных можно проверить при помощи функции is_callable().

function doStuff($closure){
    if (is_callable($closure)) {
        echo gettype($closure)."<br>";
        print_r($closure);
    }
}

$closure = function(){
    echo "Hello, World!";
};

doStuff($closure);

Обратите внимание, функция gettype определяет тип переменной $closure как объект. Поэтому функция is_object также вернёт значение true, но это не адекватная проверка в нашем случае. Замыкание нужно проверять именно функцией is_callable.

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

В анонимной функции можно сделать видимой переменную из родительской области видимости используя конструкцию use. Вот пример:

function doStuff($closure){
    if (is_callable($closure)) {
        $closure();
    }
}

$name = "Max";
$closure = function() use ($name) {
    echo "Hello, $name!";
};

doStuff($closure);

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

function doStuff($closure){
    if (is_callable($closure)) {
        $closure();
    }
}

$name = "Max";
$id = 12;

$closure = function() use ($name, $id) {
    echo "Hello, $name ($id)!";
};

doStuff($closure);

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

Аргументы в анонимных функциях

В анонимную функцию можно передать аргументы. Давайте для примера передадим один аргумент в нашу функцию.

function doStuff($closure){
    if (is_callable($closure)) {
        $closure('Аргумент');
    }
}

$name = "Max";
$id = 12;

$closure = function($argument) use ($name, $id) {
    echo "Hello, $name ($id)!" . "<br>" . $argument;
};

doStuff($closure);

С аргументами всё очень просто, тут анонимные функции ничем не отличаются от обычных.

Функция preg_replace_callback

Несколько встроенных в PHP функций, которые принимают в качестве аргумента замыкание, вот одна из них: preg_replace_callback — выполняет поиск по регулярному выражению и замену с использованием callback-функции (замыкания).

preg_replace_callback ( $pattern , $callback, $subject )

Где:

  • $pattern — искомый шаблон, может быть как строкой, так и массивом строк.
  • $callback — вызываемая callback-функция, которой будет передан массив совпавших элементов из строки subject. Callback-функция должна вернуть строку с заменой.
  • $subject — строка или массив строк для поиска и замены.

Это краткий синтаксис, подробнее про возможности этой функции можно почитать на сайте мануала по PHP.

echo preg_replace_callback('~-([a-z])~', function ($match) {
    return strtoupper($match[1]);
}, 'hello-world');
// выведет helloWorld

Ещё функции принимающие аргументом замыкание: array_filter, array_map, array_reduce, usort.

Функция call_user_func

Функция call_user_func — вызывает пользовательскую функцию, указанную в первом параметре. Возвращает результат функции, или FALSE в случае ошибки.

Примеры использования call_user_func:

// простой пример замыкания (callback)
function my_callback_function() {
    echo 'hello world!';
}
call_user_func('my_callback_function');

Пример использования call_user_func в ООП:

// примеры callback-метода
class MyClass {
    function myCallbackMethod() {
        echo 'Hello World!';
    }
}

// вызов метода статического класса без создания объекта
call_user_func(array('MyClass', 'myCallbackMethod'));

// вызов метода объекта
$obj = new MyClass();
call_user_func(array(&$obj, 'myCallbackMethod'));

Класс Closure

Анонимные функции в PHP реализованы с помощью класса Closure. Все анонимные функции являются объектами этого встроенного класса. При вызове объекта как функции, вызывается магический метод __invoke (начиная с PHP5.3).

function doStuff(){
    $variable = 55;

    return function($variable_out) use($variable) {
        echo "<br>Sum: " . ($variable + $variable_out);
    };
}
$r = doStuff();

$r(35);

doStuff()->__invoke(5);