First-Class Callable Syntax (PHP 8.1)
debt(d5/e1/b1/t3)
Closest to 'specialist tool catches it' (d5), because the detection_hints list rector, phpstan, and php-cs-fixer — all specialist static-analysis or code-transformation tools. The common mistake (using string callables 'strlen' instead of strlen(...)) is not caught by a standard linter by default but is flagged by PHPStan rules and Rector migrations.
Closest to 'one-line patch or single-call swap' (e1), because the quick_fix explicitly states: replace 'strlen' or Closure::fromCallable('strlen') with strlen(...) — a mechanical, one-line substitution that Rector can even automate.
Closest to 'minimal commitment' (b1), because this is a localised syntactic choice at the call site. Adopting or not adopting first-class callable syntax imposes no structural weight on future maintainers; each usage is independent and trivially reversible.
Closest to 'minor surprise' (t3), because the misconception field notes it is often seen as merely a shorthand for Closure::fromCallable(), when it also works uniformly for static methods, instance methods, and built-ins. Additionally, the edge case that language constructs (isset, echo) are excluded is a small gotcha. These are one or two discoverable surprises, not a systematic contradiction.
Also Known As
TL;DR
Explanation
PHP 8.1 introduced first-class callable syntax: appending (...) to any callable produces a Closure without needing Closure::fromCallable() or an anonymous function wrapper. Works with functions (strlen(...)), static methods (MyClass::method(...)), instance methods ($obj->method(...)), and built-in functions (array_map(strtolower(...), $items)). This enables clean functional-style pipelines: $pipeline = array_map(trim(...), array_filter(strlen(...), $items)). The created Closure captures the callable at the point of creation — changes to the callable's implementation are reflected. PHPStan and Psalm treat first-class callables as typed Closures, improving static analysis.
Common Misconception
Why It Matters
Common Mistakes
- Still using string callables ('strlen') where strlen(...) gives IDE support and refactoring safety.
- Forgetting it works for static methods (Foo::bar(...)) and instance methods ($obj->method(...)).
- Using it where a plain closure fn($x) => doSomething($x) is more readable due to argument transformation.
- Trying to use it with language constructs like isset or echo — they are not functions and cannot be used this way.
Code Examples
// String callable — no IDE support, breaks on rename:
$lengths = array_map('strlen', $strings);
$fn = Closure::fromCallable('array_reverse'); // Verbose
// First-class callable — refactor-safe:
$lengths = array_map(strlen(...), $strings);
$fn = array_reverse(...);
// Before
$fn = Closure::fromCallable('strtolower');
$fn = fn(string $s) => strtolower($s);
// PHP 8.1
$fn = strtolower(...);
$result = array_map(strtolower(...), $strings);