The new Closure::fromCallable() in PHP 7.1

With PHP 5.5 going EOL earlier this week and the PHP 7.1 beta expected later this month, now sounds like a good time to look into a neat little feature coming in 7.1: easily converting any callable into a proper Closure using the new Closure::fromCallable() method.

If you're already familiar with closures in PHP, you can skip right ahead.

PHP has had closures since version 5.3. Think of it as similar to an anonymous function in Javascript; they're usually passed off to another method, which either calls it directly or stores it for later use.

As an example, let's look at Laravel's collection class. It has a reject method, which you can use to filter out values you don't want in the collection:

$ages = collect([ 3 , 7.5 , 12 , 19.5 , 23 ]); $adults = $ages->reject( function ($age) { return $age < 18 ; });

Behind the scenes, the reject method will call our closure callback for every item in the collection. If the age is less than 18 , it will be rejected from our collection.

Now imagine you want to reject every value in the collection that is not a whole number. Basically, reject all float s. There's a built-in is_float function, so why don't we pass that in directly?

$ages = collect([ 3 , 7.5 , 12 , 19.5 , 23 ]); $integers = $ages->reject( 'is_float' );

Unexpectedly, this will not work. Why? In addition to a closure, Laravel also allows you to pass in a single value to reject , which will remove that value from the collection. Since we've passed it a string, Laravel thinks we want to remove the 'is_float' item from the collection. Whoops!

The way around this is to actually pass in a closure and call is_float from within our closure callback:

$ages = collect([ 3 , 7.5 , 12 , 19.5 , 23 ]); $integers = $ages->reject( function ($age) { return is_float($age); });

In PHP 7.1, the Closure class has a new static fromCallable method, which automatically creates a Closure from any callbable you pass it. Using this new method, we can clean up our code a bit:

$ages = collect([ 3 , 7.5 , 12 , 19.5 , 23 ]); $integers = $ages->reject(Closure::fromCallable( 'is_float' ));

Pretty neat!

Another scenario where you'd want a closure is if you want to pass in an array-callable of a private method. As mentioned in the PHP docs, you can create a callable to a method on an object by using an array with this format: [$object, 'method'] . This can be quite convenient when using a method on your class that has the logic for the callback:

$due = $orders->filter([ $this , 'isDue' ]);

This works well, with the only caveat being that the isDue method now has to be made public - otherwise the collection class won't be able to access it.

This is where Closure::fromCallable() once again comes to the rescue. When used from within a class, the resulting closure will automatically be bound to that class, allowing it to access private methods:

$due = $orders->filter(Closure::fromCallable([ $this , 'isDue' ]));

The isDue method can now stay private or protected , but the collection will still be able to access it through the wrapping closure!

None of this is really earth shattering, but it's a neat little addition that can come in handy every so often.

Questions? Comments? Complaints? Ping me on Twitter.