What's new in PHP 9

What's new in PHP 9 banner

PHP 9 will be the next major release of the PHP language.

The release date is still unknown, but we have hints about what's new. By looking into the PHP Wiki RFC section, we can get clues about what is coming next.

In this article, we'll cover the approved/implemented RFC (Request for Comments) and keep it updated with the latest information about PHP 9.

When is a major release necessary?

A new major release is needed whenever breaking changes in the core libraries are introduced. PHP uses the SemVer versioning scheme. 

SemVer stands for "Semantic Versioning," a versioning scheme used primarily in software development to communicate information about the changes and compatibility of a software product over time.

The purpose of SemVer is to make it easier for developers and users to understand how different versions of a software library, framework, or application relate to each other in terms of compatibility and functionality.

SemVer follows a three-part version number format: MAJOR.MINOR.PATCH. Each part carries specific information:

  1. MAJOR version: This is incremented when significant changes might break backward compatibility. It indicates substantial changes in the software, and users may need to adapt their code or configurations to accommodate these changes.

  2. MINOR version: This is incremented when new features are added to the software in a backward-compatible manner. It indicates that new functionality has been introduced but without breaking existing features.

  3. PATCH version: This is incremented when backward-compatible bug fixes or patches are applied to the software. It indicates that issues have been addressed without introducing new features or breaking existing functionality.

What is coming in PHP 9?

I went through the PHP Wiki and checked in which RFC's PHP 9 is mentioned. These are the most notable changes coming in PHP 9. Note that nothing is confirmed yet, but it's highly likely that most of these changes will be introduced in PHP 9.

Throw exception for dynamic properties [rfc]

When writing to a non-declared property of an object, as of now, PHP will silently create a dynamic property. This is rarely done intentionally, which is why, in PHP 9, an exception will be thrown unless the class explicitly allows dynamic properties. stdClass and __get/__set are not affected by this change.

Let's look at an example:

class Car {
    public $model;
}
 
$car = new Car;
 
// Assigns declared property Car::$model.
$car->model = "BMW";
 
$car->year = 2005;
// PHP <= 8.1: Silently creates dynamic $car->year property.
// PHP    8.2: Raises deprecation warning, still creates dynamic property.
// PHP    9.0: Throws Error exception.

Explicitly allow dynamic properties

#[AllowDynamicProperties]
class Foo {}
$foo = new Foo;
 
// No deprecation warning and no exception is thrown.
$foo->bar = 1;

Fatal error when overriding an internal method with an incompatible return type [rfc]

A fatal error will be thrown when overriding internal methods with an incompatible return type.

PHP 8.x raises a deprecation notice

class MyDateTime extends DateTime
{
    public function modify(string $modifier) { return false; }
}
 
// Deprecated: Declaration of MyDateTime::modify(string $modifier) should be
// compatible with DateTime::modify(string $modifier): DateTime|false

PHP 9 will trigger a Fatal error

class MyDateTime extends DateTime
{
    public function modify(string $modifier) { return false; }
}
 
// Fatal error: Declaration of MyDateTime::modify(string $modifier) must be
// compatible with DateTime::modify(string $modifier): DateTime|false

Phase out overloaded function signatures [rfc]

What is method overloading?

Method overloading is multiple versions of the same method with the same name but different parameter types.

 Example:

class Foo {
    function sum(int $a, int $b): int
    {
        echo "int version was called\n";
        return $a + $b;
    }
    
    function sum(float $a, float $b): float
    {
        echo "float version was called\n";
        return $a + $b;
    }
}

$foo = new Foo();
$foo->sum(1, 2); // echos 'int version was called' 
$foo->sum(1.1, 2.2); // echos 'float version was called'

Most of the modern languages like Java or C# support method overloading.

Due to the significant performance hit it may have, method overloading in PHP is not going to be implemented soon, if ever.

The implemented proposal is to phase out the overloaded function signatures over the course of PHP 9.x or PHP 10.x.

array_keys()

array_keys() currently supports two signatures:
  • function array_keys(array $array): array {} - returns all keys of $array
  • function array_keys(array $array, mixed $filter_value, bool $strict = false): array {} - returns the keys for items whose value matches the $filter_value criteria.

