Oh Dear is the all-in-one monitoring tool for your entire website. We monitor uptime, SSL certificates, broken links, scheduled tasks and more. You'll get a notifications for us when something's wrong. All that paired with a developer friendly API and kick-ass documentation. O, and you'll also be able to create a public status page under a minute. Start monitoring using our free trial now.

Discovering PHP's first-class callable syntax

Original – by Freek Van der Herten – 3 minute read

When looking at recent changes in the Laravel framework, I saw some PHP syntax that I didn't see before. Because I've been working with PHP for over 20 years and have a firm grasp of the language, I was surprised to see new syntax for the first time.

Look at these lines in the bin/facades.php in laravel/framework.

 $resolvedMethods = $proxies->map(fn ($fqcn) => new ReflectionClass($fqcn))
        ->flatMap(fn ($class) => [$class, ...resolveDocMixins($class)])
        ->flatMap(resolveMethods(...))
        ->reject(isMagic(...))
        ->reject(isInternal(...))
        ->reject(isDeprecated(...))
        ->reject(fulfillsBuiltinInterface(...))
        ->reject(fn ($method) => conflictsWithFacade($facade, $method))
        ->unique(resolveName(...))
        ->map(normaliseDetails(...));

The syntax I had never seen before was those three dots inside a function call.

->flatMap(resolveMethods(...))

You might have seen the ... operator (aka the spread or splat operator) in various other contexts.

You can use it to unpack arrays...

$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
// ['banana', 'orange', 'apple', 'pear', 'watermelon'];

... or to grab arguments for a function:

function myFunction(...$arguments) {
	var_dump($arguments); // shows an array ['a', 'b', 'c']
}
	
myFunction('a', 'b', 'c');

A neat trick you can also do is to pass all parameters of a function to another function.

function myFunction(...$arguments) {
	// $arguments now holds an array of all passed arguments
	
	// all elements in the array will be passed as 
	//separate arguments to `anotherFunction`.
	anotherFunction(...$arguments);
}

First-class callable syntax

In this case...

->flatMap(resolveMethods(...))

the ... operator does something completely else. In this context ...` is called "the first class callable syntax". It's been available since PHP 8.1. It will wrap the function it is used in in a closure.

So this code...

$myFunction = strtoupper(...);

... is equivalent to:

$myFunction = function(...$arguments) {
	return strtoupper(...$argument);
}

So any arguments you pass to it, will be passed to the function you're wrapping in a closure.

Let's use it.

$myFunction('a') // returns 'A';

Let's unpack it using a simple example. Imagine you have this collection you want to uppercase.

collect(['a', 'b', 'c'])
   ->map(function($letter) {
      return strtoupper($letter);
   });

Using the first-class callable syntax, you can rewrite that code like this.

collect(['a', 'b', 'c'])
   ->map(strtoupper(...));

Cool, right?

Of course, you can also use it for non-global functions, such as class functions as well.

class MyClass()
{
    public function execute(): Collection
    {
        return collect(['a', 'b', 'c'])
            ->map($this->doubleString(...));
    }
    
    public function doubleString(string $string): string
    {
        return $string . $string;
    }
}

 // returns a collection with 'aa', 'bb, and 'cc'.
(new MyClass)->execute();

Very neat!

If you want to know more about this syntax, check out this excellent post at PHP Watch, which also lists the limitations and edge cases.

Stay up to date with all things Laravel, PHP, and JavaScript.

You can follow me on these platforms:

On all these platforms, regularly share programming tips, and what I myself have learned in ongoing projects.

Every month I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.

Comments

What are your thoughts on "Discovering PHP's first-class callable syntax"?

Comments powered by Laravel Comments
Want to join the conversation? Log in or create an account to post a comment.