skip to content

In PHP 8, how do named arguments let a caller skip optional parameters, and which errors can a named-argument call raise?

level: middleimportance: must knowfreq 58%

answer

  1. name: value after positional ones
  2. order-independent, skip any default
  3. Unknown named parameter $x
  4. overwrites previous argument
  5. positional after named: compile error

basics

~20 s

Since PHP 8.0 a call can pass name: value, matched by parameter name, so any optional parameter can be skipped in any order. An unknown or repeated name throws Error; a positional argument after a named one fails at compile time.

solid answer

~40 s

Named arguments, added in PHP 8.0, match a value to a parameter by its **name**: `buildReport('2026-Q3', format: 'json')`. They are order-independent and let you skip any optional parameter without restating the defaults before it, which is what makes a function with many optional filters usable. They may follow positional arguments, never precede them: `f(a: 1, 2)` is a compile-time `Cannot use positional argument after named argument`. At runtime, a name the function does not declare throws `Error: Unknown named parameter $formt`; naming a parameter already filled positionally throws `Error: Named parameter $period overwrites previous argument`; and a required parameter left out throws `ArgumentCountError` (`Argument #1 ($period) not passed`). Built-in functions take them too, using the parameter names in the manual.

code

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

function buildReport(
    string $period,
    ?string $region = null,
    ?string $product = null,
    int $minRevenue = 0,
    bool $includeRefunds = false,
    string $format = 'csv',
): string {
    return sprintf('%s/%s/%s/%d/%s/%s', $period, $region ?? '*', $product ?? '*',
        $minRevenue, $includeRefunds ? 'refunds' : 'net', $format);
}

// Positional: every default before $format must be repeated
echo buildReport('2026-Q3', null, null, 0, false, 'json'), "\n"; // 2026-Q3/*/*/0/net/json
// Named: only what differs, in any order
echo buildReport('2026-Q3', format: 'json', region: 'EU'), "\n";   // 2026-Q3/EU/*/0/net/json

go deeper

for a junior

Recall the name: value syntax, that named arguments come after positional ones, and that they let you skip optional parameters.

for a middle

Explain each failure: unknown name, overwrite, missing required parameter, positional after named, and which of them are catchable at runtime.

for a senior

Show where named arguments pay off, such as long optional lists and boolean flags, and how variadic option bags hide typos you would otherwise catch.

for a principal

Judge whether an API should invite named calls at all, since every parameter name then becomes a contract you must keep.