The RFC proposes to add a new function to replace the 2nd signature with the $filter_value criteria.

function array_keys(array $array): array {}
function array_keys_filter(array $array, mixed $filter_value, bool $strict = false): array {}

DatePeriod::__construct()

Currently DatePeriod::__construct() supports three signatures.
class DatePeriod
{
    ...
 
    public function __construct(DateTimeInterface $start, DateInterval $interval, int $recurrences, int $options = 0) {}

    public function __construct(DateTimeInterface $start, DateInterval $interval, DateTimeInterface $end, int $options = 0) {}

    public function __construct(string $isostr, int $options = 0) {}
 
    ...
}

The RFC proposes to add a new static factory method to replace the third signature.

class DatePeriod
{
    ...
 
    public function __construct(DateTimeInterface $start, DateInterval $interval, DateTimeInterface|int $end, int $options = 0) {}
 
    public static function createFromISO8601String(string $specification, int $options = 0): static {}
 
    ...
}

Additional methods

For a full list of methods, please visit the RFC.

Remove ${} string interpolation [rfc]

PHP allows embedding variables in strings with double quotes (") in a couple of ways

  1. Directly embedding variables (“$foo”)
  2. Braces outside the variable (“{$foo}”)
  3. Braces after the dollar sign (“${foo}”)
  4. Variable variables (“${expr}”, equivalent to (string) ${expr})

In PHP 9, options 3 and 4 will be removed.

Remove autovivification on false [rfc]

PHP allows autovivification - auto-creation of arrays from an undefined variable, null or false.

Example of autovivification:

// From undefined
$arr[] = 'some value';
$arr['doesNotExist'][] = 2;
// From false
$arr = false;
$arr[] = 2;
// From null
$arr = null;
$arr[] = 2;

In PHP 9, autovivification from false (second example) will throw a fatal error. 

Throw an Error exception upon accessing an undefined variable [rfc]

Accessing an undefined variable currently emits a warning Warning: Undefined variable $varname.

In PHP 9, accessing an undefined variable will throw an error.

$car = 'BMW';
echo 'My new car is: ' . $caar;
// Exception will be thrown, because $caar is typo and it doesn't exist.

Throw an error if a constructor or destructor returns a value [rfc]

Currently, constructors and destructors can return values, but these magic methods are supposed to be void, according to the documentation.

In PHP 9, a fatal error will be generated if a constructor or destructor returns a value.

class Test {
	public function __construct() {
		// this is illegal
		return 0;
	}
 
	// this is also illegal
	public function __destruct(): mixed {}
}
 
class Test2 {
	// this is legal (secondary vote)
	public function __construct(): void {}
 
	// this is also legal
	public function __destruct() {}
}

Remove utf8_encode and utf8_decode [rfc]

These functions currently convert strings encoded in ISO-8859-1 (“Latin 1”) to and from UTF-8, respectively. Since these two functions are widely misused, in PHP 9 they will be removed.

Throw an error when passing null to non-nullable arguments of internal functions [rfc]

Internal functions defined by PHP or PHP extensions silently accept null values for non-nullable arguments. This is inconsistent with the user-defined functions.

Example:

var_dump(str_contains("foobar", null));
// bool(true)

function my_str_contains(string $haystack, string $needle): bool {
    ...
}
var_dump(my_str_contains("foobar", null));
// TypeError: my_str_contains(): Argument #2 ($needle) must be of type string, null given

In PHP 9, a TypeError will be thrown for internal functions, consistent with the behavior of user-defined functions.

Remove Serializable interface [rfc]

In PHP 7.4 new __serialize() and __unserialize() magic methods were introduced to replace the broken Serializable Interface. Check this rfc for more information on why the Serializable interface is considered broken.

In PHP 9, the Serializable interface will be removed.

Use exceptions by default in SQLite3 extension [rfc]

The SQLite3 extension currently uses PHP warnings by default. PDO is using exceptions.

In PHP 9, SQLite3 will use exceptions by default.

Conclusion

These are some of the most notable changes expected in PHP 9.

As a major release, a lot of the deprecations will become exceptions and fatal errors. This process is necessary to keep the codebase consistent and clean.

I'm sure that new exciting features will come as well. I'll keep an eye on each new change and update this post to reflect it.