Le blog

Ce qu’il faut retenir des nouveautés de PHP 8.4

Publié le 15 novembre 2024

La sortie officielle de PHP 8.4 est prévue pour la semaine prochaine, le 21 novembre 2024. Avant ce lancement, une série de pré-versions (alpha, bêta, et release candidates) ont permis à la communauté de tester les nouvelles fonctionnalités et d’apporter des ajustements de dernière minute. PHP 8.4 introduit de multiples améliorations, notamment des nouvelles fonctions pour manipuler les tableaux, des hooks de propriété inspirés d'autres langages, ou encore une syntaxe simplifiée. Passons ensemble en revue les nouveautés à retenir de cette version. 

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

Définition des Property Hooks

Les Property Hooks sont l’une des fonctionnalités majeures introduites dans la version 8.4. L'implémentation en PHP s'inspire de celle existante déjà dans d'autres langages comme Kotlin, C#, Swift, Javascript ou Python. Prenons quelques exemples pour illustrer à quoi servent les Property Hooks et comment les utiliser :

<?php

class Foo {
	private string $id;

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

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

Depuis PHP 8, on pouvait simplifier grâce à la Constructor Property Promotion en : 

<?php

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

Dans le cas où il faudrait conserver les accesseurs (getter/setter), on pense aux modèles riches contenant de la logique métier, nous perdons l’avantage d’une syntaxe plus légère. C’est chose réparée avec la version 8.4 de PHP et l’introduction des Property Hooks :

<?php

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

Au premier abord, la syntaxe peut paraître déroutante mais comme toute évolution syntaxique, on s’habitue avec le temps.

Une autre possibilité introduite par les Property Hooks est de pouvoir définir des interfaces sur des propriétés. Aujourd’hui nous étions contraints de devoir écrire deux méthodes get et set même avec des modèles anémiques pour remplir le contrat défini par l’interface.

Avec cette nouvelle version de PHP, nous pouvons écrire les définitions suivantes :

<?php

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

// Les fonction fléchées peuvent être aussi utilisées pour raccourcir la syntaxe
class Foo implements HasId {
    function __construct(
        public string $id {
            get => '#' . $this->id;
            set (string $id) => $this->id = mb_strtoupper($id);
        },
    ) {}
}

// Le contrat d’interface est également respecté sans l’utilisation des Property Hooks en déclarant publiquement la propriété
class Bar implements HasId {
    function __construct(public string $id) {}
}

Retrouvez un exemple complet sur cette page.

#

Définir la visibilité d'une propriété de classe avec la visibilité asymétrique

La visibilité asymétrique permet de définir, sur une même propriété, une visibilité différente selon que l’opération concernée accède en lecture ou en écriture à la propriété.  Il devient alors possible de définir une visibilité publique sur l’accès en lecture et une visibilité plus restreinte (protégée ou privée) sur l’accès en écriture. Les deux classes suivantes sont équivalentes, la syntaxe est plus succincte avec l’introduction de la visibilité asymétrique.

<?php

class Foo {
	function __construct(
	    private string $id,
	) {}

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

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

La visibilité asymétrique vient avec quelques règles assez naturelles à appréhender :

