skip to content

Invocation Forms & Pipes

PHP accepts strings, [object, 'method'] arrays, closures and invokable objects as callables, and 8.5 adds the |> pipe. Interviewers ask which forms still work and how strlen(...) differs.

part ofPHPoverview, primer and where to startread it →
on this pageshow

explore

questions

5

In PHP 8.5, which values count as a callable, and which older callable forms are deprecated?

level: juniorimportance: must knowfreq 55%

answer

  1. string name, array pair, object, Closure
  2. strings are always fully qualified
  3. 'Class::method' and [Class::class, 'method']
  4. 8.2: self, parent, static in callables
  5. the form $callable() cannot call

basics

~20 s

A PHP callable is a function-name string, a 'Class::method' string, an [object or class, 'method'] array, a Closure, or an invokable object. Since PHP 8.2, relative forms such as 'self::method' or ['parent', 'method'] are deprecated.

solid answer

~40 s

PHP accepts five shapes wherever a `callable` is expected: a **string** naming a function (`'strtoupper'`) or a static method (`'Slugger::clean'`); an **array** of an object or class name plus a method name (`[$slugger, 'slug']`, `[Slugger::class, 'clean']`); a **`Closure`**, whether from `function`, `fn` or the `strlen(...)` syntax; and an **object with `__invoke()`**. All of them can be invoked directly as `$callable($arg)`. String names are resolved as fully qualified, so inside a namespace `'shout'` means the global `\shout`, not `App\Text\shout`. PHP 8.2 deprecated the **relative callables** that only `call_user_func()`-style calls understood: `'self::m'`, `'parent::m'`, `'static::m'`, `['self', 'm']` and the like, and `['B', 'A::m']`. They still run in 8.5 but emit `E_DEPRECATED`; use `self::class`, `static::class` or `self::m(...)` instead.

code

php · 29 lines
php
<?php
declare(strict_types=1);

namespace App\Text;

final class Slugger
{
    public function slug(string $s): string { return strtolower(trim($s)); }
    public static function clean(string $s): string { return trim($s); }
}

function shout(string $s): string { return strtoupper($s); }

$slugger = new Slugger();
$forms = [
    'strtoupper',                  // global function
    'App\Text\shout',              // namespaced function: fully qualified
    'App\Text\Slugger::clean',     // "Class::method" string
    [$slugger, 'slug'],            // object + method
    [Slugger::class, 'clean'],     // class + static method
    fn (string $s): string => $s,  // Closure
    shout(...),                    // first-class callable (8.1+)
];
foreach ($forms as $callable) {
    echo '[', $callable(' Title '), ']';
}
// [ TITLE ][ TITLE ][Title][title][Title][ Title ][ TITLE ]

// 'shout' alone would mean \shout(): Error: Call to undefined function shout()

go deeper

for a junior

Recall the shapes a callable can take: function-name string, [object, 'method'] array, Closure and invokable object, and that each can be called as $f().

for a middle

Explain namespace resolution of string callables, scope and visibility for array callables, and exactly which relative forms 8.2 deprecated.

for a senior

Plan the migration of relative callables and string callbacks to first-class callables, and explain why Closure is the type to store callbacks in properties.

for a principal

Decide where a codebase still accepts string or array callables, such as configuration, versus closures everywhere else, weighing flexibility against static analysis.

