PHP 8.5 : tour des fonctionnalités à venir

Nouveautés de PHP 8.5 : pipe operator, #[NoDiscard], constantes de closures statiques, array_first/last, gains intl et debug. Par quoi commencer.

TL;DR Échelle de hype (mon classement joueur)

  1. Pipe Operator (|>) – Bonheur de transformation linéaire et lisible. Aimant à refactorings.
  2. Attribut #[\NoDiscard] – Transforme « on a oublié d'utiliser le retour » en avertissement immédiat. Se marie merveilleusement avec les pipes.
  3. Closures statiques / callables first-class dans les expressions constantes – Cartes de stratégie et arguments d'attributs au moment de la compilation. Bonbon pour frameworks.
  4. php --ini=diff – Diff instantané de dérive d'environnement. Vous évite la spéléologie de config.
  5. Attributs sur constantes globales et de classe – Des métadonnées partout (flags, dépréciations, tags sémantiques).
  6. array_first() / array_last() – Évident, révélateur d'intention, non mutateur. Adieu effets de bord de reset().
  7. get_exception_handler() et compagnie – Introspection pour gestion d'erreurs en couches (victoire niveau framework / infra).
  8. Douceurs Intl (IntlListFormatter, Locale::isRightToLeft()) – UX localisée plus fluide avec presque aucun code.
  9. Levenshtein conscient des graphèmes – Fuzzy matching utilisateur qui respecte vraiment les caractères humains.
  10. Objet Directory + introspection cURL / build / divers – Polissage de cohérence et d'opérabilité.

(Oui, votre ordre peut différer. C'est le plaisir : en débattre autour d'un café.)


1. Pipe Operator (|>) – « Easy Peasy Lemon Squeezy »

Les appels imbriqués et les variables temporaires jetables ? Disparus. Le pipe operator prend la valeur à gauche et la transmet comme premier argument au callable à droite. Vous lisez de haut en bas, la logique coule comme de la prose, et l'intention vous saute au visage.

Avant (marelle de variables) :

$email = $request->string('email');
$email = trim($email);
$email = strtolower($email);
sendEmail($email);

Avant (imbrication de parenthèses) :

sendEmail(strtolower(trim($request->string('email'))));

Après (zen du pipe) :

$request->string('email')
    |> trim(...)
    |> strtolower(...)
    |> sendEmail(...);

Pourquoi c'est important :

  • Flux de données visible. Plus de pile mentale de retours imbriqués.
  • Se combine très bien avec de petits helpers purs.
  • Encourage à décomposer les transformations en fonctions / closures nommées.
  • Satisfait automatiquement #[\NoDiscard], puisque la valeur continue d'avancer.

Conseil de style : Gardez chaque étape sans effet de bord ; réservez le pipe final à un effet (par exemple persister, envoyer, émettre), pour repérer visuellement où la « pureté » se termine.


2. #[\NoDiscard] – L'intention militarisée

Combien de bugs subtils étaient juste « on a appelé le truc mais oublié d'utiliser ce qu'il retournait » ? Marquez une fonction ou une méthode avec #[\NoDiscard] pour exiger que son résultat soit utilisé, ou ignoré consciemment via un cast (void).

#[\NoDiscard("Token must be used – did you forget to persist or dispatch?")]
function issueAuthToken(User $user): string {
    return generateTokenFor($user);
}

issueAuthToken($user); // ⚠ Emits warning in 8.5
(void) issueAuthToken($user); // Explicit intentional discard

Patterns :

  • Objets de résultat (Result, Outcome, ValidationReport).
  • Builders immuables (qui retournent une nouvelle instance à chaque appel).
  • Garde de sécurité / d'effets de bord (tokens, signatures).

Synergie : Dans une pipeline, le retour de chaque étape est naturellement consommé par la suivante, donc les oublis accidentels disparaissent.


3. Closures statiques dans les expressions constantes – « Attends... quoi ?! »

Vous pouvez maintenant intégrer des closures statiques (ou des callables first-class) dans les expressions constantes, valeurs de propriété par défaut, arguments d'attributs et tableaux de paramètres par défaut. Pensez registres de stratégie au moment de la compilation, sans gymnastique de câblage au démarrage.

class Sanitizers {
    public const STAGES = [
        'trim' => trim(...),
        'upper' => static function (string $v): string { return strtoupper($v); },
    ];
}

// Attribute example
#[Validate(
    rules: [
        'title' => static function(string $v){ return mb_strlen($v) > 0; },
        'slug'  => static function(string $v){ return preg_match('/^[a-z0-9-]+$/', $v); },
    ]
)]
class Article {}

Pourquoi ça claque :

  • Élimine les recherches dans un service locator pour les stratégies simples.
  • Pousse les tables de correspondance pures dans des constantes (immuables + cacheables).
  • Les attributs peuvent maintenant encapsuler directement de la logique, pas seulement des métadonnées scalaires.

