---
title: 'PHP 8.0 from PHP 7.4: JIT, Union Types, Match & Nullsafe — Upgrade Guide | DevSense'
description: "Découvrez PHP 8.0 après la version 7.4 : arguments nommés, expression match, opérateur nullsafe, attributs, compilateur JIT, types union—ce qui change, ce qu'il faut réécrire, et exemples exécutables."
faq:
    - { question: 'Quel est le principal avantage du compilateur JIT dans PHP 8.0 ?', answer: "Le compilateur JIT (Just-In-Time) traduit le bytecode en code machine au moment de l'exécution. Cela offre jusqu'à 500 % d'augmentation des performances pour les tâches lourdes calculées par le processeur (CPU-bound), bien que les applications web classiques y gagnent environ 1 à 5 %." }
    - { question: "En quoi l'expression match diffère-t-elle d'une instruction switch classique ?", answer: "Match utilise une comparaison d'identité stricte (===), retourne directement une valeur, n'a pas de comportement de cascade (fall-through, donc pas besoin de 'break') et lève une exception UnhandledMatchError si aucune valeur ne correspond et qu'aucun bloc default n'est fourni." }
    - { question: "Qu'est-ce que la promotion de propriétés de constructeur (Constructor Property Promotion) dans PHP 8.0 ?", answer: "Elle permet aux développeurs de déclarer et d'initialiser les propriétés de classe directement dans la liste des paramètres du constructeur, éliminant ainsi le besoin de déclarations et d'assignations séparées." }
    - { question: 'Comment les comparaisons lâches ont-elles changé dans PHP 8.0 ?', answer: 'Dans PHP 8.0, les comparaisons non strictes entre des nombres et des chaînes non numériques comparent ces éléments en tant que chaînes de caractères au lieu de convertir la chaîne en 0, évitant ainsi des bogues tels que ''0 == "foobar"'' retournant true.' }
published: '2026-05-31'
---
# PHP 8.0 : Fonctionnalités majeures

Depuis PHP 7.4 : notes basées sur les benchmarks concernant le JIT, les types et les changements non rétrocompatibles qui surviennent dans les applications réelles.

