skip to content

In PHP 8, how do built-in functions such as strlen() handle wrong-typed and null arguments in coercive and strict mode?

level: middleimportance: should knowfreq 34%

answer

  1. built-ins have declared signatures too
  2. 8.0: TypeError, not warning plus null
  3. null to scalar: deprecated since 8.1
  4. strict mode: null is a TypeError
  5. user functions never coerce null

basics

~20 s

Since PHP 8.0 built-in functions check arguments against their declared types: a value that cannot be accepted throws TypeError. In coercive mode null passed to a non-nullable scalar parameter still works but is deprecated since 8.1; in strict mode it is a TypeError.

solid answer

~40 s

Built-in functions such as `strlen(string $string): int` have real parameter types and follow the caller's mode like user functions. In **coercive mode** `strlen(123)` returns `3`; in **strict mode** it throws `TypeError: strlen(): Argument #1 ($string) must be of type string, int given`. Since **PHP 8.0**, a value no mode can accept — an array for `string` — throws `TypeError` instead of the old warning plus a `null` return. The odd case is `null`: built-ins historically accepted it for scalar parameters, so in coercive mode `strlen(null)` still returns `0` but emits `Passing null to parameter #1 ($string) of type string is deprecated` since **PHP 8.1**. User functions never had that exception: `null` to a non-nullable parameter is a `TypeError` in both modes.

code

php · 16 lines
php
<?php
// coercive file
var_dump(strlen(123));   // int(3)
var_dump(strlen(null));  // int(0), plus:
// Deprecated: strlen(): Passing null to parameter #1 ($string) of type string is deprecated

function memberName(string $name): string
{
    return $name;
}

try {
    memberName(null);    // user function: no null exception
} catch (TypeError $e) {
    echo $e->getMessage(), PHP_EOL;
}

go deeper

for a junior

Recall that built-in functions have typed parameters and throw TypeError for an argument they cannot take, such as an array passed to strlen().

for a middle

Explain how built-ins follow the caller's mode, the PHP 8.0 switch from warning-plus-null to TypeError, and the 8.1 null deprecation that user functions never needed.

for a senior

Plan cleanup of null-to-built-in deprecations in a legacy codebase: find them in logs, fix meaning at the source, and keep E_DEPRECATED visible in CI.

for a principal

Treat deprecations as scheduled breakage: budget the upgrade path to a future major version rather than suppressing notices that mark tomorrow's TypeErrors.

## Built-in functions have signatures Functions that ship with PHP — `strlen()`, `str_repeat()`, `round()`, `array_sum()` — are declared in the engine's stub files with parameter and return types, for example `function strlen(string $string): int {}` and `function str_repeat(string $string, int $times): string {}`. Those declarations are enforced the same way as the ones in your own code, and they follow the **calling file's** `strict_types` mode. ## PHP 8.0: one error for bad arguments Before PHP 8.0 most built-ins handled a wrong argument by emitting a warning and returning `null`, so a bad call produced a quiet `null` that travelled onward. PHP 8.0 made the behaviour consistent: a built-in given an argument it cannot accept throws `TypeError` (and a value of the right type but invalid, such as a negative repeat count passed to `str_repeat()`, throws `ValueError`, which belongs to the exception-hierarchy topic). ```php <?php // coercive file, PHP 8.5 strlen([1, 2]); // TypeError: strlen(): Argument #1 ($string) must be of type string, array given ``` ## Coercive versus strict for built-ins In a file without `strict_types`, a built-in coerces scalars exactly like a user function; in a strict file it demands the declared type: | Call | Coercive file | Strict file | |---|---|---| | `strlen(123)` | `3` | `TypeError` | | `str_repeat('-', '3')` | `"---"` | `TypeError` | | `round('2.5')` | `3.0` | `TypeError` | | `strlen([1])` | `TypeError` | `TypeError` | | `strlen(null)` | `0`, plus a deprecation | `TypeError` | So turning on `strict_types` in a file also tightens every built-in call in it. A typical fallout is a width or count read from a config string and passed straight to `str_repeat()` or `str_pad()`. ## The null exception, and its deprecation `null` is the one place where built-ins and user functions differed. - A **user-defined** function never coerces `null`. A parameter declared `int` rejects `null` with `TypeError` in both modes; it accepts `null` only when declared nullable (`?int`, `int|null`). - A **built-in** function, in coercive mode, historically treated `null` for a scalar parameter as the empty value of that type: `strlen(null)` was `0`, `str_contains($haystack, null)` searched for `""`. **PHP 8.1** deprecated that exception to align the two. In PHP 8.5 the behaviour is still a deprecation, not an error: ```text Deprecated: strlen(): Passing null to parameter #1 ($string) of type string is deprecated ``` In **strict mode** there was never an exception for `null`: `strlen(null)` throws `TypeError`. Parameters that are genuinely nullable in the signature — declared `?string`, for example — accept `null` in both modes without any notice. ## Where this bites in real code 1. **Nullable data meeting string functions.** A column that can be `NULL`, a missing array key read with `??` omitted, or an optional request field ends up in `trim()`, `strtolower()` or `htmlspecialchars()`. On PHP 8.1+ each call logs a deprecation; a later major version may turn it into a `TypeError`. The fix is to decide explicitly what `null` means — `$name ?? ''`, or an early branch — rather than silencing notices. 2. **Strict files calling built-ins with strings.** `str_repeat('-', $width)` where `$width` came from a config file as `'3'` works in a coercive file and throws after the file gains `declare(strict_types=1)`. 3. **Return types around built-ins.** `round()` is declared to return `float`, so `return round($x);` from a function declared `: int` fails in a strict declaring file, and in a coercive file relies on the float having no fractional part. 4. **Catching the wrong thing.** `TypeError` extends `Error`, not `Exception`, so legacy `catch (Exception $e)` blocks written for the old warning-plus-null era do not catch it. ## Summary for an interview - Built-ins are typed and follow the caller's mode. - PHP 8.0: unacceptable arguments throw `TypeError` instead of warning and returning `null`. - PHP 8.1: `null` to a non-nullable scalar parameter of a built-in is deprecated in coercive mode; strict mode already rejected it. - User functions never had the `null` exception.

  • In PHP 8.1+, what is the right fix for a flood of 'Passing null to parameter' deprecations from trim() and strtolower()?
    Decide what `null` means at the point it enters: default it (`$row['note'] ?? ''`), branch on it, or make the upstream value non-nullable. Suppressing `E_DEPRECATED` hides the list of call sites you will have to fix once the deprecation becomes an error, and casting everything with `(string)` blindly can mask real missing-data bugs.
  • In PHP 8, what did a built-in like strlen() do with an array argument before and after PHP 8.0?
    Before 8.0 it emitted a warning and returned `null`, so execution continued with a `null` where an int was expected. Since 8.0 it throws `TypeError: strlen(): Argument #1 ($string) must be of type string, array given` in both coercive and strict mode, because an array is not coercible to string.

saying these in an interview costs you the question

  • Says strict_types only applies to user-defined functions, not built-ins.
  • Thinks PHP 8 built-ins still warn and return null on a wrong argument type.
  • Believes strlen(null) is already a TypeError in coercive mode on PHP 8.5.
  • Claims user functions also silently accept null for a non-nullable string parameter.
  • Plans to silence E_DEPRECATED instead of fixing null-to-built-in call sites.