Contrainte : Doit être static ; pas de $this, pas de capture de variable. Si vous avez besoin de contexte, passez-le explicitement plus tard.


4. php --ini=diff – Rayon X de dérive de config

Fatigué du « Mais ça marche en staging » ? Ce flag CLI affiche seulement les directives INI qui diffèrent de la valeur par défaut.

php --ini=diff
# memory_limit: "128M" -> "-1"
# max_execution_time: "30" -> "0"

Cas d'usage :

  • Étape CI pour imposer une base cohérente.
  • Vérification rapide quand un worker se comporte bizarrement.
  • Triage d'anomalies mémoire / temps.

Astuce pro : capturez la sortie dans le contrôle de version pour les bases runtime.


5. Attributs sur constantes globales et de classe – Des métadonnées partout

Les constantes passent de « valeur idiote » à « participante annotée ». Décorez les flags de domaine, feature toggles, avis de dépréciation, sémantique d'unité, directement au site de définition.

#[Deprecated("Use FEATURE_NEW_PRICING instead")]
public const FEATURE_OLD_PRICING = 1;

#[Unit("ms")]
public const DEFAULT_TIMEOUT = 250;

Levier framework : Auto-découvrir les dépréciations, alimenter des catalogues de fonctionnalités, générer de la doc, ou faire respecter une politique via réflexion.


6. array_first() / array_last() – L'évidence existe enfin

Arrêtez les acrobaties de pointeur (reset(), end()) ou les slices juste pour regarder. Ces helpers expriment l'intention directement et ne modifient pas l'état interne du tableau.

$firstUser = array_first($users, default: null);
$lastUser  = array_last($users, default: null);

