---
title: 'PHP 8.1: Enums, Fibers, Readonly & Intersection Types — PHP Upgrade Guide | DevSense'
description: 'PHP 8.1 après la version 8.0 : enums, propriétés readonly, fibers, types intersection/never, first-class callables—plus les règles sur $GLOBALS, les exceptions MySQLi et les dépréciations.'
faq:
    - { question: "Qu'est-ce que les Enums dans PHP 8.1 et comment sont-ils utilisés ?", answer: "Les Enums sont un moyen sécurisé d'un point de vue type pour définir un ensemble fermé de valeurs possibles. Les backed enums associent des cas à des valeurs de type string ou int pour la sérialisation, tandis que les pure enums se basent uniquement sur l'identité et représentent des états abstraits." }
    - { question: 'Quelles sont les règles pour les propriétés Readonly dans PHP 8.1 ?', answer: "Les propriétés readonly doivent être typées, ne peuvent pas avoir de valeur par défaut et ne peuvent être initialisées qu'une seule fois (généralement dans le constructeur). Les tentatives ultérieures de modification lèveront une erreur fatale." }
    - { question: 'Comment fonctionne la syntaxe first-class callable dans PHP 8.1 ?', answer: "Elle permet de créer une Closure à partir de n'importe quelle fonction ou méthode appelable à l'aide de trois points `...` (par exemple `strlen(...)`), en remplaçant la syntaxe plus verbeuse `Closure::fromCallable()`." }
    - { question: 'À quoi sert le type de retour never ?', answer: "Le type `never` indique qu'une fonction ne retournera jamais de valeur. Cela signifie qu'elle doit soit appeler `exit()`, soit lever une exception, soit entrer dans une boucle infinie, facilitant ainsi l'analyse statique." }
published: '2026-05-31'
---
# PHP 8.1 : Fonctionnalités majeures

Chemin de mise à niveau depuis PHP 8.0.x : ajouts au langage, resserrement de l'exécution et listes de contrôle pour les environnements de staging.