## What named arguments are Before PHP 8.0 every argument was **positional**: the first value went to the first parameter, the second to the second, and so on. PHP 8.0 added **named arguments**, written `parameterName: value` at the call site. The engine matches each one to the parameter with that name, so: - the call documents itself (`includeRefunds: true` instead of a bare `true`); - order no longer matters among the named ones; - any optional parameter can be skipped, however far down the list it sits. The name is a plain identifier, the parameter's name without the `$`. Reserved words are allowed (`array: $rows`, as in `array_slice(array: $rows, offset: 2)`), but the name cannot come from a variable. ## Skipping defaults in a report builder Picture a report function with one required parameter and five optional filters. Positionally, a caller who only wants JSON output must repeat every default in between: `buildReport('2026-Q3', null, null, 0, false, 'json')`. Each repeated default is a copy that silently goes stale if the function's default changes. With named arguments the caller writes only what differs: `buildReport('2026-Q3', format: 'json', region: 'EU')`. The skipped parameters take their declared defaults. ## Rules for mixing 1. **Positional first, named after.** `buildReport('2026-Q3', format: 'json')` is fine; `buildReport(period: '2026-Q3', 'json')` is rejected when the file compiles. 2. **Each parameter at most once.** A name may not repeat, and may not target a parameter a positional argument already filled. 3. **Required parameters are still required.** Skipping one, by name or by position, throws `ArgumentCountError`. 4. **After unpacking, since PHP 8.1.** `buildReport(...$base, format: 'json')` is allowed, as long as the named argument does not overwrite a value the unpack supplied. ## The errors, side by side | Call (for `buildReport(string $period, ..., string $format = 'csv')`) | Result | Raised | |---|---|---| | `buildReport('2026-Q3', formt: 'json')` | `Error: Unknown named parameter $formt` | at the call | | `buildReport('2026-Q3', period: '2026-Q4')` | `Error: Named parameter $period overwrites previous argument` | at the call | | `buildReport(format: 'json')` | `ArgumentCountError: buildReport(): Argument #1 ($period) not passed` | at the call | | `buildReport(period: '2026-Q3', 'json')` | `Fatal error: Cannot use positional argument after named argument` | at compile time | The first three are thrown objects you can catch; the last stops the file from compiling at all. One exception to the first row: if the function declares a **variadic** parameter (`...$options`), an unknown name is not an error, it is collected into that array under a string key. ## Built-in functions Named arguments work for internal functions as well, with the parameter names from the manual: - `htmlspecialchars($text, double_encode: false)` skips `$flags` and `$encoding`, keeping their defaults. - `array_slice($rows, 1, preserve_keys: true)` skips `$length`. - `str_pad('7', 3, pad_type: STR_PAD_LEFT)` skips `$pad_string`. A few internal parameters have **no default expressible in PHP**. `array_keys($map, strict: true)` throws `ArgumentCountError: array_keys(): Argument #2 ($filter_value) must be passed explicitly, because the default value is not known`; pass `filter_value:` too. ## What named arguments do not do - They do not rename or alias anything: the caller must use the exact name the called function declares. - They cannot target a parameter dynamically. To choose names at runtime, unpack an array with string keys, `buildReport(...$options)`, where each key becomes a named argument and an unknown key throws the same `Unknown named parameter` error. - They do not make a required parameter optional, and since PHP 8.1 they cannot rescue an optional parameter declared before a required one. ## When to reach for them Named arguments earn their place where a positional call would be unreadable or fragile: - **boolean and flag arguments**, where `includeRefunds: true` says what a bare `true` hides; - **long optional lists**, where the caller wants the fifth option and not the four before it; - **built-ins with awkward orders**, such as `htmlspecialchars()` when only `double_encode` differs. They cost something too. Every named call ties the caller to the callee's parameter names, so a rename in the called function breaks the call even though its positional shape is unchanged. For short, obvious signatures such as `max($a, $b)` or `str_contains($haystack, $needle)`, positional calls stay clearer. Many teams therefore use named arguments at call sites that pass flags or skip defaults, and positional ones everywhere else.

  • In PHP 8, how can a caller choose argument names at runtime if the name: syntax only takes literal identifiers?
    Unpack an array with string keys: `buildReport(...['period' => '2026-Q3', $key => $value])`. Each string key is treated as a named argument, integer keys as positional ones, and positional keys must come before string keys. An unknown key throws the same `Error: Unknown named parameter`, and since PHP 8.1 you can add literal named arguments after the unpack.
  • Why does array_keys($map, strict: true) throw in PHP 8.5 when array_keys($map) works?
    The optional `$filter_value` parameter of `array_keys()` has no default that PHP code can express, so it cannot be skipped by name. The call throws `ArgumentCountError: array_keys(): Argument #2 ($filter_value) must be passed explicitly, because the default value is not known`. Passing `filter_value:` along with `strict: true` works.
  • What happens to an unknown named argument when the PHP function declares a variadic ...$options parameter?
    It is not an error. The engine collects every unmatched named argument into the variadic array under its name as a string key, so `f('x', color: 'red')` for `function f($a, ...$options)` gives `$options === ['color' => 'red']`. That is handy for option bags, but a typo is then collected silently instead of throwing.

saying these in an interview costs you the question

  • Named arguments may come before positional ones if every name is correct.
  • An unknown named argument is ignored, like an extra positional argument.
  • Named arguments only work with user-defined functions, not built-ins.
  • The parameter name can come from a variable, as in $name: $value.
  • Passing a parameter positionally and again by name lets the named value win.