  • Les propriétés doivent impérativement être typées (contrainte issue de l’implémentation en PHP).
  • Seules les propriétés d’objet sont concernées, les propriétés statiques ne peuvent pas en bénéficier (contrainte également issue de l’implémentation en PHP).
  • Pour les propriétés typées object et si la propriété a été définie en private(set), l’objet lié ne pourra pas être modifié dans un scope différent de celui de la classe courante. Par contre, les propriétés de l’objet lié peuvent être modifiées si elles sont définies comme telles.
  • Pour les propriétés types en array et si la propriété a été définie en private(set), le tableau ne peut pas être manipulé (ajout d’un élément, suppression d’un élément…) en dehors du scope de la classe courante. 
  • La visibilité du set ne peut pas être plus étendue que celle du get.
  • En cas d’héritage de classe ou de contrats d’interface, la visibilité ne peut pas être plus restrictive et elle peut être plus extensive. 

La différence entre une propriété définie en readonly (introduit en PHP 8.1) et une propriété en public private(set) est assez minime mais mérite d’être mentionnée. La visibilité asymétrique telle que définie précédemment va avoir le même effet, sauf qu’elle va permettre la mutation interne. Autrement dit, le readonly restreint la mutation et a aussi l’effet d’une écriture unique lors de l’instanciation.

Bien que fonctionnant indépendamment des Property Hooks, les deux mécanismes peuvent être combinés. Un exemple reprenant les deux fonctionnalités est disponible ici.

#

Support natif pour les objets paresseux (lazy objects)

Les objets paresseux sont des objets dont l’instanciation effective va être reportée jusqu’au moment d’en avoir réellement besoin (parce que leur instanciation est généralement coûteuse). Ils sont utilisés massivement dans Doctrine et Symfony pour des raisons de performance.

Martin Fowler établit dans sa définition théorique quatre implémentations possibles. Deux d’entre elles sont des implémentations qui ne nécessitent pas de modifier les objets déjà existants : Ghost et Proxy. Ce sont celles qui sont retenues et accessibles à travers l’ajout de méthodes dans l’API de Reflection de PHP.

Dans les deux cas, une fonction d’initialisation est créée. Pour les Ghosts, la fonction va agir directement sur l’objet. Pour Proxy, c’est la fonction qui instancie l’objet paresseux et les interactions sont ensuite remontées à l’instance réelle. Dans les deux cas, le mécanisme d’instanciation est déclenchée par l’accès à l’état de l’objet réel : lire ou écrire une propriété, tester si une propriété à une valeur, cloner… Ce comportement peut être désactivé sur des propriétés particulières grâce à des fonctions spécifiques et dans certains cas définis ou paramétrables, comme pour le débogage ou la sérialisation.

Étant réservé à des cas d’usages très limités et par définition assez abstraits, nous vous invitons à lire la RFC pour découvrir des exemples de code et le fonctionnement détaillé des deux différentes implémentations. 

#

new sans parenthèses

Un peu plus anecdotique, cette évolution vient alléger la syntaxe lors de l’instanciation d’un nouvel objet en rendant superflu les parenthèses autour.

<?php
// Avant et toujours valide
$o = (new Operation(0))->add(10)->multiply(2);
// Depuis PHP 8.4
$o = new Operation(0)->add(10)->multiply(2);
#

Parser HTML5 

Une nouvelle classe DOM\HTMLDocument a été créée pour permettre de parser du HTML5, afin de garantir la rétrocompatibilité avec l’HTML4, la classe actuelle est conservée et demeure inchangée. Les deux classes conservent la même API et peuvent donc être utilisées de la même façon. Seule la logique de construction a été changée et requiert d’utiliser l’une des factories à disposition. La librairie sous-jacente écrite en C est Lexbor.

#

Nouvelles fonctions de tableau

Quatre nouvelles fonctions agissant sur les tableaux ont été ajoutées, elles viennent compléter les fonctions existantes.

  • array_find va renvoyer la première correspondance de la fonction de rappel qui lui est passée :
<?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 va avoir le même fonctionnement que la fonction précédente mais renvoyer la clé plutôt que la valeur :
<?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 va renvoyer un booléen à true si au moins un élément du tableau correspond à la fonction de rappel :
<?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 va renvoyer un booléen à true si tous les éléments du tableau correspondent à la fonction de rappel :
<?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
#

Améliorations et corrections de bugs

Entre autre choses, nous avons retenu les modifications suivantes :

  • L’ajout de nouvelles fonctions multibytes pour manipuler les chaînes de caractères (mb_trim, mb_ltrim, mb_rtrim, mb_ucfirst, mb_lcfirst).
  • L’ajout de nouvelles fonctions pour créer des objets DateTime à partir d’un timestamp.
  • Les éléments de langage exit et die perdent leur statut en faveur de fonctions spéciales. Sans rupture de compatibilité descendante, cela permet d’avoir un fonctionnement similaire aux fonctions, un typage plus précis des paramètres d’entrée, par exemple.
  • Les extensions suivantes sortent du core pour rejoindre PECL : Pspel, IMAP, OCI8 et PDO-OCI.
  • Ajout de quatre nouveaux mode d’arrondis pour la fonction round
  • Ajout de deux nouveaux algorithmes de chiffrage dans Sodium et montée de version d’OpenSSL.
  • Ajout de  nouvelles options pour cURL.

Le changelog complet sera disponible sur le site de PHP comme à chaque sortie de version.

#

Place à la migration 

Grâce à ces nouvelles fonctionnalités et améliorations, vous disposez de toutes les clés pour migrer vos projets vers la dernière version de PHP. En plus de partager notre expertise PHP à travers nos publications et conférences régulières, saviez-vous que nous proposons également des formations autour de ce langage ? Contactez notre pôle formation pour découvrir notre catalogue et les différentes modalités de financement possibles !

Julien Lary

Julien Lary

Lead developer

Mots-clésPHP

Le blog

Pour aller plus loin