## Table des matières
* [Arguments nommés (Named Arguments)](#named-arguments)
* [Expression Match](#match-expression)
* [Opérateur Nullsafe (?->)](#nullsafe-operator)
* [Promotion de propriétés de constructeur](#constructor-promotion)
* [Évolution du système de types (Union, mixed, static)](#type-system)
* [Attributs / Annotations](#attributes)
* [Nouvelles fonctions de chaînes de caractères](#string-functions)
* [Weak Maps (Cartes faibles)](#weak-maps)
* [Expression Throw et gestion des erreurs](#throw-and-errors)
* [Compilateur JIT et performances](#jit-performance)
* [Changements dans les modules et le noyau](#modules-changes)
* [Ajustements syntaxiques (::class, virgules de fin)](#syntax-tweaks)
* [Comparaisons de chaînes à nombres plus saines](#saner-comparisons)
* [Changements non rétrocompatibles (notes de migration)](#backward-incompatible)
* [Erreurs courantes](#common-mistakes)
* [Quiz d'auto-évaluation](#self-test-quiz)

---

Mettre à niveau une version majeure de langage est toujours un pari risqué : vous voulez bénéficier des nouveaux gains de performance, mais vous redoutez les plantages de production nocturnes causés par de vieilles hypothèses de code patrimonial. PHP 8.0 n'est pas une simple mise à jour mineure—c'est une réinitialisation fondamentale de la plateforme. En introduisant le compilateur JIT, en convertissant les conversions de types lâches en comparaisons strictes et en transformant les avertissements silencieux en erreurs fatales qui interrompent les scripts, PHP 8.0 exige un changement total dans notre manière d'écrire et de migrer notre code. Voici une analyse pragmatique et éprouvée de ce qui change sous le capot et de ce qui va réellement casser dans votre application.

<a id="named-arguments"></a>
## Arguments nommés

Passer des arguments à une fonction par leur nom élimine le besoin de se rappeler leur ordre et permet d'ignorer les paramètres optionnels. Cela rend le code auto-documenté.

> [!NOTE]
> **Le saviez-vous ?**
> Depuis PHP 8.0, si vous utilisez des arguments nommés, renommer un paramètre dans une méthode de classe parente ou une interface est un changement non rétrocompatible ! Si une classe enfant ou un appelant utilise l'ancien nom du paramètre, PHP lèvera un `ArgumentCountError` ou une `Error`.

```php
// Avant : vous deviez spécifier tous les paramètres dans l'ordre
// app/Services/CookieService.php
setcookie('test', '', time() + 60 * 60 * 2, '/', '', false, true);

// PHP 8.0 : spécifiez uniquement ce qui est requis
// app/Services/CookieService.php
setcookie(
    name: 'test',
    expires: time() + 60 * 60 * 2,
    httponly: true
);
```

---

<a id="match-expression"></a>
## Expression Match

Une alternative plus stricte et plus concise à l'instruction switch. Elle retourne une valeur et utilise une comparaison stricte (===), éliminant ainsi les bogues inattendus liés au transtypage implicite.

```php
// app/Services/ResponseService.php
$statusCode = 200;

$statusMessage = match ($statusCode) {
    200, 300 => 'Succès ou Redirection',
    400, 404 => 'Erreur Client',
    500 => 'Erreur Serveur',
    default => 'Statut inconnu',
};
```

---

<a id="nullsafe-operator"></a>
## Opérateur Nullsafe (?->)

Permet de lire des propriétés et d'appeler des méthodes en chaîne. Si l'un des éléments vaut null, toute la chaîne renvoie null sans lever d'erreur fatale.

```php
// PHP 7.4
// app/Services/SessionService.php
$country = null;
if ($session !== null) {
    $user = $session->user;
    if ($user !== null) {
        $country = $user->getAddress()->country;
    }
}

// PHP 8.0
// app/Services/SessionService.php
$country = $session?->user?->getAddress()?->country;
```

---

<a id="constructor-promotion"></a>
## Promotion de propriétés de constructeur

Auparavant, la création de simples DTO (Data Transfer Objects) ou Value Objects nécessitait d'écrire beaucoup de code répétitif : déclarer les propriétés, les passer au constructeur et les affecter. PHP 8.0 combine ces trois étapes en une seule.

```php
// PHP 7.4 : Approche classique (et verbeuse)
// app/DTO/UserDTO.php
class UserDTO 
{
    public string $name;
    public string $email;
    protected int $age;

    public function __construct(string $name, string $email, int $age) 
    {
        $this->name = $name;
        $this->email = $email;
        $this->age = $age;
    }
}

// PHP 8.0 : Élégant et concis
// app/DTO/UserDTO.php
class UserDTO 
{
    public function __construct(
        public string $name,
        public string $email,
        protected int $age,
    ) {}
}
```

---

<a id="type-system"></a>
## Évolution du système de types

Dans PHP 8.0, le système de types est devenu nettement plus strict et plus expressif.

### Types Union
Avant PHP 8.0, si une variable pouvait accepter plusieurs types de données (par exemple, `int` ou `float`), nous devions nous appuyer sur des annotations PHPDoc. Maintenant, PHP prend cela en charge de manière native.

```php
// app/Services/CalculatorService.php
class Calculator 
{
    private int|float $number;

    public function setNumber(int|float $number): void 
    {
        $this->number = $number;
    }
}
```

### Le pseudo-type `mixed`

Le nouveau type `mixed` résout les problèmes du code hérité. Il équivaut à `array|bool|callable|int|float|null|object|resource|string`. Remarque : `mixed` inclut déjà null, donc écrire `?mixed` ou `mixed|null` n'est pas autorisé et déclenchera une erreur de compilation.

### Le type de retour `static`

Pour implémenter des motifs tels que le \"Late Static Binding\" (liaison statique tardive) et les interfaces fluides, le type de retour `static` a été ajouté (auparavant, seul `self` était disponible).

```php
// app/Factories/BaseFactory.php
class BaseFactory {
    public function create(): static {
        return new static();
    }
}
```

### L'interface `Stringable`

Si une classe implémente la méthode magique `__toString()`, PHP 8.0 lui attribue automatiquement (implicitement) l'interface `Stringable`. Cela permet d'utiliser `string|Stringable` dans les déclarations de type.

---

<a id="attributes"></a>
## Attributs / Annotations (Analyse approfondie)

Les **attributs** (introduits en PHP 8.0) sont des **métadonnées structurées** que vous associez à des classes, méthodes, propriétés, paramètres et constantes. Le moteur les stocke dans le bytecode ; vous les lisez grâce à la **Réflexion**. Ce ne sont **pas des éléments magiques** : rien ne se produit à moins que *votre* framework, votre routeur ou votre outil ne les inspecte—même idée que les annotations Java/C#, mais natives à PHP.

### Attributs vs PHPDoc

| | PHPDoc (`@route`, `@deprecated`) | Attributs (`#[Route]`) |
|---|----------------------------------|-------------------------|
| Analyse | Chaîne dans les commentaires ; nécessite des parseurs personnalisés | Syntaxe native ; pas d'expressions régulières sur les commentaires |
| Typage | Informel ; risque de dérive par rapport au code réel | Les arguments du constructeur sont de réelles valeurs PHP |
| Outils | Le support IDE varie | L'API de Réflexion est stable et rapide |

Utilisez PHPDoc pour la **documentation humaine** ; utilisez les attributs pour les **comportements que votre code va réellement exploiter** (routage, règles de validation, sérialisation, génération de code).

### Déclarer une classe d'attribut

N'importe quelle classe peut être utilisée comme un attribut si elle est marquée avec l'attribut intégré `#[\Attribute]`. Un masque binaire optionnel limite **où** elle peut apparaître (`Attribute::TARGET_*`). Omettre les cibles permet de l'appliquer par défaut à **toutes** les cibles.

```php
// app/Attributes/Route.php
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
final class Route
{
    public function __construct(
        public string $path,
        public array $methods = ['GET'],
    ) {}
}
```

* **`final`** est couramment utilisé pour éviter que la classe d'attribut ne soit héritée par accident.
* Les paramètres du constructeur deviennent les **arguments** passés lors de l'utilisation (`#[Route('/x', methods: [...])]`).

Vous pouvez également marquer un attribut comme **répétable** sur le même élément (par exemple, plusieurs validateurs) avec `Attribute::IS_REPEATABLE` dans le masque binaire.

### Appliquer des attributs (syntaxe)

Les attributs se placent **avant** la déclaration, un ou plusieurs par ligne, ou regroupés :

```php
// app/Http/Controllers/UserController.php
#[Route('/api/users', methods: ['GET', 'POST'])]
final class UserController {}

// app/Http/Controllers/ItemController.php
#[Route('/items')]
final class ItemController
{
    #[Route('/items/{id}', methods: ['GET'])]
    public function show(int $id): array
    {
        return ['id' => $id];
    }
}
```

Les arguments nommés (PHP 8.0) se lisent naturellement : `methods: ['GET']` correspond au paramètre du constructeur.

### Fonctionnement à l'exécution

1. PHP **analyse** les attributs à la compilation et les associe à la structure de réflexion.
2. À l'exécution, vous obtenez un objet `ReflectionClass`, `ReflectionMethod`, `ReflectionProperty`, etc.
3. Appelez **`getAttributes()`** pour obtenir des instances de `ReflectionAttribute`, puis **`newInstance()`** pour instancier votre classe d'attribut avec les arguments issus du code source.

Rien ne s'exécute seul : votre code de démarrage, votre conteneur d'injection de dépendances (DI) ou votre routeur doit appeler l'API de Réflexion.

### Lire des attributs par Réflexion

```php
// app/Services/RouterService.php
$reflection = new ReflectionClass(UserController::class);

// Uniquement les attributs d'une classe donnée (recommandé)
foreach ($reflection->getAttributes(Route::class) as $attr) {
    /** @var Route $route */
    $route = $attr->newInstance();
    // $route->path, $route->methods
}

// Tous les attributs de la classe (à filtrer vous-même)
foreach ($reflection->getAttributes() as $attr) {
    $name = $attr->getName();       // ex: Route::class
    $args = $attr->getArguments(); // arguments bruts du constructeur
}
```

Utilisez **`ReflectionMethod::getAttributes()`** pour les routes de méthodes ou middlewares, **`ReflectionParameter::getAttributes()`** pour la validation de paramètres, etc.

---

<a id="saner-comparisons"></a>
## Comparaisons de chaînes à nombres plus saines

C'est l'un des changements non rétrocompatibles les plus subtils. Auparavant, lors d'une comparaison lâche entre un nombre et une chaîne, PHP convertissait la chaîne en nombre. Dans PHP 8.0, si la chaîne n'est pas numérique, les nombres sont comparés en tant que chaînes.

```php
// app/Services/ComparisonService.php
// PHP 7.4
0 == 'foobar'; // true

// PHP 8.0
0 == 'foobar'; // false
```

---

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

### Utilisation de `::class` sur les objets

Vous pouvez désormais récupérer le nom de la classe directement depuis une variable d'objet en utilisant `::class`. Auparavant, la fonction `get_class()` était requise pour cela.

```php
// app/Services/ReflectionService.php
$object = new \App\Models\User();
// PHP 7: get_class($object);
echo $object::class; // Affiche "App\Models\User"
```

### Virgules de fin

Il est désormais autorisé de laisser une virgule de fin dans les listes de paramètres de fonctions, de méthodes et de fermetures (closures). Cela rend les commits Git plus propres.

```php
// app/Services/RequestService.php
public function makeRequest(
    string $url,
    array $data,
    array $headers, // <-- la virgule de fin est désormais légale
) { ... }
```

---

<a id="string-functions"></a>
## Nouvelles fonctions de chaînes de caractères

Au lieu d'utiliser `strpos()` et de vérifier que le résultat est `!== false`, trois nouvelles fonctions ont été ajoutées et renvoient un `bool` strict :

```php
// app/Services/StringService.php
$str = "DevSense est génial";

str_starts_with($str, "DevSense"); // true
str_contains($str, "génial");      // true
str_ends_with($str, "génial");     // true
```

### La fonction `get_debug_type()`

La nouvelle fonction `get_debug_type()` renvoie le type d'une variable de manière beaucoup plus lisible (par exemple, `App\Models\User` au lieu de simplement `object`, ou `int` au lieu de `integer`). Elle est idéale pour composer des messages d'erreur clairs.

---

<a id="weak-maps"></a>
## Weak Maps (Cartes faibles)

Une avancée architecturale majeure pour les `ORM` et le `caching`. Une `WeakMap` permet de créer des liaisons vers des objets d'une manière qui **n'empêche pas** le ramasse-miettes (Garbage Collector) de supprimer ces objets de la mémoire.

```php
// app/Services/CacheService.php
class Cache {
    private WeakMap $cache;
    
    public function __construct() {
        $this->cache = new WeakMap();
    }
    
    public function getMetadata(object $obj): array {
        if (!isset($this->cache[$obj])) {
            $this->cache[$obj] = $this->computeExpensiveData($obj);
        }
        return $this->cache[$obj];
    }
}
```

Dès que l'objet `$obj` est détruit ailleurs dans l'application, il disparaît automatiquement de la `WeakMap`, libérant instantanément la mémoire.

---

<a id="throw-and-errors"></a>
## Expression Throw et gestion des erreurs

L'opérateur `throw` passe du statut d'instruction (statement) à celui d'expression.

```php
// app/Services/UserService.php
// Vous pouvez désormais lever des exceptions directement dans les opérateurs ternaires ou les fusions de null (null coalescing)
$user = $request->get('user') ?? throw new InvalidArgumentException('User requis');

$callable = fn() => throw new Exception('Ceci ne devrait pas s'exécuter');
```

Changement de rigueur global : dans PHP 8.0, l'opérateur de suppression d'erreur `@` ne masque plus les erreurs fatales.
De plus, la plupart des avertissements du moteur (Engine Warnings) ont été convertis en exceptions d'erreur (par exemple, tenter d'accéder à une propriété d'une variable non-objet fait désormais planter le script au lieu de simplement consigner un Warning).

### Captures sans variables (non-capturing catches)

Si vous devez attraper une exception mais que vous n'avez pas besoin de l'objet d'exception lui-même, la variable peut désormais être omise dans le bloc `catch`.

```php
// app/Services/ErrorService.php
// Avant : nous devions déclarer la variable $e
try {
    // code
} catch (Exception $e) {
    Log::error('Une erreur est survenue');
}

// PHP 8.0 :
try {
    // code
} catch (Exception) {
    Log::error('Une erreur est survenue');
}
```

### Erreurs de types pour les fonctions internes

La plupart des fonctions internes de PHP lèvent désormais des exceptions strictes de type `TypeError` ou `ValueError` lorsque des paramètres non valides leur sont passés, au lieu d'émettre un Warning et de retourner null.

```php
// app/Services/DebugService.php
// PHP 7.4
strlen([]); // Warning: strlen() expects parameter 1 to be string, array given

// PHP 8.0
strlen([]); // TypeError: strlen(): Argument #1 ($str) must be of type string, array given
array_chunk([], -1); // ValueError: array_chunk(): Argument #2 ($length) must be greater than 0
```

---

<a id="jit-performance"></a>
## Compilateur JIT et performances

Le changement architectural le plus fondamental sous le capot de PHP 8.0 est l'introduction du **compilateur JIT (Just-In-Time)**.

**OPcache** (Avant PHP 8) : le code source était converti en codes d'opération (OpCodes) et la machine virtuelle Zend les exécutait ligne par ligne.

**JIT (PHP 8.0+)** : analyse les OpCodes et compile les chemins les plus fréquemment exécutés (\"hot paths\") directement en code machine natif pour le processeur (x86/ARM).

Pour les applications web classiques, principalement limitées par les entrées/sorties (I/O-bound), le gain de performance est d'environ 1 à 5 %. Mais pour les tâches de calcul intensives (CPU Bound), l'amélioration est de l'ordre de **300 % à 500 %**.

> [!NOTE]
> **Le saviez-vous ?**
> Le compilateur JIT de PHP 8.0 s'exécute en réalité au sein d'OPcache. Si vous activez `opcache.jit_buffer_size` mais oubliez de configurer `opcache.enable` ou `opcache.enable_cli`, le compilateur JIT restera complètement inactif sans aucun avertissement !

---

<a id="modules-changes"></a>
## Changements dans les modules et le noyau

Le nettoyage du noyau concernant les types de ressources opaques se poursuit, ces derniers étant remplacés par des objets :

* **cURL** : `curl_init()` renvoie désormais une instance de la classe `CurlHandle`.
* **GD** : `imagecreate()` renvoie une instance de `GdImage`.
* **Sockets** : `socket_create()` renvoie une instance de `Socket`.

Nettoyage de la mémoire : les nouveaux objets sont **automatiquement** détruits par le ramasse-miettes. Des fonctions comme `curl_close()` n'ont plus aucune utilité.

Autres modifications des extensions :
* **JSON** est désormais intégré de manière permanente au noyau (il ne peut plus être désactivé avec l'option `--disable-json`).
* **XML-RPC** a été déplacé du noyau vers **PECL**.
* Ajout de la fonction mathématique **`fdiv()`**, qui permet la division par zéro (renvoie `INF`, `-INF` ou `NAN` au lieu de lever une erreur).

---

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

Même si vous n'utilisez pas la nouvelle syntaxe, la mise à niveau vers PHP 8.0 peut casser des applications en raison de comportements d'exécution plus stricts et de suppressions de fonctionnalités historiques. Voici une liste compacte des points d'attention.

### Langage / mots-clés / suppressions

* `match` est désormais un **mot-clé réservé**.
* `mixed` est désormais un **mot réservé** (il ne peut plus être utilisé pour nommer des classes, interfaces ou traits, et est interdit dans les espaces de noms).
* `__autoload()` a été supprimée. Utilisez `spl_autoload_register()` à la place.
* `create_function()` a été supprimée. Utilisez des fonctions anonymes / fermetures.
* `each()` a été supprimée. Utilisez `foreach` ou `ArrayIterator`.
* Les définitions de constantes insensibles à la casse ont été supprimées (`define('FOO', 'bar', true)` n'est plus pris en charge).
* Les méthodes ayant le même nom que la classe ne sont plus traitées comme des constructeurs (utilisez `__construct()`).
* L'appel statique de méthodes non statiques n'est plus autorisé.
* Les transtypages `(real)` et `(unset)` ont été supprimés.

### Gestion des erreurs & diagnostics (exécution plus stricte)

* Les échecs d'assertions lèvent désormais une exception par défaut.
* La directive INI `track_errors` a été supprimée (ainsi `$php_errormsg` n'est plus disponible ; utilisez `error_get_last()`).
* L'opérateur `@` ne masque plus les erreurs fatales.
* Le niveau d'erreur par défaut est désormais `E_ALL`.
* De nombreux avertissements sont devenus des exceptions `Error` (par exemple : écrire dans une propriété d'une variable non-objet, types de clés de tableau ou d'offsets de chaîne non valides, déballage d'une variable non-tableau/non-Traversable).
* De nombreuses notifications (notices) sont devenues des avertissements (variables, propriétés ou clés de tableaux non définies, conversion de tableau en chaîne, etc.).

### Chaînes numériques & coercition de types

* Les comparaisons non strictes entre nombres et chaînes non numériques se comportent différemment (les nombres sont comparés en tant que chaînes).
* Les opérations arithmétiques ou binaires sur des chaînes non numériques lèvent désormais une `TypeError`.
* Le transtypage de flottant en chaîne est désormais indépendant de la configuration locale du système (locale-independent).

---

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

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

### 1. Expression Match sans Default
Une expression `match` doit être exhaustive. Si aucune des conditions ne correspond au sujet et qu'aucun bloc `default` n'est fourni, PHP lèvera une exception `UnhandledMatchError`.

```php
// app/Services/PaymentService.php
// MAUVAIS : Plantera si $status vaut 'failed'
$message = match ($status) {
    'pending' => 'En attente de paiement',
    'completed' => 'Payé avec succès',
};

// BON : Gérez toujours une option par défaut
$message = match ($status) {
    'pending' => 'En attente de paiement',
    'completed' => 'Payé avec succès',
    default => 'Paiement échoué ou annulé',
};
```

### 2. Effets de bord dans les chaînes d'opérateurs Nullsafe
L'opérateur nullsafe court-circuite toute la chaîne si un élément intermédiaire vaut `null`. Cependant, si vous tentez d'effectuer une affectation dans une chaîne nullsafe ou d'appeler une méthode ayant des effets de bord, celle-ci pourrait ne pas s'exécuter. De plus, l'opérateur nullsafe ne peut pas être utilisé sur le côté gauche d'une affectation.

```php
// app/Services/UserService.php
// MAUVAIS : Erreur de syntaxe - impossible d'écrire sur une chaîne nullsafe
$session?->user?->name = 'John'; 

// MAUVAIS : La méthode avec effets de bord peut ne pas s'exécuter si $logger est null
$logger?->log('Action occurred')?->sendNotification();
```

### 3. Position des arguments nommés
Bien que vous puissiez mélanger des arguments positionnels et nommés, les arguments nommés doivent toujours être placés **après** les arguments positionnels. Les mélanger dans le mauvais ordre entraîne une erreur de compilation.

```php
// app/Services/NotificationService.php
// MAUVAIS : Argument nommé placé avant un argument positionnel (Erreur de compilation)
sendEmail(subject: 'Hello', $userEmail);

// BON : Arguments nommés placés à la fin
sendEmail($userEmail, subject: 'Hello');
```

---

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

Testez votre compréhension des fonctionnalités de PHP 8.0.

### Question 1 : Quelle est la sortie du code suivant ?
```php
// app/Http/Controllers/TestController.php
$result = 0 == 'hello';
var_dump($result);
```
* A) `bool(true)`
* B) `bool(false)`
* C) `TypeError`

<details>
<summary>Afficher les réponses</summary>

**Réponse : B**  
Dans PHP 8.0, la comparaison non stricte (`==`) entre un nombre et une chaîne de caractères non numérique compare ces éléments en tant que chaînes. Ainsi, `0 == 'hello'` est évalué comme `'0' == 'hello'`, ce qui donne `false`. En PHP 7.4, cela aurait retourné `true`.
</details>

### Question 2 : Quelle exception est levée si une expression `match` n'a pas de branche correspondante et aucun bloc `default` ?
* A) `RuntimeException`
* B) `ValueError`
* C) `UnhandledMatchError`

<details>
<summary>Afficher les réponses</summary>

**Réponse : C**  
PHP 8.0 lève une exception `UnhandledMatchError` si aucune des branches de l'expression match ne correspond à la valeur d'entrée et qu'aucun bloc `default` n'est fourni.
</details>

---

## Résumé (Conclusion)

Considérez PHP 8.0 comme une **réinitialisation de la plateforme** : moins de surprises liées aux comparaisons lâches, des échecs plus clairs pour les API internes et un système de types qui s'aligne enfin sur la réalité des grandes bases de code PHP. Le résultat se traduit par moins de bogues exclusifs à la production et une transition plus fluide vers les versions 8.1+.