In PHP 8, when does a function call throw TypeError, ValueError or ArgumentCountError, and how are those three classes related?
answer
- wrong type vs wrong value
- ValueError new in 8.0
- ArgumentCountError extends TypeError
- extra args to user functions are fine
- Argument #N ($name) in the message
basics
~20 sTypeError means a value has the wrong type; ValueError (PHP 8.0) means the type is right but the value is invalid; ArgumentCountError, a TypeError subclass, means too few arguments, or the wrong count for a built-in.
solid answer
~40 s`TypeError` is thrown when a value does not match a declared type: an argument, a return value or a typed property. Built-ins throw it too, so `strlen([])` fails with `strlen(): Argument #1 ($string) must be of type string, array given`. `ValueError`, added in PHP 8.0, is for the right type with an unacceptable value: `str_repeat('-', -1)` throws `Argument #2 ($times) must be greater than or equal to 0`, and `array_chunk($a, 0)` throws `must be greater than 0`. `ArgumentCountError` extends `TypeError` and is thrown for too few arguments to any function, and for the wrong number to a non-variadic built-in; extra arguments to a user function are silently accepted. So `catch (TypeError)` also catches `ArgumentCountError`, while `ValueError` sits directly under `Error`. In PHP 7 many of these cases were a warning plus `null`.
code
php · 21 lines<?php
declare(strict_types=1);
function pair(int $a, int $b): int { return $a + $b; }
$calls = [
fn () => strlen([]), // TypeError
fn () => str_repeat('-', -1), // ValueError
fn () => pair(1), // ArgumentCountError
fn () => pair(1, 2, 3), // fine: extra argument ignored
];
foreach ($calls as $call) {
try {
var_dump($call());
} catch (ArgumentCountError | ValueError $e) {
echo get_class($e), ': ', $e->getMessage(), PHP_EOL;
} catch (TypeError $e) {
echo 'TypeError: ', $e->getMessage(), PHP_EOL;
}
}go deeper
Know that PHP 8 throws TypeError for wrong types and ValueError for bad values, and that these are Errors, not Exceptions.
Place ArgumentCountError under TypeError, explain the user-function versus built-in argument-count rules, and read the Argument #N ($name) messages.
Treat these errors as caller bugs in logs and alerts, validate external input before built-in calls, and order catch clauses for subclass-first matching.
Decide how libraries in the organisation signal invalid arguments, balancing Error classes that mirror the engine against Exception classes callers already catch.
## Three engine errors for bad calls When a call is wrong, PHP 8 distinguishes **what** is wrong and throws a specific `Error` subclass. All three are on the `Error` branch of `Throwable`, so `catch (Exception)` sees none of them. | Class | Parent | Meaning | Example | |---|---|---|---| | `TypeError` | `Error` | value has the wrong type | `strlen([])` | | `ArgumentCountError` | `TypeError` | wrong number of arguments | calling `f($a, $b)` as `f(1)` | | `ValueError` | `Error` | right type, invalid value | `str_repeat('-', -1)` | ## TypeError Thrown when a value does not fit a declared type: - an **argument** that cannot be accepted for a parameter type, in user functions and built-ins alike; - a **return value** that does not match the declared return type; - a **typed property** assigned a value of the wrong type; - operators applied to unsupported operands, such as `[] + 1` or `"abc" * 2` (`Unsupported operand types: string * int`). Whether a scalar like `"42"` is coerced to `int` or rejected depends on the calling file's `strict_types` mode; arrays and objects are never coerced to scalars, so `strlen([])` fails in both modes. ## ValueError Added in PHP 8.0 for arguments whose **type is right but value is not**. Built-in functions now validate their inputs and throw it instead of warning: - `str_repeat('-', -1)`: `Argument #2 ($times) must be greater than or equal to 0`; - `array_chunk($items, 0)`: `Argument #2 ($length) must be greater than 0`; - `array_combine($keys, $values)` with different lengths: the two arrays must have the same number of elements; - `random_int(10, 1)`: `Argument #1 ($min) must be less than or equal to argument #2 ($max)`. In PHP 7 many such calls emitted a warning and returned `null` or `false`, and code that forgot to check the return value carried on with a bad value. ## ArgumentCountError A subclass of `TypeError`, thrown when: 1. a **user-defined** function or method receives **too few** arguments: `Too few arguments to function f(), 1 passed in ... and exactly 2 expected`; 2. a **non-variadic built-in** receives too few **or too many** (an exception for built-ins since PHP 8.0, where PHP 7 often only warned). Passing **too many** arguments to a user-defined function is **not** an error: the extras are ignored by the parameters but visible through `func_get_args()`. Because of the inheritance, `catch (TypeError $e)` also catches `ArgumentCountError`. If you need to treat them differently, put the `ArgumentCountError` clause first. ## Ordering catch clauses PHP tries `catch` clauses top to bottom and uses the first one whose class matches. Because `ArgumentCountError` is a `TypeError`: - `catch (TypeError $e)` placed first also takes every `ArgumentCountError`; - to treat them differently, list `ArgumentCountError` first, then `TypeError`; - `ValueError` never overlaps with either, so its position does not matter; - a final `catch (Error $e)` picks up the rest of the engine branch, such as `DivisionByZeroError`. ## Reading the messages Since PHP 8.0 built-in errors use a consistent format naming the function, the argument position and the parameter name: `str_repeat(): Argument #2 ($times) must be greater than or equal to 0`. That format makes logs searchable and tells you which argument to fix without reading the manual. ## What to do with them - These errors almost always mean the **caller** has a bug: fix the call, do not retry it. - Validate external input **before** passing it to built-ins, so users see a validation message rather than a `ValueError`. - Libraries that validate arguments themselves usually throw `InvalidArgumentException` (an `Exception`) so callers' existing handlers see it; the engine's own checks use the `Error` classes.
- Why is ArgumentCountError a subclass of TypeError rather than of ValueError?A wrong argument count is a mismatch with the function's signature, which is a type-level contract, not a problem with one argument's value. Placing it under `TypeError` means existing `catch (TypeError)` handlers also cover it, while `ValueError` stays reserved for right-type, wrong-value arguments.
- What did str_repeat('-', -1) do before PHP 8.0, and why does the change matter?It emitted a warning and returned `null`. Code that ignored warnings continued with `null` where a string was expected, failing later somewhere unrelated. Since 8.0 the `ValueError` stops execution at the faulty call, so the stack trace points at the real cause.
saying these in an interview costs you the question
- ValueError is a subclass of TypeError.
- Passing too many arguments to a user-defined function throws ArgumentCountError.
- catch (ValueError $e) also catches ArgumentCountError.
- Built-in functions in PHP 8 still return null with a warning for invalid arguments.
- strlen([]) succeeds when strict_types is off, because arrays coerce to strings.