rfc:fcc_in_const_expr

PHP RFC: First Class Callables in constant expressions

Introduction

The Support Closures in constant expressions RFC added support for Closures in constant expressions. The RFC left supporting “First Class Callables” (“FCC”), which can be considered syntax sugar for creating a Closure that just forwards any calls to the callable in question.

Given that FCCs effectively act as syntax sugar, the same use cases as with Closures themselves also apply to this RFC. It is intended to round off the “Closures in constant expressions” RFC.

Proposal

This RFC proposes that it shall be legal to use the FCC syntax in constant expressions. The semantics match the existing semantics of FCCs and of constant expressions. Aside from the obvious, this means:

  • If a referenced class does not yet exist, the autoloader is triggered, as with new-expressions in constant expressions.
  • If a relative reference to a free-standing function is used in a namespaced context, the function will first be looked up within the namespace, falling back to the global namespace if it does not exist.

Constraints

All the constraints imposed on FCCs outside of constant expressions naturally also apply to FCCs within constant expressions. Defining a FCC within a constant expression comes with the following additional constraints:

  • Only references to free-standing functions and to static methods (::) are supported.
  • Only the standard function_name(...) or ClassName::methodName(...) references are supported. The function name must not be an expression (constant or otherwise). [ClassName::class, 'methodName'](...), (Constant)(...), or similar are not supported.
  • Referencing methods that implicitly exist due to the existence of a __callStatic() method on the referenced class are not supported.

Scoping

As with other constant-expressions, FCC defined in constant expressions follow the expected scoping rules of the context they are placed in. This means that FCC in property default values may reference private methods of the class where they are defined, similarly to how a FCC defined in the constructor and stored in a property may access those private methods. Likewise are FCC in attribute parameters allowed to access private methods of the surrounding class.

Examples

<?php
 
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
class Attr {
    public function __construct(public Closure $value) {}
}
 
#[Attr(self::myMethod(...))]
#[Attr(strrev(...))]
class C {
    private static function myMethod(string $foo) {
        return "XXX";
    }
}
 
foreach ((new ReflectionClass(C::class))->getAttributes() as $reflectionAttribute) {
    $closure = $reflectionAttribute->newInstance()->value;
    var_dump($closure('abc'));
}
 
// Prints:
//
// string(3) "XXX"
// string(3) "cba"

Backward Incompatible Changes

None. Using first class callables within constant expressions previously resulted in a compile-time error. Closures can appear within constant expressions since the Support Closures in constant expressions RFC.

Nevertheless, as with every RFC that changes what previously was a compile-time error to be valid PHP code, this RFC requires changes to static analyzers and IDEs to correctly understand the semantics of the code and not erroneously report errors.

Proposed PHP Version(s)

Next 8.x (8.5).

RFC Impact

To SAPIs

None.

To Existing Extensions

None.

To Opcache

The current implementation uses a special AST structure to cache the resolved function for consistent behavior of the global function fallback. To correctly cache this AST structure, Opcache changes are required. The PR passes all tests both with and without JIT enabled.

New Constants

None.

php.ini Defaults

None.

Open Issues

None.

Unaffected PHP Functionality

Only constant expression are affected by this change, but Closures could already appear within them.

Future Scope

None.

Proposed Voting Choices

Support first class callables in constant expressions as proposed?
Real name Yes No
Final result: 0 0
This poll has been closed.

Patches and Tests

Implementation

After the project is implemented, this section should contain

  1. the version(s) it was merged into
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature
  4. a link to the language specification section (if any)

References

Rejected Features

n/a

rfc/fcc_in_const_expr.txt · Last modified: 2025/01/09 14:16 by timwolla