In PHP 8, what happens when arithmetic uses a numeric string, a leading-numeric string such as '5 boxes', a non-numeric string or an array?
answer
- numeric string: silently a number
- leading-numeric: value plus E_WARNING
- "A non-numeric value encountered"
- non-numeric or array: TypeError
- "Unsupported operand types: string + int"
basics
~20 sA numeric string behaves as a number silently. A leading-numeric string like '5 boxes' uses its numeric prefix and raises E_WARNING 'A non-numeric value encountered'. A non-numeric string, an array or a plain object throws TypeError 'Unsupported operand types'.
solid answer
~40 sSince **PHP 8.0**, arithmetic sorts its string operands into three groups. A **numeric string** such as `'10'`, `' 10 '` or `'2.5e1'` is converted to `int` or `float` silently: `'10' + 5` is `15`. A **leading-numeric** string such as `'5 boxes'` uses its numeric prefix, so `'5 boxes' + 1` is `6`, but PHP raises an **`E_WARNING`**, "A non-numeric value encountered". A **non-numeric** string such as `'boxes'` throws a **`TypeError`**, "Unsupported operand types: string + int". Arrays and plain objects in arithmetic also throw `TypeError` (`array + int`); `null` counts as `0` and booleans as `0` or `1`. Before 8.0, the non-numeric case quietly produced `0` with a warning, and the leading-numeric case only raised a notice. The explicit `(int)` cast is the silent exception: it accepts all three without complaint.
code
php · 18 lines<?php
declare(strict_types=1);
var_dump('10' + 5); // int(15)
var_dump(' 2.5 ' * 2); // float(5)
var_dump('5 boxes' + 1); // int(6) + Warning: A non-numeric value encountered
try {
var_dump('boxes' + 1);
} catch (TypeError $e) {
echo $e->getMessage(), PHP_EOL; // Unsupported operand types: string + int
}
try {
var_dump([1, 2] * 2);
} catch (TypeError $e) {
echo $e->getMessage(), PHP_EOL; // Unsupported operand types: array * int
}go deeper
Know the three outcomes in PHP 8: numeric strings work silently, leading-numeric strings work with a warning, and non-numeric strings throw a TypeError.
Explain the numeric-string grammar, the PHP 8.0 changes including trailing whitespace, and why arrays and plain objects in arithmetic throw TypeError.
Plan an upgrade or import around these rules: validate at the boundary, turn warnings into test failures and resist casting just to silence errors.
Decide how a codebase treats implicit numeric conversion overall, favouring validated typed values over relying on engine coercion at arithmetic sites.
## Why this question matters Arithmetic operators (`+`, `-`, `*`, `/`, `%`, `**`) need numbers. When an operand is a string, PHP converts it, and PHP 8.0 made that conversion much stricter. Code that silently computed nonsense on PHP 7 now warns or throws, which is exactly what upgrade projects and interview questions probe. ## The three kinds of string PHP's manual defines a **numeric string** as optional whitespace, an optional sign, a decimal integer or float (exponent allowed), then optional whitespace. Every string falls into one of three groups: | Kind | Examples | In arithmetic (PHP 8.0+) | |---|---|---| | numeric | `'10'`, `' 10 '`, `'-3.5'`, `'2.5e1'` | converted silently to `int` or `float` | | leading-numeric | `'5 boxes'`, `'10.5kg'` | numeric prefix used, `E_WARNING` "A non-numeric value encountered" | | non-numeric | `'boxes'`, `''`, `'n/a'` | `TypeError` "Unsupported operand types: string + int" | A numeric string becomes an `int` when it is an integer that fits, otherwise a `float`: `'10' + 5` is `int(15)`, `'2.5' + 1` is `float(3.5)`. The PHP 8.0 change also made **trailing whitespace** legal in numeric strings. Before 8.0, `'10 '` was leading-numeric (and produced a notice); now it is simply numeric. ## Other operand types The same PHP 8.0 tightening covers non-string operands: - **arrays**: `[1, 2] * 2` throws `TypeError` "Unsupported operand types: array * int". (The one exception is `array + array`, which is the array union operator, a different operation altogether.) - **plain objects**: `new stdClass() + 1` throws `TypeError` "Unsupported operand types: stdClass + int". Some internal classes, such as GMP numbers, define their own arithmetic. - **`null`** is treated as `0` and **booleans** as `0` or `1`, silently: `null + 5` is `5`, `true + 1` is `2`. ## What changed in PHP 8.0 | Input | PHP 7.x | PHP 8.0 and later | |---|---|---| | `1 + '10 Small Pigs'` | `11` with `E_NOTICE` | `11` with `E_WARNING` | | `1 + 'bob3'` | `1` with `E_WARNING` | `TypeError` | | `1 + '10 '` | `11` with `E_NOTICE` | `11`, no diagnostic | The manual's examples show the same pattern: `1 + "bob3"` is a `TypeError` as of PHP 8.0.0, where it used to give `1`. ## Casts are the silent exception An explicit cast accepts all three string kinds with no diagnostic at all: `(int) '5 boxes'` is `5`, `(int) 'boxes'` is `0`. This asymmetry is intentional: a cast says "I know what I am doing", arithmetic does not. It also means that casting a value *before* arithmetic hides exactly the problems PHP 8 tries to surface: ```php $total = $unitPrice * $row['qty']; // throws for 'n/a', warns for '5 boxes' $total = $unitPrice * (int) $row['qty']; // silently multiplies by 0 or 5 ``` ## Handling it properly 1. **Validate at the boundary.** Check that input is numeric in the form you expect before doing arithmetic, and reject or report rows that are not. 2. **Treat the warning as a bug.** In development and CI, convert warnings to exceptions so a leading-numeric string fails the test instead of passing with a message in a log. 3. **Catch `TypeError` only at a boundary**, such as a per-row import loop, where you can report the bad row; do not wrap individual expressions. 4. **Do not "fix" a `TypeError` with a cast** unless `0` is genuinely the right answer for garbage input. ## Beyond arithmetic Other numeric operators follow the same three-way rule, for example `%` and the bit shifts: the php-src test for invalid numeric strings exercises `+`, `-`, `*`, `/`, `**`, `%`, `<<` and `>>` and expects the same warning or `TypeError` for each.
- Is '10 ' (with a trailing space) numeric in PHP 8?Yes. Since PHP 8.0 trailing whitespace is allowed in numeric strings, so `'10 ' + 1` is `11` with no diagnostic, and `is_numeric('10 ')` returns `true`. Before 8.0, trailing whitespace made the string leading-numeric, so arithmetic raised a notice and `is_numeric()` returned `false`.
- Why does [1, 2] + [3] not throw a TypeError when [1, 2] + 1 does?Because `+` between two arrays is not arithmetic: it is the array union operator, which keeps the left array's keys and adds missing keys from the right. Arithmetic on an array with a number has no meaning, so PHP 8 throws `TypeError` "Unsupported operand types: array + int".
- What happened to 'abc' * 2 before PHP 8.0?It evaluated to `0` and raised an `E_WARNING` "A non-numeric value encountered", so the script carried on with a zero. PHP 8.0 turned that case into a `TypeError`, which stops execution unless caught, and moved the warning to leading-numeric strings such as `'5 boxes'`.
saying these in an interview costs you the question
- Says 'boxes' + 1 evaluates to 1 with a warning in PHP 8.
- Believes '5 boxes' + 1 throws a TypeError in PHP 8.
- Thinks null + 5 throws a TypeError because null is not a number.
- Claims ' 10 ' + 1 raises a warning because of the trailing space.
- Fixes the TypeError by casting with (int), hiding bad input as 0.