PHP 8.1: First-class Callable Syntax
PHP 8.1 and later supports a new syntax in creating a callable
from within the current scope. With this new syntax, it is easier to create a callable
using the same syntax a function/method is called, instead of using Closure::fromCallable
.
Closure::fromCallable
returns a callable
(Closure
object) from a PHP callable (function name, method, or anonymous function). The first-class callable syntax aims to reduce the boilerplate code of Closure::fromCallable
.
For example, the following returns a callable
that calls strtoupper
:
$callable = Closure::fromCallable('strtoupper');
echo $callable('foo'); // FOO
Using the first-class callable syntax, the same callable
can be created as follows:
-$callable = Closure::fromCallable('strtoupper');
+$callable = strtoupper(...);
echo $callable('foo'); // FOO
Syntax
A valid callable followed by (...)
makes a first-class callable.
Function names
$callable = strlen(...);
Class Methods
$callable = $item->doSomething(...);
Static Methods
$callable = $item::doSomething(...);
Anonymous Functions
$function = function() {};
$callable = $function(...);
Note that the ...
token is not to be used as a placeholder for a specific parameter. Although this syntax allows partial function application, it is not supported at the moment. Attempting to pass any parameters to the callable results in a syntax error:
$callable = str_contains($text, ...);
Parse error: syntax error, unexpected token ")" in ... on line ...
PHP also uses the ...
token (internally called T_ELLIPSIS
) for variadic function declarations and at call-sites as the spread operator. There are no changes in variadic function declarations and spread operator behavior.
Restrictions
Object Instantiating is not allowed
Instantiating a new object using the new
construct is not supported with first-class callable syntax. This behavior is similar to Closure::fromCallable
.
$callable = new SplFixedArray(...);
Fatal error: Cannot create Closure for new expression in ... on line ...
Null-safe methods are not allowed
First-class callable syntax does not allow null-safe methods because it cannot be guaranteed to be a callable
.
$test?->doSomething(...);
Fatal error: Cannot combine nullsafe operator with Closure creation in ... on line ...
Attributes Arguments
Attributes cannot be declared using first-class callable syntax.
#[Attribute(...)]
class Test {}
Fatal error: Cannot create Closure as attribute argument in ... on line ...
Callable Scope
When a first-class callable is created, it also inherits the scope of the call-site that created the callable.
function shout(): void {
$value = 'Banana';
echo $value;
}
$value = 'Apple';
$callable = shout(...);
$callable(); // Banana
Because first-class callables are scoped, it is possible to return a callable
that involves calling private
methods, as long as it is returned from the object scope.
class Clock {
public function getClockCallable(): callable {
return $this->getTime(...);
}
private function getTime(): int {
return time();
}
}
$clock = new Clock();
$clock_callback = $clock->getClockCallable();
echo $clock_callback();
Notice the Clock::getClockCallable
method returns a callable
, that calls the private
method getTime
. The existing syntax of creating a callable
using an array does allow calling private
methods:
class Clock {
public function getClockCallable(): callable {
- return $this->getTime(...);
+ return [$this, 'getTime'];
}
private function getTime(): int {
return time();
}
}
$clock = new Clock();
$clock_callback = $clock->getClockCallable();
echo $clock_callback();
Fatal error: Uncaught Error: Call to private method Clock::getTime() from global scope in ...:...
Backwards Compatibility Impact
First-class callable syntax is a new syntax introduced in PHP 8.1, and it is not possible to back-port it to older PHP versions. Attempting to do results in a parse error:
Parse error: syntax error, unexpected token ")" in ... on line ...
Applications that require compatibility with older versions can continue to use Closure::fromCallable
to create a scoped callable
.