The blog

What's new in PHP 8.4?

Published on November 18, 2024

The official release of PHP 8.4 is scheduled for this week, 21 November. A series of pre-release versions (Alpha, Beta and Release Candidate) have been released in advance, allowing the community to test new features and make last-minute adjustments. PHP 8.4 introduces a wide range of improvements, including new functions for manipulating arrays, property hooks inspired by other languages, and simplified syntax. Let's take a look at what's new in this version.

 PHP 8.4" class="wp-image-10358#

Property Hooks

The version 8.4 includes a significant new feature: property hooks. The implementation in PHP is based on existing solutions from other languages, such as Kotlin, C#, Swift, JavaScript, and Python. We will now examine a few use cases to better understand how property hooks can benefit your projects and provide an overview of their functionality.

With PHP 7.4:

<?php

class Foo {
	private string $id;

	public function getId(): string {
		return $this->id;
	}

	public function setId(string $id): void {
		$this->id =$id;
	}
}

Starting with PHP 8, this can be simplified with Constructor Property Promotion: 

<?php

class Foo {
	function __construct(public string $id) {}
}

In cases where it is necessary to retain getter and setter functions, for instance in rich models containing business logic, the benefit of a lighter syntax is lost. This issue has now been resolved with the release of PHP 8.4, which introduces Property Hooks.

<?php

class Foo {
	function __construct(
		public string $id {
			get {
				return '#' . $this->id;
			}
			set(string $id){
				$this->id = mb_strtoupper($id);
			}
		},
	) {}
}

The syntax may seem confusing at first, but like all syntactical developments, you will get used to it.

Another capability introduced by property hooks is the ability to define interfaces on properties. In the past, we were forced to write two methods, get and set, even with anemic models, to fulfill the contract defined by the interface. With this new version of PHP, we can write the following definitions:

<?php

interface HasId {
    public string $id { get; set; }
}

// Arrow functions can also be used to shorten syntax
class Foo implements HasId {
    function __construct(
        public string $id {
            get => '#' . $this->id;
            set (string $id) => $this->id = mb_strtoupper($id);
        },
    ) {}
}

// The interface contract is also respected without the use of Property Hooks by publicly declaring the property
class Bar implements HasId {
    function __construct(public string $id) {}
}
#

Defining the visibility of a class property with asymmetric visibility

Asymmetric visibility allows you to define different visibility for the same property, depending on whether the operation in question has read or write access to the property. It is therefore possible to define public visibility for read access and more restricted visibility (protected or private) for write access. The following two classes are equivalent, the syntax is more concise with the introduction of asymmetric visibility.

<?php

class Foo {
	function __construct(
	    private string $id
    ) {}
    
    public function getId(): string {
    	return $this->id;
    }
}

class Bar {
	function __construct(
	    public private(set) string $id,
    ) {}
}

Asymmetric visibility comes with few rules that are fairly easy to understand:

  • Properties must be typed (a limitation of the PHP implementation).
  • Only object properties are affected, static properties cannot benefit (also a limitation of the PHP implementation).
  • For object-type properties, and if the property has been defined as private(set), the linked object can only be modified in the scope of the current class. 
  • However, the properties of the linked object can be modified if they are defined as such.
  • For array-type properties and if the property has been defined as private(set), the array cannot be manipulated (adding an element, deleting an element, etc.) outside the scope of the current class. 
  • The visibility of the set cannot be greater than the get.
  • In the case of class inheritance or interface contracts, the visibility cannot be more restrictive and can be more extensive. 

The difference between a property defined in readonly (introduced in PHP 8.1) and a property defined in public private(set) is fairly minor but worth mentioning. Asymmetric visibility as defined above has the same effect, except that it allows internal mutation. In other words, readonly restricts mutation and also has the effect of a write-only on instantiation. Although they operate independently of Property Hooks, the two mechanisms can be combined. An example of both functionalities is available here: https://3v4l.org/5IECI/rfc#vgit.master

#

Native lazy objects

Lazy objects are objects whose actual instantiation is postponed until they are actually needed (because they are generally expensive to instantiate). They are used extensively in Doctrine and Symfony for performance reasons.

In his theoretical definition, Martin Fowler gives four possible implementations. Two of these are implementations that do not require any changes to existing objects: Ghost and Proxy. These are the ones that are retained and accessible by adding methods to PHP's Reflection API.

