PHP 8.0: Internal function warnings now throw TypeError
and ValueError
exceptions
In PHP 8, internal function parameters have types and value validations enforced, and will throw \TypeError
or \ValueError
exceptions if the expected type or value is not allowed.
Prior to PHP 8, this resulted in a PHP warning.
Not all PHP warnings emitted by internal functions are transformed to exceptions, but majority of the functions will throw \TypeError
or \ValueError
exceptions if the provided type is not allowed, or the provided value is invalid. This includes functions that accept multiple types (such as a string or an array) because PHP 8 comes with Union Types.
Out of all PHP 8 changes, this will likely be the biggest pain-point when you upgrade existing code.
Reasons behind this decision
Many of the PHP internal functions gracefully handle unexpected values by raising a PHP warning, but still returning a "false-ish" value such as null
, false
, or 0
. This can lead to subtle bugs that are later discovered, if discovered at all, in different parts of the program.
For example, json_decode()
function accepts a $depth
parameter that must be a positive integer. This was not enforced with an exception prior to PHP 8. If you call json_decode()
with an invalid $depth
, json_decode()
function will raise a warning, but still return null
, which is an acceptable return type of the original json-encoded value is also null
.
With types and values enforced, json_decode()
function throws an exception when it encounters an unexpected type or a value.
This can result in applications that dismissed the warning prior to PHP 8 to fail due to the unexpected exception. However, this results in fewer bugs once fixed because PHP makes sure to defend aggressively against invalid values.
\TypeError
Examples
Warning to Exception
Most of the PHP internal functions that accept a typed parameter now throw \TypeError
exceptions instead of warnings. This can eliminate a lot of subtle bugs because most of these string functions return either false
or null
on such unexpected types, which can result in a bug somewhere else.
substr('foo', []);
PHP versions prior to 8 will raise a warning and return null
instead of throwing a \TypeError
and refusing to go forward. The type is now enforced and throws exceptions in PHP 8.
- Warning: substr() expects parameter 2 to be int, array given in ... on line ...
+ Fatal error: Uncaught TypeError: substr(): Argument #2 ($start) must be of type int, array given in ...:...
New \TypeError
without prior warnings
Some functions, such as method_exists()
did not throw exceptions on unexpected values, but returned a value that fulfills the semantic return values of the function.
Union types are used when they are deemed necessary. For example, method_exists()
function accepts either a class name (string
) or an object (object
). This is enforced as a Union Type of string|object
.
method_exists([], 'getName');
This will now throw a \TypeError
. Prior to PHP 8, it returned false
if the provided parameter is not a string or an object, but did not raise any warnings.
+ Fatal error: Uncaught TypeError: method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given in ...:...
\ValueError
examples
PHP throws \ValueError
exceptions if the provided value is of correct type, but not acceptable in the context.
json_decode('"foo"', true, -1);
Prior to PHP 8, setting a depth less than 0 resulted in a warning, which is now promoted to a \ValueError
exception:
- Warning: json_decode(): Depth must be greater than zero in ... on line ...
+ Fatal error: Uncaught ValueError: json_decode(): Argument #3 ($depth) must be greater than 0 in ...:...
Many SPL functions, mbstring_
functions, password_*
functions, etc now throw \ValueError
exceptions when the provided values cannot be used to go further.
Backwards compatibility impact
Except for certain cases such as method_exists
, the \TypeError
and \ValueError
exceptions PHP 8 and later throws resulted in a warning prior to PHP 8.
Unless you went out of your way to silent (e.g @strlen([])
) the warning, or dismissed in from the error log, you should not encounter any major problems.
Except for the few exceptional cases, all the changes you make because of the new \TypeError
and \ValueError
exceptions will work all the same in prior PHP versions as well.
The new \ValueError
exception class can be polyfilled, but that does not mean internal functions will throw exceptions instead of raising warnings.
class ValueError extends Error {}
It is possible to have a try/catch
block that accepts \ValueError
exceptions without having the \ValueError
exception class declared. It will not be thrown in prior versions, which makes the try/catch
block a harmless no-op in older PHP versions.
try {}
catch (\ValueError $exception) {}