strlen(...) Is Valid PHP Now: First-Class Callables
For twenty years, passing a function in PHP meant passing its name as a string - 'strtoupper' in quotes, immune to IDE refactoring, invisible to static analysis, resolved at runtime with a prayer. PHP 8.1's three-dot syntax finally made functions real values:
$upper = strtoupper(...); // a Closure wrapping strtoupper
$upper('hello'); // "HELLO"
$names = array_map(strtoupper(...), $names);
$valid = array_filter($emails, filter_var(...)); // partial trickery aside, it composes
The (...) isn't calling the function - it's capturing it. Works on everything callable:
$fn = trim(...); // built-in function
$fn = $formatter->format(...); // instance method (binds $formatter)
$fn = Invoice::fromArray(...); // static method
$fn = $object(...); // __invoke object
Why it beats the string (and array) spellings
// The old ways - stringly typed
array_map('strtoupper', $names);
array_map([$formatter, 'format'], $rows);
usort($items, [self::class, 'compare']);
Rename format() with your IDE and the array-callable version silently keeps the old name - a runtime error waiting in a code path you didn't test. The new syntax is parsed as a real reference: refactors follow it, "find usages" sees it, PHPStan type-checks its signature, and it respects visibility from the capturing scope (you can capture a private method inside the class and hand it out - the closure remembers where it was born).
Where it changes daily code
// Pipelines stop being noise
$slugs = array_map(Str::slug(...), $titles);
// Sorting by an existing comparator
usort($versions, version_compare(...));
// Event/route tables that survive refactoring
$handlers = [
'order.paid' => $mailer->sendReceipt(...),
'order.refunded' => $mailer->sendRefundNote(...),
];
// Cleaner than fn() wrappers when the signature already matches
$lengths = array_map(strlen(...), $words); // vs fn($w) => strlen($w)
The last point is the taste question people ask: when the target function's signature matches exactly, capture it; when you need to reorder, fill or adapt arguments, a short arrow closure is still right. Both beat strings every time.
Footnotes
- Capture happens at that line:
$obj->method(...)binds that $obj forever, like Closure::fromCallable did (this syntax is literally sugar for it). - No partial application (yet) - strtoupper(...) takes no pre-bound arguments; the RFC for that is a separate saga.
- Sorting collections this way pairs beautifully with the spaceship operator - the sorting toolkit covers that combo.
Small syntax, but it upgrades PHP's whole functional layer from string-matching folklore to actual references. Grep your codebase for array_map(' and start the cleanup.
Full-stack web developer sharing practical tutorials and building tools that ship.
Got something on your mind?
My inbox is open - no forms disappearing into the void here.
- Just say hello Found a tutorial useful? Spotted a mistake? Tell me.
- Hire me for a project Have something custom in mind? Let's talk scope and timelines.
- Product support Bought something here? I'll help you get it running.
I usually reply within 1-2 business days.