## Table des matières
* [Enums (Énumérations)](#enums)
* [Propriétés Readonly](#readonly-properties)
* [Syntaxe First-class callable](#first-class-callable)
* [Fibers](#fibers)
* [Types d'intersection](#intersection-types)
* [Type de retour never](#never-type)
* [new dans les initialisations](#new-in-initializers)
* [Ajustements syntaxiques & langage (0o, déballage de tableaux, arguments nommés après déballage)](#syntax-tweaks)
* [Changements non rétrocompatibles (notes de migration)](#backward-incompatible)
* [Dépréciations (ce qu'il faut corriger tôt)](#deprecations)
* [Extensions : changements notables](#extensions-changes)
* [Erreurs courantes](#common-mistakes)
* [Quiz d'auto-évaluation](#self-test-quiz)

---

Alors que PHP 8.0 a constitué une refonte majeure du moteur d'exécution, PHP 8.1 se concentre sur le peaufinage de la sémantique du langage et la fourniture de constructions syntaxiques modernes. En introduisant des Enums natifs, des propriétés en lecture seule (readonly) et des types d'intersection directement dans le système de types, PHP 8.1 permet aux développeurs d'écrire un code déclaratif et sécurisé au niveau des types avec beaucoup moins de code répétitif. Cependant, cette version resserre également les règles d'exécution—en restreignant les opérations d'écriture sur `$GLOBALS`, en forçant MySQLi à lever des exceptions par défaut et en marquant des dizaines de comportements historiques comme dépréciés. Voici un guide éprouvé pour comprendre ces fonctionnalités et mener à bien votre mise à niveau.

<a id="enums"></a>
## Enums (Énumérations)

Les Enums apportent une méthode native et sécurisée pour représenter un ensemble fermé de valeurs.

* **Backed enums** stockent une valeur scalaire (`string|int`) qui peut être facilement sérialisée/transmise.
* **Pure enums** sont basés uniquement sur l'identité, idéaux pour les machines d'état du domaine et les traitements exhaustifs.

> [!NOTE]
> **Le saviez-vous ?**
> Les Enums sont implémentés en interne sous forme de classes ! Vous pouvez implémenter des interfaces, définir des méthodes, écrire des méthodes statiques et utiliser des traits (avec la restriction que les traits ne peuvent pas contenir de propriétés) sur vos Enums de la même manière que pour des classes classiques.

```php
// app/Enums/Status.php
enum Status: string
{
    case Draft = 'draft';
    case Published = 'published';
    case Archived = 'archived';
}

// app/Services/PostService.php
function canEdit(Status $status): bool
{
    return $status !== Status::Archived;
}

$status = Status::from('draft');  // Status::Draft
$value  = $status->value;         // 'draft'
```

### Conseils pratiques

* Utilisez les enums à la place des drapeaux typés sous forme de chaînes (`'draft'|'published'`) dans les signatures de méthodes et les DTO.
* Privilégiez les backed enums pour la persistance et les API ; préférez les pure enums pour les états internes des flux de travail.
* Combinez-les avec `match` pour un traitement exhaustif :

```php
// app/Services/PostService.php
function label(Status $status): string
{
    return match ($status) {
        Status::Draft => 'Draft',
        Status::Published => 'Published',
        Status::Archived => 'Archived',
    };
}
```

---

<a id="readonly-properties"></a>
## Propriétés Readonly

Les propriétés en lecture seule (readonly) ne peuvent être assignées **qu'une seule fois**, généralement dans le constructeur. Il s'agit d'une victoire majeure pour les DTO immuables, les objets de valeur (value objects) et la sécurité des modèles de domaine.

> [!NOTE]
> **Le saviez-vous ?**
> Une propriété readonly ne peut pas avoir de valeur par défaut ! Si vous tentez d'écrire `public readonly string $status = 'draft';`, PHP déclenchera une erreur fatale de compilation.

```php
// app/DTO/UserProfile.php
final class UserProfile
{
    public function __construct(
        public readonly int $id,
        public readonly string $email,
    ) {}
}
```

### Ce que cela change sur le plan architectural

* Encourage les objets construits une fois pour toutes puis non modifiés.
* Réduit le besoin de propriétés privées associées à des getters pour de nombreuses structures de type DTO.
* S'intègre parfaitement avec la promotion de propriétés dans le constructeur pour des types immuables concis.

---

<a id="first-class-callable"></a>
## Syntaxe First-class callable

PHP 8.1 ajoute un moyen pratique de créer des fermetures (closures) à partir de n'importe quel élément appelable (callable) :

```php
// app/Helpers/StringHelper.php
function normalize(string $s): string
{
    return strtolower(trim($s));
}

// app/Services/DataService.php
$fn = normalize(...);               // Closure
$out = array_map($fn, [' A ', 'B ']); // ['a', 'b']
```

Ceci équivaut à `Closure::fromCallable('normalize')`, mais est plus court et plus lisible dans les pipelines de données.

---

<a id="fibers"></a>
## Fibers

Les Fibers sont des primitives de bas niveau pour la **concurrence coopérative**. Elles ne rendent pas magiquement votre code asynchrone, mais elles permettent aux frameworks et aux bibliothèques de construire des runtimes asynchrones, des planificateurs et de la concurrence structurée.

```php
// app/Services/FiberService.php
$fiber = new Fiber(function (): void {
    $value = Fiber::suspend('mis en pause');
    echo "repris avec : {$value}\n";
});

echo $fiber->start() . "\n"; // mis en pause
$fiber->resume('payload');   // repris avec : payload
```

### Quand s'y intéresser

* Si vous utilisez un framework asynchrone ou une boucle d'événements (ReactPHP, Amp) : les Fibers simplifieront grandement l'intégration.
* Pour la plupart des applications classiques en requêtes/réponses : les Fibers constituent principalement de l'infrastructure pour les bibliothèques.

---

<a id="intersection-types"></a>
## Types d'intersection

Les types d'intersection expriment le fait qu'une valeur doit satisfaire **toutes** les interfaces/classes indiquées.

```php
// app/Services/ExporterService.php
function export(iterable&Countable $items): array
{
    if (count($items) === 0) {
        return [];
    }

    return iterator_to_array($items, preserve_keys: true);
}
```

Remarque : les types d'intersection ne peuvent pas être combinés avec des types d'union dans PHP 8.1 (les types DNF ne seront introduits que plus tard, dans PHP 8.2).

---

<a id="never-type"></a>
## Type de retour never

Le type de retour `never` indique qu'une fonction **ne retourne pas** de valeur : elle lève toujours une exception ou interrompt l'exécution.

```php
// app/Helpers/ErrorHelper.php
function fail(string $message): never
{
    throw new RuntimeException($message);
}
```

Cela améliore l'analyse statique et rend le flux de contrôle explicite.

---

<a id="new-in-initializers"></a>
## new dans les initialisations

PHP 8.1 autorise l'utilisation de `new` dans plus d'endroits : valeurs par défaut des paramètres, variables statiques, initialisations de constantes et arguments d'attributs.

```php
// app/Services/Clock.php
final class Clock
{
    public function now(): DateTimeImmutable
    {
        return new DateTimeImmutable('now');
    }
}

// app/Services/HandlerService.php
function handler(Clock $clock = new Clock()): DateTimeImmutable
{
    return $clock->now();
}
```

Cela élimine de nombreux codes répétitifs liés aux usines (factories) dans les cas simples. (Soyez attentif à la durée de vie des services si vous utilisez un conteneur DI.)

---

<a id="syntax-tweaks"></a>
## Ajustements syntaxiques & langage

### Préfixe de littéral entier octal : `0o` / `0O`

```php
// app/Config/FileConfig.php
$mask = 0o755;
```

### Déballage de tableaux avec clés sous forme de chaînes de caractères

```php
// app/Services/ArrayService.php
$a = [1, 'a' => 'b'];
$b = [...$a, 'c' => 'd']; // [1, 'a' => 'b', 'c' => 'd']
```

### Argument nommé après déballage d'arguments

```php
// app/Services/ArgumentService.php
foo(...$args, named: $arg);
```

### `full_path` pour les téléversements de fichiers

Les entrées de `$_FILES` incluent désormais une clé `full_path` (destinée aux téléversements via `webkitdirectory`).

---

<a id="backward-incompatible"></a>
## Changements non rétrocompatibles (notes de migration)

Cette section est pragmatique : elle se concentre sur les problèmes qui cassent fréquemment les applications lors de la mise à niveau.

### Noyau PHP / langage

* **Restrictions d'écriture sur $GLOBALS** : l'écriture sur l'ensemble du tableau `$GLOBALS` n'est plus prise en charge (par exemple, `array_pop($GLOBALS)` provoque des erreurs). L'accès aux éléments individuels comme `$GLOBALS['x']` fonctionne toujours.
* **Variables statiques dans les méthodes héritées** : les méthodes héritées (non surchargées) partagent désormais leurs variables statiques avec la méthode parente.
* **Paramètres optionnels avant les requis** : les paramètres optionnels déclarés avant les paramètres requis sont traités comme requis ; dans PHP 8.1, cela peut désormais lever une `ArgumentCountError` au moment de l'appel (même avec des arguments nommés).
* **Compatibilité des types de retour avec les classes internes** : surcharger des méthodes internes sans types de retour compatibles déclenche des dépréciations ; utilisez `#[ReturnTypeWillChange]` au besoin pour assurer le support multi-version.
* **Nouveaux mots-clés** :
  * `readonly` est désormais un mot-clé (toujours autorisé comme nom de fonction).
  * `never` est désormais un mot réservé (ne peut plus être utilisé pour nommer des classes, interfaces ou traits, ni dans les espaces de noms).

### Migration des ressources vers des objets

Plusieurs extensions ont migré leurs ressources vers des objets (mettez à jour vos vérifications `is_resource()` vers des vérifications de type ou des valeurs booléennes) :

* **FileInfo** : utilise des objets `finfo` à la place des ressources.
* **FTP** : utilise des objets `FTP\Connection`.
* **IMAP** : utilise des objets `IMAP\Connection`.
* **LDAP** : utilise des objets `LDAP\Connection`, `LDAP\Result`, `LDAP\ResultEntry`.
* **PgSQL** : utilise des objets `PgSql\Connection`, `PgSql\Result`, `PgSql\Lob`.
* **PSpell** : utilise des objets `PSpell\Dictionary` et `PSpell\Config`.

### Modifications du comportement de PDO / MySQLi

* **MySQLi** : le mode de rapport d'erreur par défaut est passé de \"silencieux\" à \"exceptions\". Pour restaurer l'ancien comportement : `mysqli_report(MYSQLI_REPORT_OFF);`
* **PDO** : le comportement de `PDO::ATTR_STRINGIFY_FETCHES` a changé pour les booléens ; les pilotes SQLite/MySQL retournent désormais les entiers/flottants sous forme de types PHP natifs de façon plus cohérente.

### Modifications de comportement de la bibliothèque standard

* `version_compare()` n'accepte plus d'abréviations d'opérateurs non documentées.
* Les fonctions d'échappement HTML utilisent désormais par défaut `ENT_QUOTES | ENT_SUBSTITUTE` (les guillemets sont échappés ; l'UTF-8 mal formé est remplacé).

---

<a id="deprecations"></a>
## Dépréciations (ce qu'il faut corriger tôt)

Les dépréciations dans 8.1 sont les erreurs de demain. Les corriger tôt facilite la transition future vers PHP 8.2+.

### Dépréciations du noyau

* Passer `null` à des paramètres non nullables de fonctions internes est déprécié.
* Les conversions implicites de flottants en entiers avec perte de précision sont dépréciées (clés de tableaux, déclarations de types, opérateurs d'entiers).
* Appeler un élément statique sur un trait est déprécié.
* L'autovivification à partir de `false` est dépréciée (`$x = false; $x[] = 1;`).

### Dépréciations notables d'extensions

* **Date** : `strftime()` / `gmstrftime()` et `strptime()` sont dépréciées (privilégiez `date()` / `IntlDateFormatter`).
* **Filter** : `FILTER_SANITIZE_STRING` et `FILTER_SANITIZE_STRIPPED` sont dépréciées.
* **Hash** : les fonctions `mhash*()` sont dépréciées (utilisez `hash_*()`).
* **PDO** : `PDO::FETCH_SERIALIZE` est déprécié.

---

<a id="common-mistakes"></a>
## ⚠️ Erreurs courantes

Voici quelques pièges courants lors de l'utilisation des fonctionnalités de PHP 8.1 :

### 1. Modifier des propriétés Readonly en interne ou dans des sous-classes
Une propriété `readonly` ne peut pas être modifiée, même depuis l'intérieur de la classe ou par une sous-classe, une fois qu'elle a été initialisée.

```php
// app/DTO/UserProfile.php
// MAUVAIS : Modifier une propriété readonly après son initialisation lève une Error
class UserProfile {
    public function __construct(public readonly string $email) {}

    public function updateEmail(string $newEmail): void {
        $this->email = $newEmail; // Fatal Error: Cannot modify readonly property
    }
}
```

### 2. Autovivification à partir de False
Dans PHP 8.1, tenter de créer automatiquement un tableau à partir d'une valeur booléenne égale à `false` est déprécié.

```php
// app/Services/ArrayService.php
// MAUVAIS : Déclenche un avertissement de dépréciation en PHP 8.1 (et une erreur fatale en PHP 8.2)
$data = false;
$data[] = 'value';

// BON : Initialisez d'abord un tableau vide
$data = [];
$data[] = 'value';
```

### 3. Réaffecter le tableau $GLOBALS
Vous pouvez modifier les variables à l'intérieur de `$GLOBALS`, mais vous ne pouvez pas réaffecter ou détruire la variable globale `$GLOBALS` elle-même.

```php
// app/Services/LegacyService.php
// MAUVAIS : Erreur fatale en PHP 8.1
$GLOBALS = []; 

// MAUVAIS : Lier à une autre référence est illégal
$GLOBALS = &$someOtherArray;
```

---

<a id="self-test-quiz"></a>
## Quiz d'auto-évaluation

Testez votre compréhension des nouveautés de PHP 8.1.

### Question 1 : Laquelle des affirmations suivantes est vraie au sujet des propriétés Readonly en PHP 8.1 ?
* A) Elles peuvent être réassignées tant que la nouvelle valeur a le même type.
* B) Elles ne peuvent être déclarées que sur des propriétés typées (pas de propriétés non typées).
* C) On peut leur affecter une valeur par défaut dans la définition de la classe.

<details>
<summary>Afficher la réponse</summary>

**Réponse : B**  
Dans PHP 8.1, `readonly` ne peut être appliqué qu'aux propriétés typées. Déclarer une propriété en lecture seule sans type est une erreur de syntaxe. De plus, elles ne peuvent pas avoir de valeurs par défaut et ne peuvent jamais être réassignées après leur initialisation.
</details>

### Question 2 : Que se passe-t-il si vous essayez de construire automatiquement un tableau à partir d'une variable qui vaut actuellement `false` (ex: `$x = false; $x[] = 1;`) en PHP 8.1 ?
* A) Cela fonctionne sans problème, en convertissant la variable en tableau.
* B) Cela déclenche un avertissement de dépréciation.
* C) Cela lève immédiatement une erreur fatale (Fatal Error).

<details>
<summary>Afficher la réponse</summary>

**Réponse : B**  
Dans PHP 8.1, l'autovivification à partir de `false` est dépréciée et déclenche un avertissement. En PHP 8.2, cet avertissement est transformé en erreur fatale (Fatal Error).
</details>

---

## Résumé

Alors que PHP 8.0 a constitué une réinitialisation technique, PHP 8.1 en affine la sémantique et apporte des constructions modernes. Grâce aux Enums, aux propriétés Readonly et aux types d'intersection intégrés au système de types, vous écrivez un code plus propre, plus sécurisé au niveau des types, et avec beaucoup moins de code verbeux.