PHP casts almost never fail. (int) null gives you 0, and (int) "123aze" gives you 123 while quietly dropping the rest, which can hide bugs. This RFC from Alexandre Daubois and Nicolas Grekas adds two stricter kinds of cast:
(?type)letsnullpass through asnull, and validates and converts any other value.(!type)throws aTypeErrorif the value isnull, and validates and converts any other value.
Both throw a TypeError on malformed input like "123aze".
Show me
Here is a trimmed version of the RFC's own comparison:
$value = null; (int) $value; // 0, may hide a bug (?int) $value; // null (!int) $value; // TypeError $value = "123aze"; (int) $value; // 123, no error (!int) $value; // TypeError (?int) $value; // TypeError $value = "123"; (int) $value; // 123 (!int) $value; // 123 (?int) $value; // 123
A database column that might be NULL is a good fit:
$age = (?int) $row['age']; // null stays null, "25" becomes 25
How the checks work
The RFC doesn't invent new rules. It reuses the coercion rules PHP already applies to typed function parameters when strict_types is off, so (!int) $x behaves like passing $x to a function that takes an int.
A few details:
- The new casts use those same rules even when
strict_types=1is on. - Losing precision on a float throws, so
(!int) 78.9throws while(!int) 5.0gives5. - For
int,floatandbool, any object throws. - For
string, objects with__toString()work and others throw. arrayandobjectcasts still convert like normal casts. Only thenullhandling changes.
It covers all six cast types: int, float, string, bool, array and object.
What it means for existing code
Normal casts don't change. (?type) is a parse error today, so no code uses it. (!type) is valid today, where it reads as "not" applied to a constant named int or string. The RFC says a constant named after a type seems very unlikely.
Where it stands
The RFC is under discussion. It was published on October 24, 2025, and targets PHP 8.6. The vote dates haven't been set yet, and it will need a two-thirds majority to pass.