New PHP error messages in PHP 8.3New PHP errors messages in PHP 8.3

The upcoming PHP 8.3 is bringing some new errors messages. They originate from new features, deprecated ones, and extra checks on the source code. Let’s review the new PHP error messages in PHP 8.3 and get our code ready for November 2023.

Previous review

Evolution of error message counts

With distinct 783 error messages, PHP 8.3 is ranking number 4, bumping PHP 8.2 down one notch, and still far behind PHP 7.4 with its 824 distinct error messages. It is also more than PHP 8.2.

Number of PHP error messages in PHP versions

Usually, the distinct number of error messages grow within a PHP major version, as deprecations are accumulated until the next breaking change version: at that point, they are removed and the whole number drops significantly. We’ll see what happens with PHP 9.0.

New error messages

PHP 8.3 adds 38 new error messages, and removes 11 of them, for a net increase of 27.

 

 

Quick disclaimer : the error messages are read from PHP code source, which is in C. In the messages, the %s is the placeholder for strings, just like with sprintf() in PHP. They usually carry a name, which is dynamically filled by PHP.

Here are the most interesting new PHP error messages.

Calling get_class() without arguments is deprecated

Calling get_parent_class() without arguments is deprecated

Calling get_class() without arguments was only allowed when called from within a class. Then, it would use the current class as the default, and returns its name.

In PHP 8.3, get_class() and get_parent_class()’s arguments are now compulsory. It must be provided as argument. This means using $this when inside a class.

Another option is to call get_called_class(), which acts as get_class() without arguments. get_called_class() also works with static methods, a.k.a. methods without access to $this. There is no such thing as get_called_parent_class() or get_called_class_parent() (sic).

<?php
class A {
    public function foo() {
        var_dump(get_class());
        var_dump(get_called_class());
    }
}

(new A)->foo();
// PHP Deprecated:  Calling get_class() without arguments is deprecated

?>

This is a changed behavior, with deprecation notice. It will be finalized in PHP 9.

Cannot use %s as value for class constant %s::%s of type %s

This error message is linked to the typing of class constants. In PHP 8.3, class constants may be typed. When this happens, the value of the constant must be of the assigned type.

Of course, there are a wide range of possible error messages, depending on types.

<?php
class x { 
    const int A = true;
    //Cannot use bool as value for class constant x::A of type int
}

?>

This is a linted error.

RFC

Class constant %s::%s cannot have type %s

Another aspect of class constant typing is that not all types are possible.

Obviously, common types like int, float, array, bool, true, false, null, object, any class, and any valid combinaison of those with union, intersection or DNF.

<?php

class x {
    // typing is a bit superfluous, yet valid 
    const int|string|bool A = true;
    
    // a constant with different types
    // it will depend on configuration
    const ?string B = PHP_MAJOR_VERSION >= 8 ? 'OK' : null;
}

?>

On the other hand, some specific types are not possible: never, void, callable. The two first are quite obvious, while callable is forbidden to avoid confusing situations with actual method calls.

<?php

class x {
    const never GIVE = 'you up';
}

?>

This is a linted error.

Cannot use the %s modifier on a %s

Here, modifiers are options for class elements, such as abstract, static, public, final, etc. The class elements are properties, methods and class constants.

When such elements get the wrong modifier, PHP used to emit a syntax error. Although this was simple to understand, the new error messages are now a lot more expressive.

<?php

class Test {
  public function __construct(public static $x) {}
  //Cannot use the static modifier on a promoted property
}

?>

This is a lint error.

Cannot use the abstract modifier on an anonymous class

Cannot use the final modifier on an abstract method

Cannot use the final modifier on an anonymous class

Those are specific errors, related to anonymous classes. Anonynous classes can’t be abstract as they won’t be derived. They also cannot hold abstract methods for the same reason.

Anonymous classes are always final by default, as they can’t be derived. Even though, this message is an actual upgrade from a previous syntax error.

<?php

$x = new final class() {};

?>

This is a linted error.

Increment on non-alphanumeric string is deprecated

One of the open secrets of PHP is using the increment operator on strings. It actually increments strings to its next ASCII value, or it adds one to its value.

<?php

$x = 'a';
$x++;
echo $x; // b

$z = 'z';
$z++;
echo $z; // aa

?>

Knowing that, the error message above looks like the death warrant of this feature. In fact, the reality is quite subtle. For starter, the above examples are still valid.

Then, PHP still goes a long way to keep the feature working, by detecting numbers or valid incrementable strings, before reporting an error.

Here is a selection of cases, sourced from the unit tests of PHP 8.3. It includes integers, floats, negative numbers, several types of strings:

--- testing: '0' ---
int(1)
--- testing: '65' ---
int(66)
--- testing: '-44' ---
int(-43)
--- testing: '1.2' ---
float(2.2)
--- testing: '-7.7' ---
float(-6.7)
--- testing: 'abc' ---
string(3) "abd"
--- testing: '123abc' ---
string(6) "123abd"
--- testing: '123e5' ---
float(12300001)
--- testing: '123e5xyz' ---
string(8) "123e5xza"
--- testing: ' 123abc' ---

Deprecated: Increment on non-alphanumeric string is deprecated in %s on line %d
string(7) " 123abd"

Most of the previous behaviors have been preserved. The last one, which generates the error, has a string which is not obvious to convert, because of the initial space. Multi bytes strings might also have the same behavior.

To prepare for the future, such feature should be replaced by the dedicated str_increment() and str_decrement() functions, which handle these situations. They will stay compatible with future versions.

There are more details about the evolution of this feature in the RFC below.

RFC

This is an execution error.

Decrement on empty string is deprecated as non-numeric

Decrement on non-numeric string has no effect and is deprecated

Decrement on type bool has no effect, this will change in the next major version of PHP

Decrement on type null has no effect, this will change in the next major version of PHP

Specific error messages are introduced for the decrement operator. It already had no effect on strings nor boolean, and now, it is mentionned in the errors.

Increment on type bool has no effect, this will change in the next major version of PHP

If you have ever tried to apply the increment or decrement operator on boolean values, you may have noticed that it doesn’t have any effect.

Well, PHP 8.3 introduce a specific warning, that tells it to anyone who tries. The behavior is unchanged since PHP 7.0, but now, it is clearly stated.

<?php

$a = true;
$a++;
var_dump($a);

$b = true;
$b--;
var_dump($b);

?>

This is an execution error.

Duplicate declaration of static variable $%s

The variable used in the use clause of a closure represents a variable which is coming from the creating context. On the other hand, the closure may also declare a static variable (not a property), with the same name. That leads to confusion for the human and the developer alike.

Now, this is reported at linting time.

<?php

$a = null;

function () use (&$a) {
    static $a;
};

?>

This is a lint error.

Unknown errors

Here are some errors which are in the source code of PHP, but we could not reproduce. If you have experience, you can share it with us.

  • Maximum call stack size of %zu bytes reached during compilation. Try splitting expression
  • Throwing resource destructor called
  • cannot cancel the query: %s.

Get ready for PHP 8.3

Hopefully, this quick tour in the new error messages of PHP 8.3 will help preparing the code for its own migration to PHP 8.3.

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.3 ruleset, with rules dedicated to PHP 8.3. They can run on PHP 8.2, so as to be ready for November 2023.

Happy auditing.