Pattern de refactoring : Cherchez reset( / end( / les array_slice(..., 0, 1) compliqués, puis remplacez par des appels sémantiques. Diffs plus propres, moins de micro-bugs.


7. get_exception_handler() (et meilleures traces fatales) – Upgrade d'observabilité

Développeurs framework / infra, réjouissez-vous : vous pouvez maintenant introspecter le gestionnaire d'exceptions actif. Chaîner, envelopper, restaurer ou décorer sans jongler avec un global fragile.

$previous = get_exception_handler();
set_exception_handler(function(Throwable $e) use ($previous) {
    logToSentry($e);
    if ($previous) { $previous($e); }
});

Combiné à des stack traces plus riches pour les erreurs fatales, cela accélère fortement les post-mortems de production.


8. Améliorations Intl – Listes et direction à hauteur humaine

IntlListFormatter produit des conjonctions / disjonctions charmantes et conscientes de la locale, sans logique de colle faite maison.

$f = new IntlListFormatter('pt_PT', 'conjunction');
echo $f->format(['Lisboa', 'Porto', 'Coimbra']); // "Lisboa, Porto e Coimbra"

$fOr = new IntlListFormatter('en_US', 'disjunction');
echo $fOr->format(['apples', 'bananas', 'cherries']); // "apples, bananas, or cherries"

Combinez avec Locale::isRightToLeft() (ou locale_is_right_to_left()) pour basculer automatiquement la direction de mise en page.


9. Levenshtein conscient des graphèmes – Vraie distance de chaînes utilisateur

Quand les utilisateurs tapent des emoji, accents, caractères combinants, la distance en octets ou en codepoints naïfs ment. grapheme_levenshtein() respecte les caractères visibles.

grapheme_levenshtein('café', 'cafe'); // 0 – visually same after accent

Suggestions de recherche, fuzzy match et flux de connexion tolérants aux fautes deviennent linguistiquement justes.


10. Le défilé du polissage

Objet Directory : opendir() vous donne maintenant un vrai objet (sécurité de type, extension future) au lieu d'une ressource legacy.

Améliorations cURL : Meilleurs share handles + introspection multi-handle = meilleure réutilisation des connexions dans les workers longue durée (pensez RoadRunner, Swoole) et réglage de performance plus fin.

PHP_BUILD_DATE : Vérification rapide de « quel âge a ce binaire ? » pour les scripts d'audit. Parfait pour s'assurer que les noeuds d'une flotte ne traînent pas silencieusement derrière.


Mémo de synergie des fonctionnalités

Objectif Combiner
Pipeline de transformations avec usage imposé ` >+#[\NoDiscard]`
Validation déclarative / cartes de stratégie Closures statiques en expression constante + attributs de constantes
Refactors plus sûrs des tableaux legacy array_first()/array_last() + typage de retour strict
Triage d'incident de production Meilleures traces fatales + php --ini=diff + get_exception_handler()
Polissage UX international IntlListFormatter + détection de direction + distance de graphèmes

Plan d'adoption pratique

  1. Introduire le Pipe Operator progressivement : commencez dans les couches de normalisation de données pures ; imposez le style (un seul effet de bord à la fin) en code review.
  2. Annoter les APIs critiques avec #[\NoDiscard] : ciblez d'abord sécurité, persistance et builders ; mesurez les volumes d'avertissements en CI.
  3. Refactorer les tables de stratégie : déplacez les maps de callables simples dans des tableaux public const avec closures statiques, pour un coût de démarrage nul.
  4. Vérifications de dérive de config : ajoutez un job CI qui capture la sortie de php --ini=diff ; alertez sur les changements inattendus.
  5. Balayage des métadonnées : taguez les constantes avec dépréciation / unités / feature flags pour alimenter l'outillage interne.
  6. Nettoyage d'extraction aux bords des tableaux : codemod pour remplacer les patterns qui manipulent les pointeurs.
  7. Superposition des gestionnaires d'erreurs : enveloppez les handlers globaux existants avec get_exception_handler() pour l'observabilité (instrumentation Sentry/new relic).
  8. Améliorations i18n : remplacez le code manuel de « colle de liste » par IntlListFormatter ; testez l'autosélection de layout RTL.
  9. Qualité du fuzzy matching : là où du texte multilingue généré par les utilisateurs apparaît (recherche, tagging), benchmarkez la distance graphème vs classique.
  10. Script d'audit runtime : loggez PHP_BUILD_DATE + php --ini=diff chaque jour pour détecter les conteneurs vieillissants.

Mini playground « Show Me »

#[NoDiscard("Hash must be stored or compared")]
function password_hash_safe(string $plain): string {
    return password_hash($plain, PASSWORD_DEFAULT);
}

function sanitize_email(string $raw): string { return strtolower(trim($raw)); }

$request->string('email')
    |> sanitize_email(...)
    |> fn($email) => (strlen($email) > 5 ? $email : throw new InvalidArgumentException('Too short'))
    |> sendEmail(...); // Each stage consumes prior result – no discard.
class Rules {
    public const VALIDATORS = [
        'title' => static function(string $v){ return $v !== ''; },
        'slug'  => static function(string $v){ return (bool) preg_match('/^[a-z0-9-]+$/', $v); },
    ];
}

foreach (Rules::VALIDATORS as $field => $check) {
    if (! $check($data[$field] ?? '')) {
        throw new RuntimeException("Invalid $field");
    }
}

Quand ne pas attraper le jouet brillant

  • Une seule transformation triviale ? Un pipe peut être excessif ; strtolower($x) reste très bien.
  • Closures pleines de contexte ? Méthodes régulières avec injection de dépendances > hacks de closures statiques.
  • Codebase legacy en pleine montée de version ? Introduisez une fonctionnalité à la fois pour éviter la surcharge cognitive.

Récapitulatif du modèle mental

Fonctionnalité Modèle mental central
` >` Enfilage linéaire de valeur ; éliminer imbrication et temps
#[\NoDiscard] Forcer une consommation intentionnelle (utiliser ou ignorer avec (void))
Constantes de closures statiques Registre de stratégie immuable préparé au chargement
Attributs sur constantes Canal de métadonnées first-class pour outils et politiques
array_first()/last() Accès déclaratif et non mutateur aux extrémités
php --ini=diff Lentille de delta de config vs base par défaut
get_exception_handler() Introspecter et envelopper le flux global d'exceptions
Ajouts Intl Intelligence locale intégrée pour remplacer la colle artisanale
Distance graphème Opérations sur caractères perçus humainement plutôt que codepoints bruts
Polissage build & ressources Standardisation et introspection incrémentales

Vibes finales

PHP 8.5 ne hurle pas avec des changements de paradigme : il murmure des victoires ergonomiques implacables. Le combo pipe operator + #[\NoDiscard] poussera à lui seul votre code vers une intention plus claire. Ajoutez des closures au moment de la compilation et des attributs de constantes, et vos frameworks / composants deviennent plus déclaratifs, plus explicites, plus découvrables. Bam bam boom : ship it.

À vous de jouer : Choisissez une fonctionnalité (probablement le pipe), appliquez-la chirurgicalement dans un petit module, mesurez la clarté dans les retours de code review, puis étendez. L'élan bat les réécritures big-bang.

Restez joueurs, refactorez bravement, et, oui, écrivez à vos Taylors quand vous trouvez les moments « Wait, WHAT?! ».

Happy coding.


Commentaires

Boris D. Teoharov

Auteur

Bonjour, je suis Boris

Je ne suis pas écrivain. Je ne suis pas philosophe. Je suis simplement un ingénieur backend bulgare, quelque part entre des files Laravel et des index de centaines de millions de lignes.