In PHP, how do default parameter values work, and what happens when an optional parameter is declared before a required one?
answer
- used only when the argument is omitted
- an explicit null is not an omission
- constant expression, not a variable or call
- 8.0 deprecation: optional before required
- 8.1: treated as required, even by name
basics
~20 sA default applies only when the caller omits that argument; an explicit null is passed as null. Defaults must be constant expressions. An optional parameter declared before a required one is deprecated since PHP 8.0 and treated as required.
solid answer
~50 sWriting `$format = 'csv'` makes a parameter optional: PHP uses the default **only when the argument is not passed at all**. Passing `null` is passing a value, so the default does not kick in. The default must be a **constant expression** — a literal, an array, a constant or class constant, and since PHP 8.1 a `new` expression — never a variable, a property or a function call. Omitting a parameter that has no default throws `ArgumentCountError`. Optional parameters belong after required ones: since PHP 8.0 `function f($a = 1, $b)` emits a deprecation when compiled, and the engine drops the useless default, so `$a` is simply required. Since PHP 8.1 that holds even for a named call like `f(b: 2)`, which throws `ArgumentCountError`. The one form spared that notice, `Type $a = null`, is deprecated since PHP 8.4 as an implicitly nullable type.
code
php · 17 lines<?php
declare(strict_types=1);
function exportReport(string $period, string $format = 'csv', int $limit = 100): string
{
return "$period as $format, max $limit rows";
}
echo exportReport('2026-Q3'), "\n"; // 2026-Q3 as csv, max 100 rows
echo exportReport('2026-Q3', 'json'), "\n"; // 2026-Q3 as json, max 100 rows
echo exportReport('2026-Q3', limit: 10), "\n"; // 2026-Q3 as csv, max 10 rows
try {
exportReport('2026-Q3', null);
} catch (TypeError $e) {
echo $e->getMessage(), "\n"; // null is not string; the default is not used
}go deeper
Recall that a default is used only when the argument is omitted, that null is a real value, and that optional parameters go after required ones.
Explain what counts as a constant expression, why the compiler drops a default placed before a required parameter, and how 8.1 made that stick for named calls.
Show how you would clean up legacy signatures with the 8.0 and 8.4 deprecations in logs, choosing between dropping dead defaults and explicit nullable types.
Weigh long optional-parameter lists against option objects or named arguments as an API style, and how each ages as defaults change.
## What a default value is A **parameter** is the variable a function declares in its signature; an **argument** is the value a caller supplies for it. Giving a parameter a **default value** — `string $format = 'csv'` — makes it **optional**: a caller may leave it out, and PHP then fills the parameter with the default before the function body runs. A parameter without a default is **required**, and leaving it out throws `ArgumentCountError` (since PHP 7.1; older releases only warned). ## When the default applies The rule is narrow: the default is used **only when no argument arrives for that parameter**. It is not a fallback for "empty" values. | Call to `exportReport(string $period, string $format = 'csv')` | `$format` inside the function | |---|---| | `exportReport('2026-Q3')` | `'csv'` (argument omitted) | | `exportReport('2026-Q3', 'json')` | `'json'` | | `exportReport('2026-Q3', '')` | `''` (an empty string is a value) | | `exportReport('2026-Q3', null)` | a `TypeError`, because `null` is not a `string` | With an **untyped** parameter, `null` would simply arrive as `null`. Either way PHP never swaps in the default for you. If "no value" should mean "use the default", declare the parameter nullable with a `null` default and resolve it in the body, for example `$limit ??= 100;`. ## What a default may be A default must be a **constant expression**, something PHP can evaluate without running your code: - scalar literals: `10`, `'csv'`, `true`, `null`; - arrays: `[]`, `['id', 'total']`; - constants and class constants: `PHP_INT_MAX`, `self::DEFAULT_LIMIT`; - operators over those: `self::PAGE * 2`; - since PHP 8.1, `new ClassName(...)` (a separate topic; each call that omits the argument gets a fresh object). What is **not** allowed: variables (`$config['limit']`), properties (`$this->limit`) and function calls (`time()`, `getenv('LIMIT')`). Those fail at compile time. A default that names a constant is resolved when a call actually needs it, so the constant may be defined after the function; if it is still undefined then, the call throws `Error: Undefined constant`. ## Why optional parameters must come last Positional arguments fill parameters left to right. In `function makeLabel($prefix = 'ID', $number)`, a caller can never skip `$prefix` positionally, because the first argument always lands in it. The default is dead code. PHP's handling of this has tightened over three releases: 1. **PHP 8.0** deprecated the pattern. Compiling the function emits `makeLabel(): Optional parameter $prefix declared before required parameter $number is implicitly treated as a required parameter`. 2. **PHP 8.1** made the parameter required **even for named arguments**. In 8.0, `makeLabel(number: 7)` still ran and used `'ID'`; since 8.1 the engine discards the default during compilation, so the same call throws `ArgumentCountError: makeLabel(): Argument #1 ($prefix) not passed`. 3. **PHP 8.4** closed the last exemption. `Foo $foo = null, $bar` never got the 8.0 notice, because it was the PHP 5 way to write a nullable type. It is still required, and since 8.4 it triggers the implicitly-nullable deprecation instead; write `?Foo $foo, $bar`. The fix is almost always to drop the default, since it could never be used, or to move the parameter after the required ones. ## Reading the errors Two different diagnostics appear around defaults, and they fire at different moments: - The **deprecation** is raised once, when PHP compiles the file that declares the function. It points at the declaration, names both parameters, and does not stop anything from running. With `display_errors` off it shows up only in the log, which is why legacy code can carry it for years. - The **`ArgumentCountError`** is thrown at each call that leaves out a required parameter. Its message names the function, the argument number and the parameter: `Argument #1 ($prefix) not passed` for a named call, or `Too few arguments to function makeLabel(), 1 passed ... and exactly 2 expected` for a positional one. Seeing the deprecation in a log is the cue to fix the signature before a caller switches to named arguments and hits the exception. ## Practical rules - Put required parameters first and optional ones after them, ordered by how often callers change them. - Do not use `null` as a signal for "use the default" unless the parameter is explicitly nullable and the body handles it. - Keep defaults simple constant expressions; anything computed belongs in the body. - When a function grows many optional parameters, callers should skip the middle ones with **named arguments** (`exportReport('2026-Q3', limit: 10)`), available since PHP 8.0, instead of repeating every default positionally.
- Can a PHP parameter default refer to a constant that is defined later in the script?Yes. A constant in a default is resolved when a call actually needs the default, not when the function is declared. So `function connect($mode = APP_MODE)` works as long as `define('APP_MODE', ...)` has run before the first call that omits `$mode`. If the constant is still undefined at that call, it throws `Error: Undefined constant "APP_MODE"`.
- How do you get 'use the default' behaviour when the caller holds a value that may be null?Declare the parameter explicitly nullable with a `null` default, `?int $limit = null`, and resolve it in the body with `$limit ??= 100;`. Then both an omitted argument and an explicit `null` end up as 100. The alternative is to leave the argument out of the call, for example by passing only the named arguments you actually have.
saying these in an interview costs you the question
- Passing null to a parameter makes PHP use its default value.
- A default can be any expression, such as time() or $this->limit.
- Callers can skip an optional parameter that sits before a required one.
- Named arguments make an optional-before-required parameter skippable in PHP 8.5.
- Omitting a required argument just passes null with a warning.