In both cases, an initialisation function is created. For Ghosts, this function acts directly on the object. For Proxy, it is the function that instantiates the lazy object, and the interactions are then passed back to the real instance. In both cases, the instantiation mechanism is triggered by accessing the state of the real object: reading or writing a property, testing whether a property has a value, cloning, etc. This behavior can be disabled on specific properties using specific functions and in certain defined or configurable cases, such as for debugging or serialization.

As this behavior is reserved for very limited use cases, and is by definition quite abstract, we invite you to read the RFC to find out more.

#

new without parentheses

More anecdotally, this change simplifies the syntax when instantiating a new object by eliminating the need for the surrounding parentheses.

<?php
// Before PHP 8.4 and still valid
$o = (new Operation(0))->add(10)->multiply(2);
// Since PHP 8.4
$o = new Operation(0)->add(10)->multiply(2);
#

HTML5 parser

A new DOM\HTMLDocument class has been created to enable HTML5 parsing, but to ensure backwards compatibility with HTML4, the current class has been retained and remains unchanged. Both classes retain the same API and can be used in the same way. Only the construction logic has changed and requires the use of one of the available factors. The underlying library is Lexbor, written in C.

#

New array functions

Four new array functions have been added to complement the existing ones.

  • array_find returns the first match of the callback function passed to it.
<?php
$array = ['A', 'AA', 'AAA'];
$arrayWithKeys = ['A' => 1, 'AA' => 2, 'AAA' => 3];

array_find($array, static fn(string $value): bool => strlen($value) > 2); // returns AAA
array_find($array, static fn(string $value): bool => strlen($value) > 3); // returns null
array_find($arrayWithKeys, static fn(int $value, string $key): bool => $value === strlen($key)); // returns 1
  • array_find_key works in the same way as the previous function, but returns the key instead of the value.
<?php
$array = ['A', 'AA', 'AAA'];
$arrayWithKeys = ['A' => 1, 'AA' => 2, 'AAA' => 3];

array_find_key($array, static fn(string $value): bool => strlen($value) > 2); // returns 2
array_find_key($array, static fn(string $value): bool => strlen($value) > 3); // returns null
array_find_key($arrayWithKeys, static fn(int $value, string $key): bool => $value === strlen($key)); // returns A
  • array_any returns boolean true if at least one element of the array matches the callback function.
<?php
$array = ['A', 'AA', 'AAA'];
$arrayWithKeys = ['A' => 1, 'AA' => 2, 'AAA' => 3];

array_any($array, static fn(string $value): bool => strlen($value) > 2)); // returns true
array_any($arrayWithKeys, static fn(int $value, string $key): bool => $value === strlen($key)); // returns true
  • array_all returns boolean true if all elements of the array match the callback function.
<?php
$array = ['A', 'AA', 'AAA'];
$arrayWithKeys = ['A' => 1, 'AA' => 2, 'AAA' => 3];

array_all($array, static fn(string $value): bool => strlen($value) < 4)); // returns true
array_all($arrayWithKeys, static fn(int $value, string $key): bool => $value === strlen($key)); // returns true
#

Enhancements and bug fixes

Among other things, the following changes have been made:

  • New multibyte functions for string manipulation (mb_trim, mb_ltrim, mb_rtrim, mb_ucfirst, mb_lcfirst).
  • New functions for creating DateTime objects from timestamps.
  • The exit and die language elements lose their status in favor of special functions. Without breaking backwards compatibility, this allows functions to work in a similar way, with more precise typing of input parameters, for example.
  • The following extensions leave the core to join PECL: Pspel, IMAP, OCI8 and PDO-OCI.
  • Four new rounding modes for the round function.
  • Addition of two new encryption algorithms in Sodium and OpenSSL version upgrade.

The full changelog will be available on the PHP web site, as with every new release: https://www.php.net/ChangeLog-8.php

#

Time to migrate!

Thanks to these new features and enhancements, you have all the keys you need to migrate your projects to the latest version of PHP. In addition to sharing our PHP expertise through our regular publications and conferences, did you know that we also offer PHP training courses? Contact our training department to learn more about our catalog and the various financing options available!

The blog

Go further