Вызываемые выражения

Вызываемое выражение — ссылка на функцию или метод, которая передаётся в другую функцию как аргумент. Параметрам, которые принимают вызываемые выражения, объявляют тип callable.

<?php

function foo(callable $callback) {
$callback();
}

Отдельные функции наподобие array_map(), usort() или preg_replace_callback() принимают callback-функции как аргументы.

Объявление вызываемых выражений

Тип callable представляет значения или выражения, доступные для вызова как функция. Вызываемые выражения вызывают как функции или передают как аргументы в функции или методы, которые ожидают в параметре callback-функцию. Свойствам классов нельзя объявлять тип callable; вызываемым свойствам объявляют тип Closure.

Вызываемыми значениями становятся:

  • Объекты класса Closure
  • Строки (string) с названием функции или квалифицированным названием метода
  • Массивы (array) с названием класса или объектом (object) в элементе с индексом 0 и названием метода в элементе с индексом 1
  • Объекты (object), в классе которых объявили магический метод __invoke()

Объекты Closure создают синтаксисом анонимных функций, стрелочных функций, синтаксисом первоклассных вызываемых значений или методом Closure::fromCallable().

Замечание: Синтаксис первоклассных callable-значений доступен только с PHP 8.1.0.

Пример #1 Пример объявления функций обратного вызова, которые становятся объектами класса Closure

<?php

// Синтаксис анонимных функций
$double1 = function ($a) {
return
$a * 2;
};

// Синтаксис первоклассных вызываемых значений
function double_function($a) {
return
$a * 2;
}
$double2 = double_function(...);

// Синтаксис стрелочных функций
$double3 = fn($a) => $a * 2;

// Метод Closure::fromCallable
$double4 = Closure::fromCallable('double_function');

// Вызов замыкания как callback-функции
// удваивает значение каждого элемента в диапазоне
$new_numbers = array_map($double1, range(1, 5));
print
implode(' ', $new_numbers) . PHP_EOL;

$new_numbers = array_map($double2, range(1, 5));
print
implode(' ', $new_numbers) . PHP_EOL;

$new_numbers = array_map($double3, range(1, 5));
print
implode(' ', $new_numbers) . PHP_EOL;

$new_numbers = array_map($double4, range(1, 5));
print
implode(' ', $new_numbers);

Результат выполнения приведённого примера в PHP 8.1:

2 4 6 8 10
2 4 6 8 10
2 4 6 8 10
2 4 6 8 10

Вызываемые выражения объявляют как строку с названием функции или статического метода. PHP поддерживает передачу встроенных и пользовательских функций, но не языковых конструкций: array(), echo, empty(), eval(), isset(), list(), print или unset().

Для ссылки на статический метод класса не потребуется создавать объект (object) этого класса, достаточно создать массив с названием класса в элементе с индексом 0 и названием метода в элементе с индексом 1 или сослаться на метод через синтаксис с оператором ::, который разрешает область действия метода: 'ClassName::methodName'.

На методы объектов (object) в вызываемых выражениях ссылаются через массив: объект (object) указывают в элементе с индексом 0, а название метода — в элементе с индексом 1.

Видимость метода в объектах Closure проверяется относительно места объявления, а не вызова замыкания Closure, тогда как видимость методов в выражениях с псевдотипом callable проверяется относительно места вызова выражения, поэтому попытка вызова callable-выражения, которое ссылается на недоступный в точке вызова метод, выдаст ошибку. На методы классов лучше ссылаться через замыкания Closure, поскольку видимость метода в точке вызова замыкания не влияет на доступность вызова метода.

Замечание: Объекты Closure связываются с областью видимости объявления, тогда как вызываемые выражения со ссылкой на метод класса через строку или массив разрешаются в области видимости вызова. Вызываемое значение со ссылкой на закрытый или защищённый метод класса, который потребуется вызывать извне области видимости класса, создают методом Closure::fromCallable() или синтаксисом первоклассных вызываемых значений.

PHP поддерживает вызываемые выражения, которые возможно передать как аргумент, но невозможно вызывать. К таким выражениям относятся вызываемые значения, которые зависят от контекста и ссылаются на метод класса в иерархии наследования через ключевые слова: 'parent::method' или ["static", "method"].

Замечание: С PHP 8.2.0 контекстно-зависимые вызываемые выражения устарели. Независимые от контекста ссылки на методы получают путём замены выражений наподобие 'parent::method' выражениями parent::class . '::method' или синтаксисом первоклассных вызываемых значений.

Пример #2 Примеры вызова callable-выражений функцией call_user_function()

<?php

// Пример callback-функции
function my_callback_function()
{
echo
"Привет, мир!", PHP_EOL;
}

// Пример callback-метода
class MyClass
{
static function
myCallbackMethod()
{
echo
"Привет, мир!", PHP_EOL;
}
}

// Тип 1: Простой вызов callback-функции
call_user_func('my_callback_function');

// Тип 2: Вызов статического метода класса
call_user_func(['MyClass', 'myCallbackMethod']);

// Тип 3: Вызов метода объекта класса
$obj = new MyClass();
call_user_func([$obj, 'myCallbackMethod']);

// Тип 4: Вызов статического метода класса
call_user_func('MyClass::myCallbackMethod');

// Тип 5: Вызов статического метода класса с разрешением названия класса через языковую конструкцию ::class
call_user_func([MyClass::class, 'myCallbackMethod']);

// Тип 6: Вызов статического метода класса по контекстно-зависимой относительной ссылке
class A
{
public static function
who()
{
echo
'A', PHP_EOL;
}
}

class
B extends A
{
public static function
who()
{
echo
'B', PHP_EOL;
}
}

call_user_func(['B', 'parent::who']); // Выводит: A.
// Начиная с PHP 8.2.0 callable-выражения
// с относительными названиями методов устарели

// Тип 7: Объекты, классы которых реализуют магический метод __invoke(),
// доступны для вызова как функции
class C
{
public function
__invoke($name)
{
echo
'Привет, ', $name;
}
}

$c = new C();
call_user_func($c, 'PHP!');

Результат выполнения приведённого примера:

Привет, мир!
Привет, мир!
Привет, мир!
Привет, мир!
Привет, мир!

Deprecated: Callables of the form ["B", "parent::who"] are deprecated in script on line 51
A
Привет, PHP!

Замечание:

Callback-функции, которые зарегистрировали функцией call_user_func() или call_user_func_array(), не вызовутся при непойманном исключении, которое выбросила предыдущая callback-функция.