---
title: 'PHP 7.3 from 7.2: Flexible Heredoc, JSON Exceptions, PCRE2 & Migration | DevSense'
description: 'Guide de mise à niveau PHP 7.3 : syntaxe flexible heredoc/nowdoc, virgules de fin dans les appels, déstructuration par référence, is_countable, array_key_first/last, JsonException, Argon2id, PCRE2—et changements non rétrocompatibles.'
faq:
    - { question: "Qu'est-ce que la syntaxe flexible heredoc/nowdoc en PHP 7.3 ?", answer: "PHP 7.3 permet d'indenter la balise de fermeture d'un heredoc ou d'un nowdoc. L'indentation de cette balise définit la quantité d'indentation retirée de toutes les lignes à l'intérieur du corps du texte." }
    - { question: 'Comment fonctionne la classe JsonException en PHP 7.3 ?', answer: "En passant l'option 'JSON_THROW_ON_ERROR' à 'json_encode()' ou 'json_decode()', PHP lèvera une exception 'JsonException' si l'opération échoue, au lieu de nécessiter des vérifications manuelles avec 'json_last_error()'." }
    - { question: "Qu'est-ce qui change avec l'instruction 'continue' dans un 'switch' ?", answer: "L'utilisation de 'continue' à l'intérieur d'une instruction 'switch' émet désormais un avertissement (warning) car elle se comporte de manière identique à 'break'. Pour continuer une boucle externe, il faut utiliser 'continue 2' à la place." }
    - { question: "Quelles fonctions d'aide ont été introduites en PHP 7.3 ?", answer: "PHP 7.3 a ajouté 'is_countable()' pour vérifier si une variable peut être dénombrée, ainsi que 'array_key_first()' et 'array_key_last()' pour récupérer la première et la dernière clé d'un tableau." }
published: '2026-05-31'
---
# PHP 7.3 : Fonctionnalités majeures

Avez-vous déjà passé des heures à déboguer un échec silencieux d'analyse JSON en production parce que vous aviez oublié de vérifier `json_last_error()` ? PHP 7.3 résout ce problème d'expérience développeur (DX) en introduisant `JsonException` via l'option `JSON_THROW_ON_ERROR`. Cette version représente une amélioration majeure en matière de sécurité et de confort, peaufinant l'ergonomie de la syntaxe avec les heredocs indentés, les virgules de fin dans les appels, et en ajoutant des fonctions d'aide essentielles à la bibliothèque standard.

