Что такое callable?
Callable — это специальный псевдотип данных в PHP, означающий «нечто, что может быть вызвано как функция». Как будет видно ниже, значения этого псевдотипа могут быть самых разных реальных типов, но всегда есть нечто, что их объединяет — это способность быть использованными в качестве функции. (1)
А можно пример?
Да легко. Самый часто используемый в современном языке вариант callable — это анонимная функция. ( https://3v4l.org/7EB6p )
$x = function ($a) { return $a * 2; }; assert( true === is_callable($x) ); assert( 4 == $x(2) );
Функция is_callable() как раз проверяет — принадлежит ли переданное ей значение псевдотипу callable. Разумеется, анонимная функция принадлежит этому псевдотипу и is_callable() вернёт true.
Анонимные функции можно присваивать переменным и затем вызывать с помощью этих переменных (что и продемонстрировано в примере). Разумеется, анонимная функция может быть передана в качестве аргумента в другую функцию или быть возвращена функцией с помощью оператора return, что вместе с семейством функций вроде array_map или array_reduce открывает для нас дорогу к функциональному программированию (узкую, надо сказать дорожку, все-таки PHP изначально не функциональный язык).
В PHP существует специальный системный класс Closure. Когда вы создаете новую анонимную функцию, по сути вы неявно создаете объект этого класса. Подробнее об этом можно прочитать в мануале (2)
Немного путаницы. Авторы версии 5.3, в которой впервые появился современный синтаксис анонимных функций, перепутали два понятия — собственно анонимная функция (лямбда-функция) и замыкание (замыкание переменной на контекст этой анонимной функции). Именно поэтому анонимные функции реализованы в языке с помощью системного класса Closure, а не, к примеру, Lambda, как стоило бы ожидать. Имейте этот факт в виду на собеседовании — многие интервьюеры сами путают понятия «лямбда-функция» и «замыкание». Впрочем, подробный рассказ о том, что такое «замыкание», выходит за рамки этой статьи.
Строка как callable и небольшая историческая справка
Строки в PHP вполне могут быть callable! В этом случае интерпретатор будет искать обычную, неанонимную функцию с именем, совпадающим с данной строкой и, в случае успеха, вызовет такую функцию.
function foo($bar) { return $bar * 2; } $x = 'foo'; assert( true === is_callable($x) ); assert( 4 == $x(2) );
( см. https://3v4l.org/A3VvYX — true в обоих случаях )
Таким образом можно вызывать как свои функции, так и библиотечные. Есть ряд ограничений — нельзя вызвать isset(), empty() и другие функции, которые фактически являются конструкциями языка.
Стоит заметить, что callable-строка может содержать в себе конструкцию вида ‘ClassName::method’ — это не возбраняется, такие строки тоже будут callable. Обратите внимание на особенность — скобки списка аргументов в таком случае не пишутся!
class Foo { public static function bar() { return 42; } } $x = 'Foo::bar'; assert( true === is_callable($x) ); assert( 42 == call_user_func($x) );
( см. https://3v4l.org/cC9g8 — true в обоих случаях )
Вторая особенность такой callable строки в том, что невозможно вызвать ее напрямую, с помощью $x(), мы получим ошибку вида «Fatal error: Call to undefined function Foo::bar()» И здесь нам на помощь приходит специальная функция call_user_func(), которая умеет обходить «острые углы» и вызывать значения псевдотипа callable, даже если это невозможно с помощью обычного синтаксиса.
Вас могут попытаться подловить вопросом — а как давно в PHP появились анонимные функции? Корректный ответ таков: «Современный синтаксис появился в версии 5.3, а ранее, со времен PHP 4, существовал способ создания анонимных функций с помощью функции create_function() Разумеется, сейчас этот способ имеет лишь исторический интерес. И должен у любого уважающего себя программиста вызывать такие же чувства, как оператор goto и функция eval() — желание никогда это не писать.»
Почему я пишу об этом казусе здесь? Дело в том, что на самом деле create_function() не создавала лямбда-функцию в современном понимании, фактически эта функция создавала именованную функцию с именем наподобие «lambda_1» и возвращала ее имя. А дальше работал уже знакомый нам механизм, когда string является callable
Callable массивы
Массивы в PHP тоже могут быть callable! Есть два основных случая, когда это работает. Проще всего показать их на примере:
class Foo { public static function bar() { return 42; } public function baz() { return 1.46; } } assert( true === is_callable([Foo::class, 'bar']) ); assert( 42 == call_user_func([Foo::class, 'bar']) ); $x = new Foo; assert( true === is_callable([$x, 'baz']) ); assert( 1.46 == call_user_func([$x, 'baz']) );
Итак, массив, в котором нулевой элемент — это имя класса, а первый — имя статического метода, является callable. Ровно также, как и массив, состоящий из объекта и имени его динамического метода.
Стоит отметить интересную особенность: если в классе Foo определен метод __call() или __callStatic(), то is_callable(Foo $foo, ‘bar’) или is_callable(Foo::class, ‘bar’) соответственно всегда будет true. Что, в общем-то, вполне логично.
Callable объекты
Да, в PHP возможно и такое. Объект вполне может быть «функцией», достаточно лишь определить в классе магический метод __invoke():
class Filter { protected $filter = FILTER_DEFAULT; public function setFilter($filter) { $this->filter = $filter; } public function __invoke($val) { return filter_var($val, $this->filter); } } $filter = new Filter(); $filter->setFilter(FILTER_SANITIZE_EMAIL); assert( true === is_callable($filter) ); assert( 'foo@example.com' == $filter('f o o @ example . com') ); $filter->setFilter(FILTER_SANITIZE_URL); assert( 'http://test.com' == $filter('http:// test . com') );
Метод __invoke() будет автоматически вызван при попытке использования объекта, как функции.
Тайп-хинтинг
В современных версиях PHP, начиная с 5.4, появилась возможность указывать псевдотип callable в качестве хинта типа аргумента функции.
function filter($value, callable $filter) { return $filter($value); }
В случае, если переданное значение не будет callable, попытка вызова функции с таким аргументом приведет к фатальной ошибке «Catchable fatal error: Argument 2 passed to filter() must be callable»
Вместо заключения
Callable — одна из самых сложных и запутанных тем при изучении основ PHP. С одной стороны мы имеем современный строгий и чистый синтаксис лямбда-функций и замыканий, прекрасную возможность __invoke(), а с другой стороны — огромное и уже фактически ненужное историческое наследие, которое, видимо, никогда не будет выпилено из языка.
Знать это наследие важно. И не только потому, что в вашей работе с огромной вероятностью придется столкнуться со старым кодом, в котором могут использоваться разные трюки, вроде create_function(). В первую очередь знать такие вещи нужно для собственного самосовершенствования, чтобы понимать все плюсы и минусы разных подходов, конструкций и парадигм, и уметь выбрать нужные в нужный момент времени.
Источники:
Нет Ответов