---
title: 'PHP 7.4 from 7.3: Typed Properties, Arrow Functions, FFI & Migration | DevSense'
description: 'Guide de mise à niveau PHP 7.4 : propriétés typées, fonctions fléchées, variance, opérateur ??=, opérateur spread dans les tableaux, WeakReference, __serialize/__unserialize, préchargement OPcache, FFI—et changements non rétrocompatibles.'
faq:
    - { question: "Qu'est-ce que les propriétés typées dans PHP 7.4 ?", answer: 'PHP 7.4 permet de déclarer des types pour les propriétés de classe. Accéder à una propriété typée non initialisée avant de lui assigner une valeur lève une TypeError.' }
    - { question: 'Comment fonctionnent les fonctions fléchées en PHP 7.4 ?', answer: "Les fonctions fléchées utilisent le mot-clé 'fn' et capturent automatiquement les variables de la portée parente par valeur. Elles sont limitées à une seule expression qui est implicitement retournée." }
    - { question: "Qu'est-ce que le préchargement OPcache ?", answer: "Le préchargement permet de charger des fichiers PHP en mémoire au démarrage, les rendant disponibles en permanence pour toutes les requêtes sans surcoût d'analyse, mais nécessite un redémarrage du serveur pour les mettre à jour." }
    - { question: 'Quel est le nouveau mécanisme de sérialisation personnalisée dans PHP 7.4 ?', answer: "PHP 7.4 introduit les méthodes magiques '__serialize' et '__unserialize', qui remplacent l'ancienne interface 'Serializable' et offrent un moyen plus robuste de personnaliser la sérialisation." }
published: '2026-05-31'
---
# PHP 7.4 : Fonctionnalités majeures

Imaginez mettre à niveau votre version de PHP pour voir ensuite votre production s'effondrer parce qu'une propriété de classe n'a pas été initialisée. PHP 7.4 apporte la sécurité des types tant attendue pour les propriétés de classe, mais ce pouvoir s'accompagne de l'exigence stricte qu'elles soient initialisées avant tout accès. Il s'agit de la passerelle ultime vers le PHP 8 moderne, introduisant les propriétés typées et les fonctions fléchées qui exigent de la rigueur de la part des développeurs.