## Table des matières
* [Heredoc et Nowdoc flexibles](#flexible-heredoc)
* [Virgules de fin dans les appels de fonctions et de méthodes](#trailing-commas-calls)
* [Assignations par référence dans `list()` et `[]`](#list-references)
* [`instanceof` avec des littéraux](#instanceof-literals)
* [`CompileError`](#compile-error)
* [Fonctions d'aide du noyau nouvelles et mises à jour](#core-helpers)
* [JSON : `JSON_THROW_ON_ERROR` et `JsonException`](#json-exceptions)
* [Hachage de mot de passe : Argon2id](#argon2id)
* [Extensions notables & stdlib](#extensions-stdlib)
* [Recettes pratiques](#practical-recipes)
* [Erreurs courantes](#common-mistakes)
* [Limitations](#limitations)
* [Changements non rétrocompatibles (notes de migration)](#backward-incompatible)
* [Dépréciations (à corriger tôt)](#deprecations)
* [Autres modifications & opérations (compilation, INI, perf)](#other-changes)
* [Quiz interactif](#interactive-quiz)
* [Réflexions finales](#closing-thoughts)

---

<a id="flexible-heredoc"></a>
## Heredoc et Nowdoc flexibles

L'identifiant de fermeture **n'est plus obligé** de se situer à la colonne 0 dans l'ancienne forme rigide : il peut être **indenté**, et cette même indentation est **retirée de chaque ligne** du corps du texte. Cela rend le code SQL, HTML ou les textes d'aide CLI intégrés beaucoup plus lisibles dans un code respectueux de PSR-12.

**Mise en garde pour la migration :** si le corps du texte **contient le même jeton** (token) que l'identifiant de fermeture (désormais potentiellement indenté), PHP peut l'interpréter comme la **fin précoce de la chaîne**—choisissez des **identifiants uniques et longs** (`SQL_GET_USER` plutôt que `SQL`).

```php
// src/QueryBuilder.php
$query = <<<SQL
    SELECT id, name
    FROM users
    WHERE active = 1
    SQL;
```

> [!NOTE]
> Le saviez-vous ? L'indentation de la balise de fermeture dicte l'indentation retirée du texte. Si vous indentez le corps *moins* que la balise de fermeture, PHP lèvera une ParseError.

---

<a id="trailing-commas-calls"></a>
## Virgules de fin dans les appels de fonctions et de méthodes

Les appels de fonctions peuvent se terminer par une virgule de fin—pratique pour les diffs multilignes et les listes d'arguments générées :

```php
// src/LoggerService.php
$this->logger->info(
    'User saved',
    [
        'id' => $user->id,
        'ip' => $request->ip(),
    ],
);
```

Cela s'applique aux **appels** (invocations), et non à la déclaration des paramètres `function foo($a,)` en 7.3 (les virgules de fin dans les listes de paramètres sont arrivées plus tard avec PHP 8.0).

> [!NOTE]
> Le saviez-vous ? Les virgules de fin dans les *déclarations* de fonctions (définition des paramètres) sont toujours une erreur de syntaxe en PHP 7.3. Elles ne sont prises en charge que lors des *appels*.

---

<a id="list-references"></a>
## Assignations par référence dans `list()` et `[]`

La déstructuration peut lier les variables **par référence** :

```php
// src/Destructure.php
[&$head, $middle, &$tail] = $parts;
// même idée avec list() :
list(&$a, $b) = $pair;
```

Utilisez cette syntaxe lorsque vous modifiez intentionnellement des parties d'une structure partagée ; sinon, préférez les valeurs simples pour garder un flux de données évident.

---

<a id="instanceof-literals"></a>
## `instanceof` avec des littéraux

Le côté gauche de l'expression peut être un **littéral** ; le résultat est alors toujours **`false`**. Cela existe principalement par souci de **cohérence** et pour faciliter l'analyse statique du code, et non pour une logique d'exécution sur laquelle vous devriez vous appuyer.

```php
// src/Validation.php
$result = (123 instanceof User); // false
```

---

<a id="compile-error"></a>
## `CompileError`

`CompileError` est introduit comme classe de base pour certains **échecs au moment de la compilation** ; `ParseError` en **hérite**. Initialement, cela se manifeste le plus visiblement lorsque **`token_get_all($code, TOKEN_PARSE)`** rencontre des problèmes d'analyse—certaines situations qui étaient des erreurs fatales peuvent désormais apparaître sous forme d'exceptions dans les outils de développement.

```php
// src/ErrorHandling.php
try {
    eval('invalid code');
} catch (CompileError $e) {
    echo "Échec de la compilation : " . $e->getMessage();
}
```

---

<a id="core-helpers"></a>
## Fonctions d'aide du noyau nouvelles et mises à jour

* **`array_key_first($array)`** / **`array_key_last($array)`** — évitez les astuces basées sur `reset()`/`end()` pour obtenir les clés ; le comportement est défini proprement pour votre type de tableau (voir le manuel pour les tableaux vides).
* **`is_countable($value)`** — `true` pour les tableaux et les objets implémentant `Countable`—remplace la vérification courante `is_array($x) || $x instanceof \Countable` avant d'appeler `count()`.
* **`hrtime($as_number = false)`** — temps monotone haute résolution (nanosecondes) ; à préférer à `microtime(true)` pour mesurer des **intervalles de temps** (et non l'heure calendrier du système).
* **`net_get_interfaces()`** — énumère les interfaces réseau là où c'est pris en charge (dépend de la plateforme).

```php
// src/Helpers.php
$firstKey = array_key_first($myArray);
if (is_countable($data)) {
    $count = count($data);
}
```

> [!NOTE]
> Le saviez-vous ? `hrtime()` utilise l'horloge monotone du système, qui est insensible aux modifications de l'heure système (comme les synchronisations NTP). Cela en fait le seul moyen fiable de mesurer des intervalles de temps d'exécution.

---

<a id="json-exceptions"></a>
## JSON : `JSON_THROW_ON_ERROR` et `JsonException`

Passez l'option **`JSON_THROW_ON_ERROR`** à **`json_encode()`** / **`json_decode()`** pour obtenir une exception **`JsonException`** au lieu de jongler avec **`json_last_error()`**. L'option **`JSON_PARTIAL_OUTPUT_ON_ERROR`** l'emporte toujours lorsque les deux drapeaux s'appliquent.

```php
// src/JsonParser.php
try {
    $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    // Gérer l'erreur
}
```

---

<a id="argon2id"></a>
## Hachage de mot de passe : Argon2id

Lorsque PHP est compilé avec une version suffisamment récente de **libargon2**, la fonction **`password_hash()`** accepte **`PASSWORD_ARGON2ID`**, vous offrant la variante **hybride i+d** d'Argon2 en plus de la prise en charge existante d'Argon2i.

```php
// src/Security.php
$hash = password_hash('secret', PASSWORD_ARGON2ID);
```

---

<a id="extensions-stdlib"></a>
## Extensions notables & stdlib

* **PCRE** : moteur mis à niveau vers **PCRE2**—légers changements de comportement (plages de classes de caractères plus strictes, etc.) ; **`preg_quote()`** échappe désormais également le caractère **`#`**.
* **Multibyte (mbstring)** : prise en charge d'**Unicode 11**, chaînes de caractères **> 2 Go**, opérations de casse plus rapides ; **conversion de casse complète** (full case-folding) pour les comparaisons insensibles à la casse ; captures nommées **`mb_ereg_*`** alignées de plus près sur l'ergonomie de PCRE.
* **LDAP** : les **contrôles** LDAP sont intégrés aux API de recherche/modification et à **`ldap_parse_result()`** ; la gestion des options pour les contrôles serveur/client a été améliorée.
* **MySQLi / PDO MySQL** : les requêtes préparées peuvent remonter des **fractions de seconde** pour les colonnes de type `DATETIME(6)` / `TIMESTAMP(6)`.
* **FPM** : options de journalisation (`log_limit`, `log_buffering`, `decorate_workers_output`) ; **`getallheaders()`** est disponible sous FPM.
* **Sessions / cookies** : **`session_set_cookie_params(array $options)`** et **`session.cookie_samesite`** ; **`setcookie()` / `setrawcookie()`** acceptent un tableau `$options` incluant **`samesite`**.
* **Stream** : les noms IPv6 renvoyés par **`stream_socket_get_name()`** utilisent le format d'hôte **entre crochets** (par exemple `[::1]:1337`).
* **SPL autoload** : si un autoloader **lève une exception**, les **autoloader suivants sont ignorés**.
* **IMAP** : les connexions via **rsh/ssh** sont **désactivées par défaut**—à n'activer qu'avec prudence (`imap.enable_insecure_rsh`).

---

<a id="practical-recipes"></a>
## Recettes pratiques

### Vérification avant `count()`

```php
// src/recipes.php
if (is_countable($rows)) {
    $n = count($rows);
}
```

### Récupération stable des premières et dernières clés

```php
// src/recipes.php
$firstKey = array_key_first($map);
$lastKey = array_key_last($map);
```

### Analyse JSON stricte

```php
// src/recipes.php
try {
    $payload = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    // gérer ou encapsuler
}
```

### Mesure du temps haute résolution

```php
// src/recipes.php
$t0 = hrtime(true);
// ...
$elapsedNs = hrtime(true) - $t0;
```

---

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

Voici quelques erreurs courantes commises par les développeurs en PHP 7.3 :

### 1. Indenter involontairement la balise de fermeture Heredoc
Si le corps du texte comporte des lignes moins indentées que la balise de fermeture, ou si la balise de fermeture contient des espaces de fin, une ParseError est levée.
```php
// src/BadHeredoc.php
// Mauvais : le corps est moins indenté que la balise de fermeture, ce qui lève une ParseError
$text = <<<TEXT
Line 1
    Line 2
      TEXT;
```
```php
// src/GoodHeredoc.php
// Bon : l'indentation de la balise de fermeture correspond à l'indentation minimale du corps
$text = <<<TEXT
    Line 1
    Line 2
    TEXT;
```

### 2. Utiliser `continue` dans un `switch`
L'instruction `continue` à l'intérieur d'un bloc switch se comporte comme un `break` en PHP, ce qui entraîne souvent des boucles infinies lorsque les développeurs s'attendent à ce qu'elle passe à l'itération de boucle suivante. PHP 7.3 émet un avertissement pour cela.
```php
// src/BadContinueSwitch.php
// Mauvais : émet un avertissement : \"continue\" targeting \"switch\" is equivalent to \"break\"
foreach ($items as $item) {
    switch ($item) {
        case 'skip':
            continue;
    }
}
```
```php
// src/GoodContinueSwitch.php
// Bon : utilisez \"continue 2\" pour cibler la boucle foreach externe
foreach ($items as $item) {
    switch ($item) {
        case 'skip':
            continue 2;
    }
}
```

---

<a id="limitations"></a>
## Limitations

* **Virgules de fin** : prises en charge uniquement dans les appels de fonctions et de méthodes. Tenter d'écrire `function foo($a, $b,) {}` (virgule de fin dans la déclaration) lèvera une erreur d'analyse syntaxique (ParseError).
* **Heredoc flexible** : la balise de fermeture doit être sur sa propre ligne et ne peut comporter aucun texte (sauf un point-virgule ou un retour à la ligne) après elle. Tout espace blanc de fin après l'identifiant de fermeture déclenchera une erreur de compilation.

---

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

### Noyau

* **Heredoc/nowdoc flexible** : les chaînes qui **contiennent la balise de fermeture** peuvent changer de signification ou générer des **erreurs d'analyse**—renommez les balises ou échappez le contenu.
* **`continue` à l'intérieur de `switch`** : émet désormais un **avertissement** (en PHP, il se comportait comme `break`). **Vérifiez vos structures `switch` + `continue`.**
* **`ArrayAccess` avec des clés sous forme de chaînes numériques** : pour les clés littérales **`\"123\"`**, PHP **cesse de forcer le transtypage** en entier pour `ArrayAccess`—**`offsetGet(\"123\")`** est appelé au lieu de **`offsetGet(123)`**.
* **Propriétés statiques via des astuces de référence** : vous ne pouvez plus **séparer** les propriétés statiques héritées à l'aide d'une assignation par référence.
* **Références issues d'accès à des tableaux ou propriétés** : les références renvoyées par des lectures en chaîne de **tableaux ou propriétés** sont **déballées immédiatement**.
* **Déballage de `...$traversable`** : les **clés non entières** sur un objet `Traversable` **ne sont plus déballées** lors des appels.
* **Avertissements promus en exceptions (`EH_THROW`)** : ces exceptions **ne renseignent plus** la fonction **`error_get_last()`**.
* **Messages de `TypeError`** : les types incorrects s'affichent sous la forme **`int` / `bool`**, et non **`integer` / `boolean`**.
* **`compact()`** : les **variables non définies** passées par leur nom génèrent désormais une **Notice**.
* **`getimagesize()`** (et fonctions similaires) : le type MIME pour les BMP est rapporté comme **`image/bmp`** (IANA), et non **`image/x-ms-bmp`**.
* **Cookies (à partir de PHP 7.3.23)** : les **noms** des cookies entrants ne sont **pas décodés de l'URL** (alignement de sécurité).

### BCMath

* Les avertissements passent par le **gestionnaire d'erreurs standard** de PHP.
* **`bcmul()` / `bcpow()`** respectent plus strictement la **précision (scale) demandée**.

### IMAP

* Le tunneling **rsh/ssh** est **désactivé** à moins que vous n'activiez explicitement **`imap.enable_insecure_rsh`**.

### Regex Multibyte (`mb_ereg_*`)

* Les **captures nommées** modifient la **structure du tableau des correspondances** et la syntaxe de substitution de **`mb_ereg_replace()`**.

### MySQLi et PDO MySQL

* Les **fractions de seconde** sont désormais visibles dans les résultats liés.

### Reflection

* Les exports sous forme de chaînes de caractères utilisent les orthographes de type **`int` / `bool`** dans la sortie.

### SimpleXML

* Les opérations numériques sur les nœuds de texte SimpleXML choisissent **int vs float** au lieu de toujours effectuer un transtypage via **int**.

---

<a id="deprecations"></a>
## Dépréciations (à corriger tôt)

Éléments clés du noyau :

* **`define()` insensible à la casse** (`true` comme troisième argument) et utilisation d'une **casse différente** de celle définie.
* **Fonctions avec espace de noms nommées `assert()`**—à éviter ; le moteur traite **`assert()`** de manière spéciale.
* **Arguments de recherche (needles) non textuels** dans la **famille `strpos`**—transtypez explicitement.
* **`fgetss()`**, **`gzgetss()`**, **`SplFileObject::fgetss()`**, et le filtre **`string.strip_tags`**.
* **`FILTER_FLAG_SCHEME_REQUIRED` / `FILTER_FLAG_HOST_REQUIRED`** avec **`FILTER_VALIDATE_URL`** (redondant).
* **`image2wbmp()`** (GD).
* **`Normalizer::NONE`** avec ICU ≥ 56.
* Les **alias `mbereg_*()` non documentés**—basculez vers **`mb_ereg_*()`**.
* Dépréciation de l'option INI **`pdo_odbc.db2_instance_name`**.

---

<a id="interactive-quiz"></a>
## Quiz interactif

Testez vos connaissances sur PHP 7.3 :

1. **Laquelle de ces affirmations est vraie concernant les virgules de fin dans PHP 7.3 ?**
   * A) Elles sont autorisées dans les appels de fonction mais pas dans les déclarations de fonction.
   * B) Elles sont autorisées dans les déclarations de fonction mais pas dans les appels de fonction.
   * C) Elles sont autorisées à la fois dans les déclarations et dans les appels.
   * D) Elles restent des erreurs de syntaxe partout.

   <details>
   <summary>Cliquez pour afficher la réponse</summary>
   
   **Bonne réponse : A**  
   PHP 7.3 autorise les virgules de fin dans les appels de fonction/méthode (invocations), mais déclarer une fonction avec une virgule de fin dans la liste des paramètres reste une erreur d'analyse.
   </details>

2. **Que fait PHP 7.3 lorsqu'il rencontre un `continue` à l'intérieur d'une instruction `switch` ?**
   * A) Il se comporte silencieusement comme un `continue 2` sur la boucle parente.
   * B) Il lève une ParseError fatale.
   * C) Il émet un avertissement indiquant que 'continue' ciblant un 'switch' équivaut à 'break'.
   * D) Il poursuit l'exécution normalement sans aucun avertissement.

   <details>
   <summary>Cliquez pour afficher la réponse</summary>
   
   **Bonne réponse : C**  
   PHP 7.3 déclenche un avertissement car `continue` dans un `switch` ne permet pas de passer à l'itération suivante d'une boucle externe ; il se comporte comme `break`.
   </details>

3. **Quelle fonction a été introduite en PHP 7.3 pour vérifier si una variable peut être passée à `count()` ?**
   * A) `is_iterable()`
   * B) `is_countable()`
   * C) `can_count()`
   * D) `is_array_or_countable()`

   <details>
   <summary>Cliquez pour afficher la réponse</summary>
   
   **Bonne réponse : B**  
   `is_countable()` renvoie `true` si la variable est un tableau ou une instance d'une classe qui implémente `Countable`.
   </details>

---

<a id="closing-thoughts"></a>
## Réflexions finales

PHP 7.3 récompense une **revue ciblée des diffs** : recherchez les structures **`continue` + `switch`**, les appels à **`compact(`**, les implémentations de **`ArrayAccess`**, les balises **heredoc** et le **déballage d'arguments `...$generator`**. Exécutez à nouveau les tests d'intégration des expressions régulières (**regex**) et des dates/heures, puis passez à l'étape suivante—**7.4** ajoutera des fonctionnalités de langage beaucoup plus importantes, donc nettoyer les dépréciations de la version 7.3 dès maintenant réduira le bruit lors du prochain saut de version.