## What "callable" means in PHP A **callable** is any value PHP can invoke as a function. Functions such as `array_map()`, `usort()` and `preg_replace_callback()` take one, the `callable` type declaration accepts one, and `is_callable()` tests for one. PHP does not have a single function type; instead it recognises several shapes of value. ## The five forms | Form | Example | Invokes | |---|---|---| | function-name string | `'strtoupper'`, `'App\Text\shout'` | a global or namespaced function | | `"Class::method"` string | `'App\Text\Slugger::clean'` | a static method | | array `[object, 'method']` | `[$slugger, 'slug']` | an instance method on that object | | array `[class, 'method']` | `[Slugger::class, 'clean']` | a static method | | `Closure` object | `fn (string $s) => trim($s)`, `trim(...)` | the closure | | invokable object | `new TitleNormalizer()` | its `__invoke()` method | Every form can be called directly: `$callable(' Title ')`, including strings with `::` and both array forms. Language constructs such as `echo`, `isset`, `empty` or `unset` are not functions, so their names are not callables; wrap them in a function when you need one. ## Namespaces and string callables A string callable is resolved at call time with **no namespace context**. Inside `namespace App\Text;`, the call `shout($s)` finds `App\Text\shout()`, but the string `'shout'` only finds a global `\shout()`, so `array_map('shout', $titles)` throws `Error: Call to undefined function shout()`. Write the fully qualified name, `'App\Text\shout'` or `__NAMESPACE__ . '\shout'`, or avoid the string entirely with `shout(...)`, which the compiler resolves like a normal call. ## Visibility is checked where the call happens String and array callables carry no scope. `[$this, 'privateMethod']` works inside the class, but if the array is returned and invoked from outside, PHP throws `Error: Call to private method ... from global scope`. A closure created inside the class, for example `$this->privateMethod(...)`, keeps the scope it was created in and works anywhere. ## Relative callables: deprecated since PHP 8.2 Some callables only ever worked through the callback machinery (`call_user_func()`, `array_map()`, `is_callable()`, the `callable` type) and never through `$callable()`. PHP 8.2 deprecated them: - strings `"self::method"`, `"parent::method"`, `"static::method"`; - arrays `["self", "method"]`, `["parent", "method"]`, `["static", "method"]`; - arrays whose method part names another class: `["Foo", "Bar::method"]`, `[new Foo, "Bar::method"]`. In PHP 8.5 they still work but raise `Deprecated: Use of "self" in callables is deprecated` or `Deprecated: Callables of the form ["B", "A::foo"] are deprecated`. Even `is_callable()` raises the notice while returning `true`. Ordinary forms such as `"A::method"` and `["A", "method"]` are **not** affected. Replacements: 1. `[self::class, 'method']` or `[static::class, 'method']` for static methods, choosing `static::class` when late static binding is intended; 2. `[$this, 'method']` for instance methods; 3. best, a first-class callable such as `self::method(...)`, `static::method(...)` or `$this->method(...)`, which is checked when it is created and understood by static analysers. ## The `callable` type and its limits The `callable` type declaration can be used for parameters and return values but **not for properties**, because whether a string or array is callable depends on the scope it is used from. To store a callback in a property, type it `Closure` and convert on the way in (`$this->step = $step(...)` for a callable held in a variable). ## Choosing a form in modern code - Prefer **closures**: `fn` for inline logic, `name(...)` for existing functions and methods. - Keep **string and array callables** for configuration data and for code that must stay compatible with them, and write strings fully qualified. - Replace every relative callable before the deprecation turns into removal. ## Traps interviewers like to probe - **Existence is checked late.** A string or array callable is only a value; a typo surfaces when something finally calls it, often deep inside a library. - **Static methods through an instance.** `[$slugger, 'clean']` also works for a static method, but it hides the fact that no instance is needed; `[Slugger::class, 'clean']` states it. - **`is_callable()` depends on where you ask.** The same array can be callable inside a class and not outside it. - **Invokable objects are callables, not closures.** They pass a `callable` check but fail a `Closure` type declaration until converted with `$obj(...)`.

  • In PHP 8.5, what should replace the deprecated callable 'static::create' inside a class?
    Either `[static::class, 'create']`, which keeps late static binding by resolving the class name first, or the first-class callable `static::create(...)`, which produces a `Closure` checked at creation time. Both avoid the `Use of "static" in callables is deprecated` notice. `[self::class, 'create']` is only equivalent if late static binding was not intended.
  • Why does is_callable([$this, 'helper']) return true inside a class but the same array fail when a caller outside invokes it?
    Array and string callables carry no scope; visibility is checked wherever the check or call happens. Inside the class a private `helper()` is accessible, so `is_callable()` is true there. Invoked from outside, the same array throws `Error: Call to private method`. Returning `$this->helper(...)` instead gives the caller a closure that keeps the class scope.

saying these in an interview costs you the question

  • Inside a namespace, the string 'helper' resolves to the current namespace's helper().
  • 'self::method' is the recommended way to pass a static method as a callback.
  • Array callables like [$obj, 'method'] cannot be called directly with $f().
  • Relative callables stopped working in PHP 8.2.
  • A property can be typed callable to store any callback.
open as a page

In PHP 8.1+, what does first-class callable syntax like strlen(...) create, and why prefer it over 'strlen' or [$obj, 'method'] callables?

level: middleimportance: should knowfreq 45%

basics

~20 s

strlen(...) (PHP 8.1+) creates a Closure from the function or method it names, resolving it right there with the current scope. Typos fail on that line, private methods can be handed out, and IDEs and static analysers can follow it.

open as a page

In PHP, what do is_callable(), call_user_func() and call_user_func_array() do, and when is $callable(...$args) the better choice?

level: middleimportance: should knowfreq 38%

basics

~20 s

is_callable() reports whether a value can be called from the current scope. call_user_func() calls it with listed arguments; call_user_func_array() with an array, whose string keys become named arguments since PHP 8.0. Modern code usually writes $callable(...$args) instead.

open as a page

In PHP 8.5, what does the |> pipe operator do, and what must the callable on its right-hand side satisfy?

level: middleimportance: should knowfreq 40%

basics

~20 s

PHP 8.5's |> passes its left value as the only argument to the callable on its right, so chains read in execution order. That callable must need one argument, not take it by reference, and an arrow function there must be parenthesized.

open as a page

A PHP 8.5 title pipeline built with |> throws TypeError: strtolower(): Argument #1 ($string) must be of type string, bool given; what went wrong, and how should pipe steps be designed?

level: seniorimportance: nice to knowfreq 18%

basics

~20 s

A pipe feeds each step's return value into the next, so a validator returning true or false replaced the title with a bool. Steps must return the value for the next step; validators return their input or throw, since |> never short-circuits.

open as a page