phpday 2022

First class callable syntax

The first class callable syntax is introduced as of PHP 8.1.0, as a way of creating anonymous functions from callable. It supersedes existing callable syntax using strings and arrays. The advantage of this syntax is that it is accessible to static analysis, and uses the scope at the point where the callable is acquired.

CallableExpr(...) syntax is used to create a Closure object from callable. CallableExpr accepts any expression that can be directly called in the PHP grammar:

Example #1 Simple first class callable syntax

<?php

class Foo {
   public function 
method() {}
   public static function 
staticmethod() {}
   public function 
__invoke() {}
}

$obj = new Foo();
$classStr 'Foo';
$methodStr 'method';
$staticmethodStr 'staticmethod';


$f1 strlen(...);
$f2 $obj(...);  // invokable object
$f3 $obj->method(...);
$f4 $obj->$methodStr(...);
$f5 Foo::staticmethod(...);
$f6 $classStr::$staticmethodStr(...);

// traditional callable using string, array
$f7 'strlen'(...);
$f8 = [$obj'method'](...);
$f9 = [Foo::class, 'staticmethod'](...);
?>

Note:

The ... is part of the syntax, and not an omission.

CallableExpr(...) has the same semantics as Closure::fromCallable(). That is, unlike callable using strings and arrays, CallableExpr(...) respects the scope at the point where it is created:

Example #2 Scope comparison of CallableExpr(...) and traditional callable

<?php

class Foo {
    public function 
getPrivateMethod() {
        return [
$this'privateMethod'];
    }

    private function 
privateMethod() {
        echo 
__METHOD__"\n";
    }
}

$foo = new Foo;
$privateMethod $foo->getPrivateMethod();
$privateMethod();
// Fatal error: Call to private method Foo::privateMethod() from global scope
// This is because call is performed outside from Foo and visibility will be checked from this point.

class Foo1 {
    public function 
getPrivateMethod() {
        
// Uses the scope where the callable is acquired.
        
return $this->privateMethod(...); // identical to Closure::fromCallable([$this, 'privateMethod']);
    
}

    private function 
privateMethod() {
        echo 
__METHOD__"\n";
    }
}

$foo1 = new Foo1;
$privateMethod $foo1->getPrivateMethod();
$privateMethod();  // Foo1::privateMethod
?>

Note:

Object creation by this syntax (e.g new Foo(...)) is not supported, because new Foo() syntax is not considered a call.

Note:

The first-class callable syntax cannot be combined with the nullsafe operator. Both of the following result in a compile-time error:

<?php
$obj
?->method(...);
$obj?->prop->method(...);
?>

add a note add a note

User Contributed Notes

There are no user contributed notes for this page.
To Top