New PHP error messages in PHP 8.4
The upcoming PHP 8.4 is bringing new errors messages, and, also, removing some of them. The new messages originate from new features, deprecated ones, removed ones, and extra checks throughout the source code. Let’s review the new PHP error messages in PHP 8.4 and get our code ready for November 2024.
The big PHP error database
This post is build on top of the PHP error message database, which collect the PHP error messages, along with more context information, version information and possible solutions. If you like reading error messages, this is a great link. It is used to build a number of Exakat static analysis rules.
Evolution of error message counts
With distinct 1919 error messages, PHP 8.4 is ranking number 1 most verbose PHP version ever! Although, the trend is quite steady from the last versions, and we can bet that the next PHP 8.5 will have even more error messages.
Note that this graph is very different from last year, which counted 783 errors in PHP 8.3: this one is now counting 1731. This is due to an upgrade in the counting application, which is covering new calls to error functions. The teachings are still the same, though.
New error messages
PHP 8.4 adds 311 new error messages, and removes 123 of them, for a net increase of 188. A good number of the error messages are checking for incoming values in parameters, and look like must be a bitmask of IMAP_GC_TEXTS, IMAP_GC_ELT, and IMAP_GC_ENV
, or Cannot clone unconstructed MessageFormatter
. We will set them aside, for the sake of avoiding repetitive remarks.
%s(): Implicitly marking parameter $%s as nullable is deprecated, the explicit nullable type
A nullable parameter accepts null
as value and, at least, another type. It is visible with the ?
or null
in the type specification of the parameter.
<?php function foo(?string $s) : void { var_dump($s); } function bar(string $s = null) : void { var_dump($s); } ?>
Yet, it is also possible to define a null
default value, and not make the type nullable, by simply using = null
in the parameter definition. Here, the nullable is not explicitly part of the type, but the method does accept it nonetheless.
Implicit nullable is not possible with properties, or return values. They yield errors at compile or execution time, respectively.
Since PHP 8.4, the implicit nullable parameters are reported by PHP, as a deprecated feature. They will disappear in PHP 9, or in a future version. It is time to make them explicit, or clean up the type specification.
This has chances to be the most popular PHP 8.4 error message.
Cannot unset hooked property %s::$%s
Hooked properties are a new features of PHP 8.4 : a property may have a get
and/or a set
hook, defined with the property, that serve as accessors.
Normal non-static properties, as in without a hook, may be unset at any time. They are reset to the uninitialized state, and should be be used except for writing or checking with isset
.
On the other hand, hooked properties never can be removed.
<?php class x { public int $p = 3; public int $q = 5 { get => $this->q; } } $x = new x; unset($x->p); //Cannot unset hooked property x::$q unset($x->q); //Typed property x::$p must not be accessed before initialization print $x->p; ?>
=> Cannot unset hooked property %s::$%s
Must not use parent::$%s::%s() outside a property hook
Property hooks are defining methods, at the property level. These methods behave like other methods, and should be able to access the parent method, when the visibility is not private. This means that the child class may use a parent’s property hook, and doesn’t have to redefine them every time.
This is also an example of using the ::
famous operator twice in a row: one to access the parent property, and the second to reach its hook.
Now, that syntax is not allowed outside a property hook, even within the same class: no other method shall call it, or, another property.
<?php class x { public string $q { get => 'in parent'; } } class y extends x { public string $q { get => parent::$q::get(); } function foo(): string { // Must not use parent::$q::get() outside a property hook return parent::$q::get(); } } $y = new y; echo $y->q; ?>
=> Must not use parent::$%s::%s() outside a property hook
Interfaces may only include hooked properties
This error message replaces the previous Interfaces may not include properties
. Until PHP 8.4, interfaces could not define properties: only methods and constants.
Now, with hooked properties being properties tied to methods (get
and set
), it is possible to put these hooks in an interface, without a body.
<?php interface x { public string $q { get; } public string $r } ?>
Since it is always possible to implement the obvious get => $this->q
property hook, it is now trivial to include properties in PHP interfaces.
=> Interfaces may only include hooked properties
contains CR character that is not allowed in the header
There is a family of characters that are now reported when used in a mail header. CR (carriage return), LF (line feed), CRLF (both previous ones), and NULL (aka, \0
). They are linked with possible hack that introduces extra email addresses or even full mail headers in the message.
<?php // example from ./ext/standard/tests/mail/gh13415.phpt try { mail('to@example.com', 'Test Subject', 'A Message', ['Reply-To' => "foo@example.com \nCc: hacker@example.com"]); } catch (Throwable $e) { echo $e->getMessage()."\n\n"; } ?>
Basically, if certain mail headers are not sanitized enough, PHP has your back.
From the tests, I found Reply-to
and custom foo
headers are reported.
Power of base 0 and negative exponent is deprecated
It is now explicitly forbidden to take a negative exponent over 0. A negative exponent of $x
is the same a taking the positive exponent of 1/$x
: the latter makes no mathematical sense.
<?php //Deprecated: Power of base 0 and negative exponent is deprecated echo 0 ** -1; //Uncaught DivisionByZeroError: Division by zero echo 1 / 0; ?>
It used to make PHP sense, and be INF
, unlike the division by zero, which emitted a saner DivisionByZeroError
.
=> Power of base 0 and negative exponent is deprecated
A never-returning function must not return
Using the never
return type means that the function will end the script, with die
, or throw
an exception. In both cases, the return
clause would not be reached, and it is actually wrong to include one in such a method.
In fact, even is the return
command is unreachable, PHP will complain about it.
<?php function foo() : never { if (0) { return 1; } die('the end'); } ?>
%s(): never-returning function must not implicitly return
The previous error message is emitted at compile time: with an explicit return
command, it is easy to spot a potential problem. Yet, PHP doesn’t need the return
command: when omitted, it will simply return null
.
When the return is implicit, the error will happen at execution time.
<?php // all OK function foo() : never {} // error in the previous definition foo(); ?>
=> A never-returning function must not return
A function with return type must return a value
Finally, with returned types, PHP now reports attempts at returning nothing, while typing the method. return
with no explicit value means return null;
.
Although, return
and return null
do not return the same null
: the second definition below also yields the error: the null
value must be explicit.
<?php // emits the error function foo() : int { return ; } // also emits the error function foo() : null { return ; } // no error at linting time // emits the error upon execution function foo() : int { return 'a'; } ?>
Finally, a typed method, with a returned literal value, is only checked at execution time. Hence, the last code compiles, but will fail at execution time.
=> A never-returning function must not return
Array and string offset access syntax with curly braces is no longer supported
This error message is actually removed from PHP. So, believe it or not, PHP 8.4 is totally dropping the support of curly braces on arrays and strings!
<?php $array = ['a', 'b', 'c']; echo $array{2}; // echo c $string = 'ABC'; echo $string[1]; // echo B ?>
Until PHP 8.3, it was a deprecated feature, and in PHP 8.4, it is now a generic syntax error
. No need to wait for PHP 9.0!
=> Array and string offset access syntax with curly braces is no longer supported
Unknown errors
Here are some errors which are in the source code of PHP, but we could not reproduce. They are often niche error, deep inside a PHP extension, and are lacking context to be well understood. In fact, they are just fun to read, out of context.
Not yet implemented
individual body cannot be empty
Unknown and uncaught modification type.
CPU doesn't support SSE2
Cannot use variable $%s twice
- Actually, this happens when the same variable is used twice (or more) in the use expression of a closure
Previous error message reviews
- New error messages in PHP 8.3
- New error messages in PHP 8.2
- New error messages in PHP 8.1
- Common PHP 8.0 Error Messages
Get ready for PHP 8.4
Hopefully, this quick tour in the new error messages of PHP 8.4 will help preparing the code for its own migration to PHP 8.4.
Since some of the checks are only available at execution time, it is worth running static analysis on it to spot potential errors. Exakat include the Compatibility PHP 8.4 ruleset, with rules dedicated to PHP 8.4. They can run on PHP 8.3, so as to be ready for November 2024.
Happy auditing.