## Table des matières
* [Propriétés typées](#typed-properties)
* [Fonctions fléchées (Arrow functions)](#arrow-functions)
* [Covariance & contravariance](#variance)
* [Assignation par fusion de null (`??=`)](#null-coalesce-assign)
* [Opérateur Spread dans les tableaux](#array-spread)
* [Séparateur de littéral numérique](#numeric-separator)
* [Références faibles (Weak references)](#weak-references)
* [`__toString()` peut désormais lever des exceptions](#tostring-exceptions)
* [Sérialisation : `__serialize` / `__unserialize`](#custom-serialize)
* [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="typed-properties"></a>
## Propriétés typées

Les propriétés de classe peuvent déclarer des types. Les propriétés typées non initialisées **ne doivent pas être lues** avant d'avoir reçu une valeur—le message d'erreur obtenu est : **`Typed property ... must not be accessed before initialization`**.

```php
// src/User.php
final class User
{
    public int $id;
    public string $name;
    public ?string $nickname = null;
}
```

Notes issues de code réel :

* Utilisez **`?Type`** ou une valeur par défaut si l'absence de valeur est valide.
* **`callable`** n'est **pas** autorisé comme type de propriété (dépend du contexte).
* Vous ne pouvez pas attribuer une valeur par défaut de type `new SomeClass()` à une propriété lors de sa déclaration de la même manière qu'avec les types scalaires ; les propriétés typées d'objets sont contraintes.

> [!NOTE]
> Le saviez-vous ? En PHP 7.4, les propriétés typées sont implémentées au niveau du moteur avec une sécurité d'initialisation paresseuse ("lazy initialization"). Une propriété existe dans un état \"non initialisé\", qui est distinct de `null`. Appeler `isset($user->id)` renverra `false` si elle n'a pas été assignée, même si elle n'est pas nullable.

---

<a id="arrow-functions"></a>
## Fonctions fléchées (Arrow functions)

Les fonctions fléchées (`fn`) capturent les variables **par valeur** depuis la portée parente et retournent une seule expression—idéal pour les callbacks courts.

```php
// src/MathHelper.php
$factor = 10;
$nums = array_map(fn($n) => $n * $factor, [1, 2, 3]);
```

**`fn` est réservé** : vous ne pouvez pas nommer une fonction ou une classe `fn` (les noms de méthodes ou de constantes restent autorisés).

> [!NOTE]
> Le saviez-vous ? Contrairement aux fonctions fléchées de JavaScript, les fonctions fléchées de PHP capturent les variables uniquement par valeur. Vous ne pouvez pas modifier une variable de la portée externe depuis l'intérieur d'une fonction fléchée.

---

<a id="variance"></a>
## Covariance & contravariance

Les types de retour peuvent être **covariants** et les types de paramètres **contravariants** lors de l'héritage, avec certaines limites. La variance complète fonctionne mieux avec l'**autoloading** ; dans un seul fichier, seules les références **non cycliques** sont possibles car les classes doivent être connues avant d'être utilisées.

```php
// src/Repository.php
interface Repository {
    public function get(int $id): object;
}

class UserRepository implements Repository {
    public function get(int $id): User { // Type de retour covariant
        return new User();
    }
}
```

---

<a id="null-coalesce-assign"></a>
## Assignation par fusion de null (`??=`)

Assigne une valeur uniquement si la variable de gauche n'est pas définie ou vaut null :

```php
// src/Config.php
$config['debug'] ??= false;
```

---

<a id="array-spread"></a>
## Opérateur Spread dans les tableaux

Déballez les itérables à l'intérieur des littéraux de tableaux :

```php
// src/ArrayMerge.php
$base = [1, 2];
$all = [0, ...$base, 3]; // [0, 1, 2, 3]
```

S'associe bien avec `array_merge(...$arrays)` maintenant que `array_merge()` peut être appelée **sans arguments** (renvoie `[]`).

---

<a id="numeric-separator"></a>
## Séparateur de littéral numérique

Les caractères de soulignement (underscores) améliorent la lisibilité et ne modifient pas la valeur :

```php
// src/Constants.php
$million = 1_000_000;
$hex = 0xFF_FF_FF;
```

---

<a id="weak-references"></a>
## Références faibles (Weak references)

`WeakReference` vous permet de conserver une référence qui **n'empêche pas** la destruction d'un objet—utile pour les caches et les graphes sans créer de fuites de mémoire.

```php
// src/Cache.php
$obj = new stdClass();
$weakRef = WeakReference::create($obj);
```

---

<a id="tostring-exceptions"></a>
## `__toString()` peut désormais lever des exceptions

Lever des exceptions depuis `__toString()` est autorisé (auparavant fatal). Certaines erreurs fatales récupérables antérieures sont devenues des exceptions de type **`Error`**—auditez les chemins de conversion en chaînes de caractères.

```php
// src/Stringy.php
class Stringy {
    public function __toString(): string {
        throw new Exception("Échec du transtypage en chaîne");
    }
}
```

---

<a id="custom-serialize"></a>
## Sérialisation : `__serialize` / `__unserialize`

De nouveaux hooks remplacent les motifs ad hoc et supplantent l'interface `Serializable` à long terme :

```php
// src/Point.php
final class Point
{
    private int $x;
    private int $y;

    public function __serialize(): array
    {
        return ['x' => $this->x, 'y' => $this->y];
    }

    public function __unserialize(array $data): void
    {
        $this->x = $data['x'];
        $this->y = $data['y'];
    }
}
```

Les types SPL comme `ArrayObject` peuvent émettre de **nouvelles** charges utiles lisibles en 7.4+ mais pas sur les versions antérieures de PHP.

---

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

* **FFI** : appelle des bibliothèques C à partir de PHP (puissant ; nécessite un bac à sable et un audit de sécurité).
* **Préchargement OPcache** : charge un ensemble de fichiers une seule fois au démarrage pour un coût d'autoloading réduit—nécessite `opcache.preload` et souvent `opcache.preload_user`.
* **`mb_str_split()`** : identique à `str_split()` mais travaille sur des **points de code** (code points).
* **`proc_open()`** : accepte la commande sous forme de **tableau** (pas d'interpréteur de commandes), ainsi que les descripteurs `redirect` / `null`.
* **`strip_tags()`** : balises autorisées sous forme de **tableau** de noms.
* **PDO** : login/mot de passe autorisés dans le DSN pour davantage de pilotes ; **`??`** dans le SQL échappe un caractère `?` littéral (par exemple pour le JSON PostgreSQL `?`).
* **PCRE** : `preg_replace_callback*` accepte des options (flags) (`PREG_OFFSET_CAPTURE`, `PREG_UNMATCHED_AS_NULL`).
* **Password** : Argon2 via **sodium** lorsqu'il est compilé sans libargon.

---

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

### Valeurs par défaut sécurisées avec `??=`

```php
// src/recipes.php
$options['timeout'] ??= 30;
```

### Fonction fléchée dans un tri

```php
// src/recipes.php
usort($rows, fn($a, $b) => $a['score'] <=> $b['score']);
```

### Fusion par déballage de listes dynamiques

```php
// src/recipes.php
$merged = array_merge(...$chunks);
```

---

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

Voici quelques problèmes typiques rencontrés par les développeurs lors de la transition vers PHP 7.4 :

### 1. Lire des propriétés typées non initialisées
```php
// src/BadPropertyAccess.php
// Mauvais : Lire la propriété avant de lui assigner une valeur lève une erreur fatale
class BadUser {
    public int $id;
}
$user = new BadUser();
echo $user->id; // Fatal Error: Typed property BadUser::$id must not be accessed before initialization
```
```php
// src/GoodPropertyAccess.php
// Bon : Fournir une valeur par défaut ou initialiser dans le constructeur
class GoodUser {
    public int $id = 0; // valeur par défaut
    // OU :
    // public function __construct(int $id) { $this->id = $id; }
}
```

### 2. Modifier des variables de la portée externe dans les fonctions fléchées
```php
// src/BadArrowScope.php
// Mauvais : Penser que les fonctions fléchées capturent les variables de portée externe par référence
$count = 0;
$increment = fn() => $count++;
$increment();
echo $count; // Sortie : 0 (La variable externe reste inchangée !)
```
```php
// src/GoodArrowScope.php
// Bon : Utiliser une fonction anonyme traditionnelle avec liaison par référence explicite
$count = 0;
$increment = function() use (&$count) {
    $count++;
};
$increment();
echo $count; // Sortie : 1
```

---

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

* **Fonctions fléchées** : Elles sont strictement limitées à une seule expression. Vous ne pouvez pas y placer de logique multiligne ou d'instructions de retour (return) explicites.
* **Préchargement OPcache** : Les fichiers préchargés sont stockés en mémoire de manière permanente. Tout changement apporté au code source nécessite un redémarrage complet de PHP-FPM ou du serveur web pour prendre effet.
* **FFI** : Offre des performances incroyables mais contourne les couches de sécurité PHP. Elle est hautement vulnérable aux corruptions de mémoire (erreurs de segmentation) et ne devrait pas être utilisée dans des environnements d'hébergement mutualisé non fiables.

---

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

### Noyau

* **Accès par tableau sur des non-tableaux** : utiliser `null`, `bool`, `int`, `float`, ou `resource` comme `$x['k']` émet une **Notice**.
* **`get_declared_classes()`** : ne liste plus les classes **anonymes** tant qu'elles ne sont pas instanciées.
* **`fn`** : mot-clé réservé pour les noms de classes/fonctions.
* **`<?php` en fin de fichier** sans retour à la ligne : désormais analysé comme une **balise ouvrante** (auparavant ambigu avec `short_open_tag`).
* **Wrappers de flux** : `include`/`require` sur les flux peuvent appeler `stream_set_option(STREAM_OPTION_READ_BUFFER)`—implémentez-la ou retournez `false` pour éviter les avertissements.
* **Sérialisation** : **format `o` retiré** (n'avait d'importance que pour les charges utiles forgées).
* **Constantes de mot de passe** : les identifiants `PASSWORD_*` sont des **chaînes de caractères** (`'2y'`, `'argon2id'`, …), pas des entiers—le code comparant à `1`/`2`/`3` casse.
* **`htmlentities()`** : émet une **Notice** lorsque l'encodage se replie sur le comportement de `htmlspecialchars`.
* **`fread`/`fwrite`** : renvoient **`false`** en cas d'échec (et non `''`/`0`) ; l'échec peut émettre une **Notice**.
* **BCMath** : avertit en cas de chaînes numériques mal formées (toujours traitées comme zéro).
* **CURL** : la sérialisation de **`CURLFile`** lève une exception plus tôt ; `curl_version($nonDefault)` avertit et ignore les valeurs différentes de `CURLVERSION_NOW`.
* **Date** : inspecter `DateTime*` ne laisse plus de propriétés orphelines ; la **comparaison de `DateInterval`** avertit et renvoie toujours `false`.
* **Intl** : la variante par défaut de `idn_to_ascii` / `idn_to_utf8` est **`INTL_IDNA_VARIANT_UTS46`**.
* **MySQLi** : serveur embarqué retiré ; propriété non documentée `$mysqli->stat` retirée—utilisez **`mysqli::stat()`**.
* **OpenSSL** : `openssl_random_pseudo_bytes` lève une exception comme **`random_bytes`** en cas d'échec ; `$crypto_strong` vaut `true` si aucune exception n'est levée.
* **PCRE** : avec **`PREG_UNMATCHED_AS_NULL`**, les groupes non correspondants en fin de chaîne valent **`null`** (taille stable du tableau `$matches`).
* **PDO** : la sérialisation de **`PDO`/`PDOStatement`** lève une **`Exception`** (et non une `PDOException`).
* **Reflection** : la sérialisation d'objets de réflexion **lève une exception** ; certaines **valeurs numériques de constantes de modificateurs** ont changé.
* **SPL / `ArrayObject`** : le comportement de **`get_object_vars`** change sauf si `STD_PROP_LIST` est défini ; `SplPriorityQueue::setExtractFlags(0)` lève immédiatement une exception.
* **Tokenizer** : les octets inattendus deviennent des jetons **`T_BAD_CHARACTER`** au lieu de créer des trous.
* **Cookies (à partir de la version 7.4.11)** : les **noms** de cookies ne sont plus décodés de l'URL.

---

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

Points importants :

* **Opérateurs ternaires imbriqués** sans parenthèses explicites (sauf la forme non ambiguë avec opérande central).
* **Accès par accolades** `$str{0}` / `$arr{0}` → utilisez `[]`.
* **`(real)`** / **`is_real()`** → **`(float)`** / **`is_float()`**.
* **`array_key_exists()` sur les objets** → **`isset()`** / **`property_exists()`**.
* **`implode($parts, $glue)`** dans l'ordre inverse → **`implode($glue, $parts)`**.
* **`ReflectionType::__toString()`** et **`Reflection*::export()`** → utilisez des API comme **`ReflectionNamedType::getName()`** et le transtypage en chaînes des objets de réflexion.
* **`allow_url_include`**, **`FILTER_SANITIZE_MAGIC_QUOTES`**, les assistants LDAP historiques paginés, et de nombreuses petites fonctions (`money_format()`, `hebrevc()`, …)—voir la page [migration74 deprecated](https://www.php.net/manual/en/migration74.deprecated.php).

---

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

Testez vos connaissances sur les fonctionnalités de PHP 7.4 :

1. **Quel est le résultat de la lecture d'une propriété typée non initialisée en PHP 7.4 ?**
   * A) Elle retourne `null`
   * B) Elle génère une Notice et retourne `null`
   * C) Elle lève une exception de type Error
   * D) Elle retourne `false`

   <details>
   <summary>Cliquez pour afficher la réponse</summary>
   
   **Bonne réponse : C**  
   PHP 7.4 interdit strictement la lecture d'une propriété typée avant qu'elle ne soit initialisée. Tenter de le faire lève une exception `Error` (plus précisément `Error: Typed property ... must not be accessed before initialization`).
   </details>

2. **Pouvez-vous modifier une variable de la portée externe depuis l'intérieur d'une fonction fléchée ?**
   * A) Oui, les variables sont capturées par référence
   * B) Non, les variables sont capturées par valeur
   * C) Oui, mais uniquement si vous utilisez le mot-clé `use`
   * D) Oui, mais uniquement pour les objets

   <details>
   <summary>Cliquez pour afficher la réponse</summary>
   
   **Bonne réponse : B**  
   Les fonctions fléchées de PHP capturent automatiquement les variables par valeur. Tout changement apporté à des variables de la portée externe au sein de la fonction fléchée reste local et n'affecte pas la portée externe.
   </details>

3. **Quel type de propriété n'est PAS autorisé en PHP 7.4 ?**
   * A) `object`
   * B) `callable`
   * C) `iterable`
   * D) `?string`

   <details>
   <summary>Cliquez pour afficher la réponse</summary>
   
   **Bonne réponse : B**  
   Le type `callable` est explicitement interdit comme type de propriété car son comportement dépend fortement du contexte d'appel.
   </details>

---

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

Considérez PHP 7.4 comme **la version de transition** : adoptez les propriétés typées et la sérialisation moderne **avant** de passer aux erreurs plus strictes de la version 8.0. En parallèle, recherchez les algorithmes de mots de passe sous forme d'entiers, les identifiants nommés **`fn`**, la syntaxe **`$var{idx}`** et les motifs basés sur **ArrayObject**—et re-testez les **flux**, l'aléatoire **OpenSSL** et la sérialisation **PDO/Reflection** dans vos tests d'intégration.