📜 ⬆️ ⬇️

Getting ready for a PHP interview: pseudo-type "callable"

It is no secret that they like to ask tricky questions at interviews. Not always adequate, not always relevant to reality, but the fact remains that they are asking. Of course, a question is a question, and sometimes a question, seemingly stupid at first glance, is actually aimed at checking how well you know the language you write.
image
The second part of the series of articles is devoted to one of the most complex and voluminous questions about modern PHP - what is “callable”? I tried to reduce in one text a certain minimum of knowledge about this issue.


What is callable?


Callable is a special data pseudo-type in PHP, meaning "something that can be called as a function." As will be seen below, the values ​​of this pseudotype can be of various real types, but there is always something that unites them — the ability to be used as a function.

Can you give an example?


Yes Easy. The most commonly used callable variant in modern language is an anonymous function.
$x = function ($a) { return $a * 2; }; assert( true === is_callable($x) ); assert( 4 == $x(2) ); 

')
The is_callable () function just checks if the value passed to it belongs to the callable pseudotype. Of course, the anonymous function belongs to this pseudotype and is_callable () returns true.

Anonymous functions can be assigned to variables and then called using these variables (as demonstrated in the example). Of course, an anonymous function can be passed as an argument to another function or returned by a function using the return operator, which, together with a family of functions like array_map or array_reduce, opens for us the way to functional programming (narrow, I must say a track, still PHP non-functional language).

In PHP, there is a special system class Closure. When you create a new anonymous function, in essence, you implicitly create an object of this class. Read more about this in the manual: php.net/manual/ru/class.closure.php

A bit of confusion. The authors of version 5.3, in which the modern syntax of anonymous functions first appeared, confused two concepts - the anonymous function itself (lambda function) and closure (closure of a variable on the context of this anonymous function). That is why anonymous functions are implemented in the language using the system class Closure, and not, for example, Lambda, as one would expect. Keep this fact in mind at the interview - many interviewers themselves confuse the concepts of "lambda function" and "closure". However, a detailed account of what “closure” is is beyond the scope of this article.


String as callable and a little historical reference


PHP strings may well be callable! In this case, the interpreter will search for a normal, non-anonymous function with the name that matches the given string and, if successful, will call such a function.
 function foo($bar) { return $bar * 2; } $x = 'foo'; assert( true === is_callable($x) ); assert( 4 == $x(2) ); 


Thus, you can call both your own functions and library functions. There are a number of restrictions - you cannot call isset (), empty () and other functions that are actually language constructs.

It is worth noting that a callable string can contain a construction of the form 'ClassName :: method' - this is not forbidden, such strings will also be callable. Pay attention to the feature - in this case the brackets of the argument list are not written!
 class Foo { public static function bar() { return 42; } } $x = 'Foo::bar'; assert( true === is_callable($x) ); assert( 42 == call_user_func($x) ); 

The second feature of such a callable line is that it is impossible to call it directly using $ x (), we get an error like "Fatal error: Call to undefined function Foo :: bar ()" And here the special function call_user_func ( ), which is able to bypass the "sharp corners" and cause the values ​​of the callable pseudotype, even if this is impossible using the usual syntax.

You can try to catch the question - how long have anonymous functions appeared in PHP? The correct answer is: “Modern syntax appeared in version 5.3, and earlier, since PHP 4, there was a way to create anonymous functions using the create_function () function. Of course, now this method has only historical interest. And should any self-respecting programmer have the same feelings as the goto statement and the eval () function — the desire to never write this. ”

Why am I writing about this incident here? The fact is that in fact, create_function () did not create a lambda function in the modern sense; in fact, this function created a named function with a name like “lambda_1” and returned its name. And then the already familiar mechanism worked, when string is callable


Callable arrays


Arrays in PHP can also be callable! There are two main cases when it works. The easiest way to show them is with an example:
 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']) ); 

So, an array in which the zero element is the name of the class, and the first is the name of the static method, is callable. Exactly as well as an array consisting of an object and the name of its dynamic method.

It is worth noting an interesting feature (readers noticed in the comments). If the __call () or __callStatic () method is defined in the Foo class, then is_callable (Foo $ foo, 'bar') or is_callable (Foo :: class, 'bar') will always be true, respectively. That, in general, is quite logical.


Callable objects


Yes, in PHP it is possible and such. The object may well be a “function”; it is enough to define the __invoke () magic method in the class:
 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('foo @ example . com') ); $filter->setFilter(FILTER_SANITIZE_URL); assert( 'http://test.com' == $filter('http:// test . com') ); 

The __invoke () method will be automatically called when you try to use an object as a function.

Type hinting


In modern versions of PHP, starting from 5.4, it became possible to specify the callable pseudotype as the hint of the function argument type.
 function filter($value, callable $filter) { return $filter($value); } 

In case the transmitted value is not callable, an attempt to call a function with this argument will result in a fatal error “Catchable fatal error: Argument 2 passed to filter () must be callable”

Instead of conclusion


Callable is one of the most complex and confusing topics in learning the basics of PHP. On the one hand, we have a modern, rigorous and pure syntax of lambda functions and closures, an excellent possibility of __invoke (), and on the other hand, a huge and already virtually unnecessary historical legacy, which, apparently, will never be cut out of the language.

Knowing this legacy is important. And not only because in your work it is very likely that you will encounter old code that can use various tricks, like create_function (). First of all, you need to know such things for your own self-improvement, to understand all the pros and cons of different approaches, structures and paradigms, and to be able to choose the right one at the right time.

And, of course, in order not to fall face in the dirt at the interview :)

To be continued!

Literature

php.net/manual/ru/language.types.callable.php
php.net/manual/ru/function.call-user-func.php
php.net/manual/ru/functions.anonymous.php
php.net/manual/ru/class.closure.php
php.net/manual/ru/language.oop5.magic.php#object.invoke
habrahabr.ru/post/147620
habrahabr.ru/post/167181
fabien.potencier.org/on-php-5-3-lambda-functions-and-closures.html

Source: https://habr.com/ru/post/259991/


All Articles