PHP 8.5 : La Révolution Silencieuse qui Transforme Votre Code
Imaginez pouvoir écrire $users |> filter(...) |> count(...) au lieu de jongler avec des variables temporaires. Imaginez cloner un objet en modifiant une propriété d’un seul coup. Imaginez que toutes vos erreurs fatales affichent enfin leur vraie origine. Ce n’est pas de la science-fiction : c’est PHP 8.5, et il arrive en novembre 2025.
Dans ma pratique de développement PHP depuis plus de 15 ans, j’ai rarement vu une version mineure apporter autant d’innovations concrètes tout en restant aussi discrète. PHP 8.5 n’est pas une révolution fracassante comme l’était PHP 7 ou 8. C’est une évolution intelligente qui transforme votre quotidien de développeur sans faire de bruit, mais avec une efficacité redoutable.
Introduction
PHP 8.5 représente l’aboutissement de la maturation entamée avec PHP 8.0. Là où PHP 8.0 introduisait les fondations (JIT, union types, attributs), PHP 8.5 peaufine l’édifice avec plus de 40 améliorations ciblées qui touchent chaque aspect du langage : syntaxe, performance, sécurité, internationalisation.
La philosophie de cette version ? Éliminer les frictions du quotidien. Chaque nouveauté répond à une frustration réelle des développeurs : code verbeux, debugging laborieux, APIs obsolètes ou dangereuses, parsing d’URLs fragile. PHP 8.5 ne réinvente pas la roue, il la rend enfin parfaitement ronde.
Cette version illustre parfaitement le principe du “Secure by Design” : les mauvaises pratiques deviennent impossibles ou dépréciées, les bonnes pratiques deviennent naturelles. OPcache obligatoire, fermeture automatique des ressources, API URI standardisée, backtraces systématiques… tout concourt à rendre vos applications plus robustes par défaut.
Plongeons dans ces innovations qui vont transformer votre façon de coder en PHP.
L’Opérateur Pipe : Enfin du Code Fonctionnel Lisible
La Fin des Variables Temporaires Inutiles
Combien de fois avez-vous écrit ceci ?
<span class="nv">$users</span> <span class="o">=</span> <span class="nv">$userRepository</span><span class="o">-></span><span class="nf">fetchUsers</span><span class="p">();</span>
<span class="nv">$admins</span> <span class="o">=</span> <span class="nb">array_filter</span><span class="p">(</span><span class="nv">$users</span><span class="p">,</span> <span class="k">fn</span><span class="p">(</span><span class="nv">$u</span><span class="p">)</span> <span class="o">=></span> <span class="nv">$u</span><span class="o">-></span><span class="nf">isAdmin</span><span class="p">());</span>
<span class="nv">$count</span> <span class="o">=</span> <span class="nb">count</span><span class="p">(</span><span class="nv">$admins</span><span class="p">);</span>
Trois lignes. Deux variables jetables qui polluent votre scope. Un flux de pensée interrompu.
PHP 8.5 introduit l’opérateur pipe |>, inspiré des langages fonctionnels (F#, Elixir, Hack), qui change radicalement la donne :
<span class="nv">$count</span> <span class="o">=</span> <span class="nv">$userRepository</span><span class="o">-></span><span class="nf">fetchUsers</span><span class="p">()</span>
<span class="o">|></span> <span class="p">(</span><span class="k">fn</span><span class="p">(</span><span class="nv">$list</span><span class="p">)</span> <span class="o">=></span> <span class="nb">array_filter</span><span class="p">(</span><span class="nv">$list</span><span class="p">,</span> <span class="k">fn</span><span class="p">(</span><span class="nv">$u</span><span class="p">)</span> <span class="o">=></span> <span class="nv">$u</span><span class="o">-></span><span class="nf">isAdmin</span><span class="p">()))</span>
<span class="o">|></span> <span class="nb">count</span><span class="p">(</span><span class="mf">...</span><span class="p">);</span>
Une seule expression. Le résultat de gauche devient l’argument de droite. Votre intention se lit comme une phrase : “Récupère les utilisateurs, filtre les admins, compte-les.” Pas de bruit, pas de distractions.
Point-Free Style et Expressivité
L’opérateur pipe brille particulièrement avec les first-class callables (la syntaxe ... introduite en PHP 8.1) :
<span class="c1">// Transformation de données élégante</span>
<span class="nv">$finalPrice</span> <span class="o">=</span> <span class="nv">$product</span><span class="o">-></span><span class="nf">getPrice</span><span class="p">()</span>
<span class="o">|></span> <span class="nf">applyDiscount</span><span class="p">(</span><span class="mf">...</span><span class="p">)</span>
<span class="o">|></span> <span class="nf">addTax</span><span class="p">(</span><span class="mf">...</span><span class="p">)</span>
<span class="o">|></span> <span class="nb">round</span><span class="p">(</span><span class="mf">...</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
<span class="c1">// Pipeline de validation</span>
<span class="nv">$isValid</span> <span class="o">=</span> <span class="nv">$input</span>
<span class="o">|></span> <span class="nb">trim</span><span class="p">(</span><span class="mf">...</span><span class="p">)</span>
<span class="o">|></span> <span class="nb">strtolower</span><span class="p">(</span><span class="mf">...</span><span class="p">)</span>
<span class="o">|></span> <span class="nf">validateEmail</span><span class="p">(</span><span class="mf">...</span><span class="p">);</span>
Notez comme le code se lit naturellement de haut en bas, suivant le flux de transformation. C’est l’essence même de la programmation fonctionnelle : composer des fonctions pures pour créer des transformations complexes à partir d’opérations simples.
Les Règles du Jeu
L’opérateur pipe n’est pas magique, il a des règles strictes qui garantissent sa prévisibilité :
- Évaluation gauche → droite : chaque expression est calculée séquentiellement
- Un seul argument : la fonction à droite doit accepter exactement un paramètre
- Précédence cohérente :
|>se place avant les comparaisons, après l’arithmétique - Pas de magie noire : c’est du sucre syntaxique, pas de runtime overhead
<span class="c1">// ❌ ERREUR : fonction multi-arguments</span>
<span class="nv">$result</span> <span class="o">=</span> <span class="nv">$data</span> <span class="o">|></span> <span class="nb">array_map</span><span class="p">(</span><span class="k">fn</span><span class="p">(</span><span class="nv">$x</span><span class="p">)</span> <span class="o">=></span> <span class="nv">$x</span> <span class="o">*</span> <span class="mi">2</span><span class="p">,</span> <span class="mf">...</span><span class="p">);</span> <span class="c1">// Compile error</span>
<span class="c1">// ✅ CORRECT : wrapper ou partial application</span>
<span class="nv">$result</span> <span class="o">=</span> <span class="nv">$data</span> <span class="o">|></span> <span class="p">(</span><span class="k">fn</span><span class="p">(</span><span class="nv">$arr</span><span class="p">)</span> <span class="o">=></span> <span class="nb">array_map</span><span class="p">(</span><span class="k">fn</span><span class="p">(</span><span class="nv">$x</span><span class="p">)</span> <span class="o">=></span> <span class="nv">$x</span> <span class="o">*</span> <span class="mi">2</span><span class="p">,</span> <span class="nv">$arr</span><span class="p">));</span>
Cette contrainte peut sembler limitante, mais elle force une conception claire : chaque étape du pipeline fait une chose et la fait bien.
Cas d’Usage Réels
Dans PrestaShop, imaginez traiter les commandes avec un pipeline :
<span class="nv">$monthlyRevenue</span> <span class="o">=</span> <span class="nc">Order</span><span class="o">::</span><span class="nf">fetchByMonth</span><span class="p">(</span><span class="nv">$month</span><span class="p">)</span>
<span class="o">|></span> <span class="p">(</span><span class="k">fn</span><span class="p">(</span><span class="nv">$orders</span><span class="p">)</span> <span class="o">=></span> <span class="nb">array_filter</span><span class="p">(</span><span class="nv">$orders</span><span class="p">,</span> <span class="k">fn</span><span class="p">(</span><span class="nv">$o</span><span class="p">)</span> <span class="o">=></span> <span class="nv">$o</span><span class="o">-></span><span class="nf">isPaid</span><span class="p">()))</span>
<span class="o">|></span> <span class="p">(</span><span class="k">fn</span><span class="p">(</span><span class="nv">$orders</span><span class="p">)</span> <span class="o">=></span> <span class="nb">array_map</span><span class="p">(</span><span class="k">fn</span><span class="p">(</span><span class="nv">$o</span><span class="p">)</span> <span class="o">=></span> <span class="nv">$o</span><span class="o">-></span><span class="nf">getTotalPaid</span><span class="p">(),</span> <span class="nv">$orders</span><span class="p">))</span>
<span class="o">|></span> <span class="nb">array_sum</span><span class="p">(</span><span class="mf">...</span><span class="p">);</span>
Ou construire un système de prix dynamique :
<span class="nv">$displayPrice</span> <span class="o">=</span> <span class="nv">$basePrice</span>
<span class="o">|></span> <span class="nf">applyCustomerGroupDiscount</span><span class="p">(</span><span class="nv">$customer</span><span class="p">,</span> <span class="mf">...</span><span class="p">)</span>
<span class="o">|></span> <span class="nf">applyVolumeDiscount</span><span class="p">(</span><span class="nv">$quantity</span><span class="p">,</span> <span class="mf">...</span><span class="p">)</span>
<span class="o">|></span> <span class="nf">convertCurrency</span><span class="p">(</span><span class="nv">$targetCurrency</span><span class="p">,</span> <span class="mf">...</span><span class="p">)</span>
<span class="o">|></span> <span class="nf">formatPrice</span><span class="p">(</span><span class="mf">...</span><span class="p">);</span>
Le bénéfice ? Le code devient self-documenting. Chaque étape est explicite, testable indépendamment, et réutilisable. Fini les méthodes à rallonge où on perd le fil.
Clone With : La Fin du Calvaire des Objets Immuables
Le Problème des Readonly Properties
PHP 8.1 a introduit les propriétés readonly, excellentes pour l’immuabilité… jusqu’à ce qu’on veuille créer une variante d’un objet :
<span class="k">readonly</span> <span class="kd">class</span> <span class="nc">User</span> <span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">__construct</span><span class="p">(</span>
<span class="k">public</span> <span class="kt">string</span> <span class="nv">$name</span><span class="p">,</span>
<span class="k">public</span> <span class="kt">string</span> <span class="nv">$email</span><span class="p">,</span>
<span class="k">public</span> <span class="kt">int</span> <span class="nv">$age</span>
<span class="p">)</span> <span class="p">{}</span>
<span class="p">}</span>
<span class="c1">// Comment créer un User avec juste l'email changé ?</span>
<span class="c1">// Option 1 : Factory verbeux</span>
<span class="nv">$newUser</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">User</span><span class="p">(</span><span class="nv">$user</span><span class="o">-></span><span class="n">name</span><span class="p">,</span> <span class="s1">'newemail@example.com'</span><span class="p">,</span> <span class="nv">$user</span><span class="o">-></span><span class="n">age</span><span class="p">);</span>
<span class="c1">// Option 2 : Réflexion (casse readonly, anti-pattern)</span>
<span class="nv">$reflection</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ReflectionProperty</span><span class="p">(</span><span class="nv">$user</span><span class="p">,</span> <span class="s1">'email'</span><span class="p">);</span>
<span class="nv">$reflection</span><span class="o">-></span><span class="nf">setValue</span><span class="p">(</span><span class="nv">$user</span><span class="p">,</span> <span class="s1">'newemail@example.com'</span><span class="p">);</span> <span class="c1">// 😱</span>
Frustrant, non ? L’immuabilité est une excellente pratique, mais la verbosité tuait l’adoption.
La Solution Élégante : clone with
PHP 8.5 résout ce problème avec une syntaxe inspirée (encore) des langages fonctionnels :
<span class="nv">$newUser</span> <span class="o">=</span> <span class="k">clone</span> <span class="nv">$user</span> <span class="n">with</span> <span class="p">[</span><span class="s1">'email'</span> <span class="o">=></span> <span class="s1">'newemail@example.com'</span><span class="p">];</span>
Une ligne. Lisible. Sûr. Le nouvel objet est une copie parfaite de $user, sauf pour email qui prend la nouvelle valeur.
Fonctionnement en Profondeur
La magie opère en plusieurs étapes :
- Clonage classique :
clonecrée une copie shallow de l’objet - Appel de
__clone(): si défini, votre logique de clonage custom s’exécute - Surcharge des propriétés : le tableau
withécrase les valeurs spécifiées - Respect des hooks : si vous utilisez les property hooks (PHP 8.4+), ils sont invoqués
<span class="k">readonly</span> <span class="kd">class</span> <span class="nc">Money</span> <span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">__construct</span><span class="p">(</span>
<span class="k">public</span> <span class="kt">float</span> <span class="nv">$amount</span><span class="p">,</span>
<span class="k">public</span> <span class="kt">string</span> <span class="nv">$currency</span>
<span class="p">)</span> <span class="p">{}</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">__clone</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Logique custom si nécessaire</span>
<span class="k">echo</span> <span class="s2">"Cloning money object</span><span class="se">\n</span><span class="s2">"</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nv">$euros</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Money</span><span class="p">(</span><span class="mf">100.0</span><span class="p">,</span> <span class="s1">'EUR'</span><span class="p">);</span>
<span class="nv">$dollars</span> <span class="o">=</span> <span class="k">clone</span> <span class="nv">$euros</span> <span class="n">with</span> <span class="p">[</span><span class="s1">'currency'</span> <span class="o">=></span> <span class="s1">'USD'</span><span class="p">,</span> <span class="s1">'amount'</span> <span class="o">=></span> <span class="mf">120.0</span><span class="p">];</span>
<span class="c1">// Output: "Cloning money object"</span>
<span class="c1">// $dollars->amount === 120.0</span>
<span class="c1">// $dollars->currency === 'USD'</span>
Clone as First-Class Callable
Cerise sur le gâteau : clone devient un callable de première classe :
<span class="nv">$users</span> <span class="o">=</span> <span class="p">[</span><span class="cm">/* ... */</span><span class="p">];</span>
<span class="nv">$userCopies</span> <span class="o">=</span> <span class="nb">array_map</span><span class="p">(</span><span class="k">clone</span><span class="p">(</span><span class="mf">...</span><span class="p">),</span> <span class="nv">$users</span><span class="p">);</span>
<span class="c1">// Ou avec des modifications uniformes</span>
<span class="nv">$anonymized</span> <span class="o">=</span> <span class="nb">array_map</span><span class="p">(</span>
<span class="k">fn</span><span class="p">(</span><span class="nv">$user</span><span class="p">)</span> <span class="o">=></span> <span class="k">clone</span> <span class="nv">$user</span> <span class="n">with</span> <span class="p">[</span><span class="s1">'email'</span> <span class="o">=></span> <span class="s1">'redacted@example.com'</span><span class="p">],</span>
<span class="nv">$users</span>
<span class="p">);</span>
Impact sur l’architecture : Les patterns comme Event Sourcing, Value Objects, ou Copy-on-Write deviennent triviaux à implémenter. Plus besoin de builders complexes ou de méthodes withX() à rallonge (pattern withers de PSR-7).
OPcache Obligatoire : Performance Garantie pour Tous
La Fin d’une Anomalie
Historiquement, OPcache était une extension optionnelle. On pouvait compiler PHP sans. Grosse erreur.
Pourquoi ? Parce que sans OPcache, PHP recompile votre code à chaque requête. Imaginez recompiler un monolithe e-commerce à chaque visite : catastrophique.
Pourtant, des environnements de production tournaient encore sans OPcache, par méconnaissance ou configuration défaillante. PHP 8.5 met fin à cette absurdité.
OPcache : Toujours Là, Toujours Actif
Depuis PHP 8.5, OPcache est :
- Compilé par défaut : impossible de le désactiver à la compilation
- Activé par défaut :
opcache.enable=1dès l’installation - Partie intégrante du moteur : pas une extension tierce
Conséquence ? Toutes les applications PHP bénéficient automatiquement :
- Réduction drastique du CPU : le bytecode est mis en cache
- Temps de réponse divisés par 3 à 10 selon la complexité
- Scalabilité améliorée : moins de charge par requête
Ce Que Ça Change pour Vous
Si vous développez sous Docker, Kubernetes, ou déployez sur des plateformes cloud :
<span class="c"># Avant PHP 8.5 : configuration manuelle obligatoire</span>
<span class="k">RUN </span>docker-php-ext-install opcache
<span class="k">RUN </span><span class="nb">echo</span> <span class="s2">"opcache.enable=1"</span> <span class="o">>></span> /usr/local/etc/php/conf.d/opcache.ini
<span class="k">RUN </span><span class="nb">echo</span> <span class="s2">"opcache.memory_consumption=128"</span> <span class="o">>></span> /usr/local/etc/php/conf.d/opcache.ini
<span class="c"># Avec PHP 8.5 : OPcache déjà là, optimisez juste les paramètres</span>
<span class="k">RUN </span><span class="nb">echo</span> <span class="s2">"opcache.memory_consumption=256"</span> <span class="o">>></span> /usr/local/etc/php/conf.d/opcache.ini
Pour PrestaShop ? C’est énorme. PrestaShop charge des centaines de classes par requête. Sans OPcache, chaque requête recompile des mégaoctets de code. Avec OPcache obligatoire, zéro risque de config foireuse = performance garantie en production.
JIT : Gains Incrémentaux Mais Réels
Le JIT (Just-In-Time compiler), introduit en PHP 8.0, continue d’évoluer. PHP 8.5 apporte :
- 5-10% d’amélioration sur les applications web standards
- Optimisations mémoire : empreinte réduite dans certains cas
- Meilleur profiling : diagnostics internes affinés
Le JIT reste surtout bénéfique pour le calcul intensif (traitement d’images, parsing XML/JSON massif, algorithmes complexes). Pour l’e-commerce classique (I/O-bound), l’impact est modéré mais cumulatif.
Backtraces sur Erreurs Fatales : Le Debugging Révolutionné
Le Cauchemar du “Fatal Error in Unknown on Line 42”
Qui n’a jamais vu ceci en production ?
Fatal error: Allowed memory size exhausted in /var/www/classes/Product.php on line 1247
Parfait. Vous savez où c’est mort. Mais pourquoi ? Quelle chaîne d’appels a conduit à ce désastre ?
Avant PHP 8.5, vous deviez :
- Tenter de reproduire localement (bonne chance)
- Ajouter du logging partout (puissant mais chronophage)
- Activer Xdebug en prod (🔥 performance hit)
La Solution : Backtraces Automatiques
PHP 8.5 résout ce problème élégamment : toute erreur fatale génère maintenant un backtrace complet.
<span class="nb">register_shutdown_function</span><span class="p">(</span><span class="k">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nv">$error</span> <span class="o">=</span> <span class="nb">error_get_last</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$error</span> <span class="o">&&</span> <span class="nv">$error</span><span class="p">[</span><span class="s1">'type'</span><span class="p">]</span> <span class="o">===</span> <span class="kc">E_ERROR</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Nouveauté PHP 8.5 : clé 'trace' disponible !</span>
<span class="nv">$trace</span> <span class="o">=</span> <span class="nv">$error</span><span class="p">[</span><span class="s1">'trace'</span><span class="p">]</span> <span class="o">??</span> <span class="p">[];</span>
<span class="c1">// Logger la pile complète</span>
<span class="nb">error_log</span><span class="p">(</span><span class="s2">"Fatal error stack trace:</span><span class="se">\n</span><span class="s2">"</span> <span class="mf">.</span> <span class="nb">print_r</span><span class="p">(</span><span class="nv">$trace</span><span class="p">,</span> <span class="kc">true</span><span class="p">));</span>
<span class="c1">// Envoyer à votre système de monitoring</span>
<span class="nc">Sentry</span><span class="o">::</span><span class="nf">captureException</span><span class="p">(</span><span class="k">new</span> <span class="nc">ErrorException</span><span class="p">(</span>
<span class="nv">$error</span><span class="p">[</span><span class="s1">'message'</span><span class="p">],</span>
<span class="mi">0</span><span class="p">,</span>
<span class="nv">$error</span><span class="p">[</span><span class="s1">'type'</span><span class="p">],</span>
<span class="nv">$error</span><span class="p">[</span><span class="s1">'file'</span><span class="p">],</span>
<span class="nv">$error</span><span class="p">[</span><span class="s1">'line'</span><span class="p">]</span>
<span class="p">),</span> <span class="p">[</span><span class="s1">'stacktrace'</span> <span class="o">=></span> <span class="nv">$trace</span><span class="p">]);</span>
<span class="p">}</span>
<span class="p">});</span>
Résultat ? En cas d’erreur fatale, vous voyez immédiatement :
Fatal error: Memory exhausted in Product.php:1247
Stack trace:
#0 CartController.php(89): Product->getImages()
#1 FrontController.php(156): CartController->displayCart()
#2 Dispatcher.php(412): FrontController->run()
#3 index.php(28): Dispatcher::dispatch()
Ah ! Le contrôleur de panier charge trop d’images. Diagnostic en 10 secondes au lieu de 2 heures.
Impact sur les Frameworks
Pour PrestaShop, Symfony, Laravel… c’est un game-changer :
- Monitoring amélioré : intégration native avec Sentry, Rollbar, Bugsnag
- Debug en production : tracer les causes profondes sans perturber le service
- Formation des juniors : comprendre les erreurs devient pédagogique
Extension URI : Adieu parse_url(), Bonjour Sécurité
Les Failles Cachées de parse_url()
parse_url() a servi loyalement pendant 25 ans. Mais elle a un secret honteux : elle est cassée.
Quelques exemples qui vous feront froid dans le dos :
<span class="c1">// Confusion avec caractères Unicode similaires</span>
<span class="nb">parse_url</span><span class="p">(</span><span class="s1">'http://раураl.com/account'</span><span class="p">);</span> <span class="c1">// Cyrillic 'а' vs Latin 'a'</span>
<span class="c1">// Peut bypasser des whitelist mal implémentées</span>
<span class="c1">// Parsing incohérent avec les navigateurs</span>
<span class="nb">parse_url</span><span class="p">(</span><span class="s1">'http://user@evil.com:user@legit.com/'</span><span class="p">);</span>
<span class="c1">// PHP : host = "evil.com", les navigateurs : host = "legit.com"</span>
<span class="c1">// Redirections ouvertes possibles</span>
<span class="c1">// Encodage ambigu</span>
<span class="nb">parse_url</span><span class="p">(</span><span class="s1">'http://example.com/%2F../admin'</span><span class="p">);</span>
<span class="c1">// Path normalization non standard</span>
Ces quirks ont causé des failles de sécurité réelles : redirections ouvertes, bypasses de filtres, injections SSRF.
L’Extension URI : Standard et Robuste
PHP 8.5 introduit une nouvelle extension ext/uri avec deux classes immuables :
<span class="kn">use</span> <span class="nc">Uri\Rfc3986\Uri</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">Uri\WhatWg\Url</span><span class="p">;</span>
<span class="c1">// Parsing RFC 3986 (URIs génériques)</span>
<span class="nv">$uri</span> <span class="o">=</span> <span class="nc">Uri</span><span class="o">::</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'https://user:pass@example.com:8080/path?q=1#frag'</span><span class="p">);</span>
<span class="k">echo</span> <span class="nv">$uri</span><span class="o">-></span><span class="nf">getScheme</span><span class="p">();</span> <span class="c1">// "https"</span>
<span class="k">echo</span> <span class="nv">$uri</span><span class="o">-></span><span class="nf">getHost</span><span class="p">();</span> <span class="c1">// "example.com"</span>
<span class="k">echo</span> <span class="nv">$uri</span><span class="o">-></span><span class="nf">getPort</span><span class="p">();</span> <span class="c1">// 8080</span>
<span class="k">echo</span> <span class="nv">$uri</span><span class="o">-></span><span class="nf">getPath</span><span class="p">();</span> <span class="c1">// "/path"</span>
<span class="k">echo</span> <span class="nv">$uri</span><span class="o">-></span><span class="nf">getQuery</span><span class="p">();</span> <span class="c1">// "q=1"</span>
<span class="k">echo</span> <span class="nv">$uri</span><span class="o">-></span><span class="nf">getFragment</span><span class="p">();</span> <span class="c1">// "frag"</span>
<span class="c1">// Parsing WHATWG (URLs web modernes)</span>
<span class="nv">$url</span> <span class="o">=</span> <span class="nc">Url</span><span class="o">::</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'https://example.com/search?q=php 8.5'</span><span class="p">);</span>
<span class="c1">// Normalisation automatique</span>
<span class="k">echo</span> <span class="nv">$url</span><span class="o">-></span><span class="nf">getSearchParams</span><span class="p">()</span><span class="o">-></span><span class="nf">get</span><span class="p">(</span><span class="s1">'q'</span><span class="p">);</span> <span class="c1">// "php 8.5" (décodé)</span>
Manipulation Immuable et Sûre
Les objets Uri et Url sont immuables (pattern similaire à PSR-7) :
<span class="nv">$base</span> <span class="o">=</span> <span class="nc">Uri</span><span class="o">::</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'https://api.example.com/v1'</span><span class="p">);</span>
<span class="nv">$endpoint</span> <span class="o">=</span> <span class="nv">$base</span>
<span class="o">-></span><span class="nf">withPath</span><span class="p">(</span><span class="s1">'/v2/users'</span><span class="p">)</span>
<span class="o">-></span><span class="nf">withQuery</span><span class="p">(</span><span class="s1">'filter=active&limit=10'</span><span class="p">);</span>
<span class="k">echo</span> <span class="nv">$endpoint</span><span class="p">;</span> <span class="c1">// "https://api.example.com/v2/users?filter=active&limit=10"</span>
<span class="k">echo</span> <span class="nv">$base</span><span class="p">;</span> <span class="c1">// "https://api.example.com/v1" (inchangé)</span>
Résolution d’URLs relatives (enfin !) :
<span class="nv">$base</span> <span class="o">=</span> <span class="nc">Uri</span><span class="o">::</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'https://example.com/blog/'</span><span class="p">);</span>
<span class="nv">$relative</span> <span class="o">=</span> <span class="nc">Uri</span><span class="o">::</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'../images/logo.webp'</span><span class="p">);</span>
<span class="nv">$resolved</span> <span class="o">=</span> <span class="nv">$base</span><span class="o">-></span><span class="nf">resolve</span><span class="p">(</span><span class="nv">$relative</span><span class="p">);</span>
<span class="k">echo</span> <span class="nv">$resolved</span><span class="p">;</span> <span class="c1">// "https://example.com/images/logo.webp"</span>
Sécurité par Design
Contrairement à parse_url(), l’extension URI :
- Suit les standards à la lettre (RFC 3986, WHATWG URL)
- Normalise les encodages : pas d’ambiguïté
%2Fvs/ - Valide strictement : URLs malformées = exception
- Cohérence avec les navigateurs : comportement prévisible
<span class="c1">// Validation stricte</span>
<span class="k">try</span> <span class="p">{</span>
<span class="nv">$malicious</span> <span class="o">=</span> <span class="nc">Uri</span><span class="o">::</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'http://раураl.com'</span><span class="p">);</span> <span class="c1">// Cyrillic lookalike</span>
<span class="c1">// Exception levée si caractères non-ASCII sans encoding</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nc">ValueError</span> <span class="nv">$e</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Handle gracefully</span>
<span class="p">}</span>
<span class="c1">// Comparaison canonique</span>
<span class="nv">$url1</span> <span class="o">=</span> <span class="nc">Url</span><span class="o">::</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'https://Example.COM/Path'</span><span class="p">);</span>
<span class="nv">$url2</span> <span class="o">=</span> <span class="nc">Url</span><span class="o">::</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'https://example.com/Path'</span><span class="p">);</span>
<span class="k">echo</span> <span class="nv">$url1</span><span class="o">-></span><span class="nf">equals</span><span class="p">(</span><span class="nv">$url2</span><span class="p">);</span> <span class="c1">// true (normalisation automatique)</span>
Pour PrestaShop ? Valider les URLs de redirection, parser les webhooks de paiement, gérer les liens deep dans l’app mobile… toutes ces opérations deviennent robustes et sûres par défaut.
Nouvelles Fonctions Utilitaires : Les Petits Bonheurs
array_first() et array_last()
Combien de fois avez-vous écrit reset($array) ou end($array) en pestant contre l’effet de bord sur le pointeur interne ?
<span class="c1">// Avant PHP 8.5 : verbeux et fragile</span>
<span class="nv">$firstUser</span> <span class="o">=</span> <span class="nb">reset</span><span class="p">(</span><span class="nv">$users</span><span class="p">);</span> <span class="c1">// Modifie le pointeur interne 😡</span>
<span class="nv">$lastUser</span> <span class="o">=</span> <span class="nb">end</span><span class="p">(</span><span class="nv">$users</span><span class="p">);</span> <span class="c1">// Pareil</span>
<span class="c1">// PHP 8.5 : simple et sans surprise</span>
<span class="nv">$firstUser</span> <span class="o">=</span> <span class="nf">array_first</span><span class="p">(</span><span class="nv">$users</span><span class="p">);</span>
<span class="nv">$lastUser</span> <span class="o">=</span> <span class="nf">array_last</span><span class="p">(</span><span class="nv">$users</span><span class="p">);</span>
<span class="c1">// Tableau vide ? Pas de warning, juste null</span>
<span class="nv">$empty</span> <span class="o">=</span> <span class="p">[];</span>
<span class="nv">$first</span> <span class="o">=</span> <span class="nf">array_first</span><span class="p">(</span><span class="nv">$empty</span><span class="p">);</span> <span class="c1">// null (pas d'erreur)</span>
Cas d’usage PrestaShop :
<span class="c1">// Récupérer la première image d'un produit</span>
<span class="nv">$coverImage</span> <span class="o">=</span> <span class="nf">array_first</span><span class="p">(</span><span class="nv">$product</span><span class="o">-></span><span class="nf">getImages</span><span class="p">());</span>
<span class="c1">// Dernière commande d'un client</span>
<span class="nv">$latestOrder</span> <span class="o">=</span> <span class="nf">array_last</span><span class="p">(</span><span class="nv">$customer</span><span class="o">-></span><span class="nf">getOrders</span><span class="p">());</span>
get_error_handler() et get_exception_handler()
Avant, récupérer le handler actif nécessitait des contorsions :
<span class="c1">// Méthode "sale" pré-8.5</span>
<span class="nv">$oldHandler</span> <span class="o">=</span> <span class="nb">set_error_handler</span><span class="p">(</span><span class="k">fn</span><span class="p">()</span> <span class="o">=></span> <span class="kc">null</span><span class="p">);</span>
<span class="nb">restore_error_handler</span><span class="p">();</span>
<span class="c1">// $oldHandler contient le handler précédent</span>
PHP 8.5 simplifie :
<span class="nv">$currentErrorHandler</span> <span class="o">=</span> <span class="nf">get_error_handler</span><span class="p">();</span>
<span class="nv">$currentExceptionHandler</span> <span class="o">=</span> <span class="nf">get_exception_handler</span><span class="p">();</span>
<span class="c1">// null si aucun handler défini</span>
<span class="c1">// callable sinon</span>
Utilité ? Pour les frameworks qui veulent enchaîner les handlers :
<span class="kd">class</span> <span class="nc">ErrorMiddleware</span> <span class="p">{</span>
<span class="k">private</span> <span class="nv">$previousHandler</span><span class="p">;</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">register</span><span class="p">()</span> <span class="p">{</span>
<span class="nv">$this</span><span class="o">-></span><span class="n">previousHandler</span> <span class="o">=</span> <span class="nf">get_error_handler</span><span class="p">();</span>
<span class="nb">set_error_handler</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="nv">$errno</span><span class="p">,</span> <span class="nv">$errstr</span><span class="p">,</span> <span class="nv">$errfile</span><span class="p">,</span> <span class="nv">$errline</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Notre logique custom</span>
<span class="nv">$this</span><span class="o">-></span><span class="nf">logError</span><span class="p">(</span><span class="nv">$errno</span><span class="p">,</span> <span class="nv">$errstr</span><span class="p">,</span> <span class="nv">$errfile</span><span class="p">,</span> <span class="nv">$errline</span><span class="p">);</span>
<span class="c1">// Déléguer au handler précédent si existe</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="n">previousHandler</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="n">previousHandler</span><span class="p">)(</span><span class="nv">$errno</span><span class="p">,</span> <span class="nv">$errstr</span><span class="p">,</span> <span class="nv">$errfile</span><span class="p">,</span> <span class="nv">$errline</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">false</span><span class="p">;</span> <span class="c1">// Comportement par défaut</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
PHP_BUILD_DATE et PHP_BUILD_PROVIDER
Diagnostiquer les différences entre environnements devient trivial :
<span class="k">echo</span> <span class="s2">"PHP Version: "</span> <span class="mf">.</span> <span class="kc">PHP_VERSION</span> <span class="mf">.</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">;</span>
<span class="k">echo</span> <span class="s2">"Built on: "</span> <span class="mf">.</span> <span class="kc">PHP_BUILD_DATE</span> <span class="mf">.</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">;</span>
<span class="k">echo</span> <span class="s2">"Provider: "</span> <span class="mf">.</span> <span class="kc">PHP_BUILD_PROVIDER</span> <span class="mf">.</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">;</span>
<span class="c1">// Output exemple :</span>
<span class="c1">// PHP Version: 8.5.0</span>
<span class="c1">// Built on: Nov 21 2025 14:32:10</span>
<span class="c1">// Provider: Ubuntu</span>
Pourquoi c’est utile ? Parce que deux serveurs avec PHP 8.5 peuvent avoir des comportements différents selon :
- Les extensions compilées
- Les patches du distributeur (Ubuntu vs Alpine vs official)
- Les flags de compilation (debug, ZTS, etc.)
Ces constantes permettent de détecter rapidement les écarts en production.
Attributs Enrichis : Métaprogrammation Simplifiée
#[NoDiscard] : Forcer l’Utilisation du Retour
Certaines fonctions ne doivent JAMAIS être appelées sans traiter leur retour :
<span class="c1">#[\NoDiscard]</span>
<span class="k">function</span> <span class="n">executePayment</span><span class="p">(</span><span class="kt">Order</span> <span class="nv">$order</span><span class="p">):</span> <span class="kt">PaymentResult</span> <span class="p">{</span>
<span class="c1">// Logique critique</span>
<span class="k">return</span> <span class="k">new</span> <span class="nc">PaymentResult</span><span class="p">(</span><span class="nv">$success</span><span class="p">,</span> <span class="nv">$transactionId</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// ❌ Ceci déclenchera un warning</span>
<span class="nf">executePayment</span><span class="p">(</span><span class="nv">$order</span><span class="p">);</span> <span class="c1">// Warning: Result of executePayment() is not used</span>
<span class="c1">// ✅ Correct</span>
<span class="nv">$result</span> <span class="o">=</span> <span class="nf">executePayment</span><span class="p">(</span><span class="nv">$order</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$result</span><span class="o">-></span><span class="nf">isSuccess</span><span class="p">())</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="p">}</span>
<span class="c1">// ✅ Ou ignorer explicitement</span>
<span class="p">(</span><span class="n">void</span><span class="p">)</span> <span class="nf">executePayment</span><span class="p">(</span><span class="nv">$order</span><span class="p">);</span> <span class="c1">// Cast void = "je sais ce que je fais"</span>
Cas d’usage : validation, transactions DB, appels API, opérations de fichiers. Tout ce qui peut échouer silencieusement.
#[Override] sur les Propriétés
Éviter les typos et divergences dans les héritages :
<span class="kd">class</span> <span class="nc">BaseProduct</span> <span class="p">{</span>
<span class="k">public</span> <span class="kt">string</span> <span class="nv">$name</span><span class="p">;</span>
<span class="k">public</span> <span class="kt">float</span> <span class="nv">$price</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nc">DiscountedProduct</span> <span class="kd">extends</span> <span class="nc">BaseProduct</span> <span class="p">{</span>
<span class="c1">#[\Override]</span>
<span class="k">public</span> <span class="kt">float</span> <span class="nv">$price</span><span class="p">;</span> <span class="c1">// ✅ OK, existe dans BaseProduct</span>
<span class="c1">#[\Override]</span>
<span class="k">public</span> <span class="kt">float</span> <span class="nv">$discount</span><span class="p">;</span> <span class="c1">// ❌ Compile Error: no such property in parent</span>
<span class="p">}</span>
Pour PrestaShop ? Le système d’overrides de modules devient plus sûr. Si le core change une propriété, l’attribut #[Override] détecte immédiatement la rupture.
#[Deprecated] pour les Traits
Marquer un trait obsolète :
<span class="c1">#[\Deprecated("Use NewHelperTrait instead", since: "2.5.0")]</span>
<span class="kd">trait</span> <span class="nc">OldHelperTrait</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nc">MyClass</span> <span class="p">{</span>
<span class="kn">use</span> <span class="nc">OldHelperTrait</span><span class="p">;</span> <span class="c1">// Warning: OldHelperTrait is deprecated</span>
<span class="p">}</span>
Aide les équipes à gérer la dette technique progressivement.
Internationalisation Avancée avec ext/intl
IntlListFormatter : Listes Naturelles
Afficher des listes respectueusement selon la locale :
<span class="nv">$formatter</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">IntlListFormatter</span><span class="p">(</span><span class="s1">'fr_FR'</span><span class="p">,</span> <span class="nc">IntlListFormatter</span><span class="o">::</span><span class="no">TYPE_AND</span><span class="p">);</span>
<span class="k">echo</span> <span class="nv">$formatter</span><span class="o">-></span><span class="nf">format</span><span class="p">([</span><span class="s1">'pommes'</span><span class="p">,</span> <span class="s1">'bananes'</span><span class="p">,</span> <span class="s1">'oranges'</span><span class="p">]);</span>
<span class="c1">// "pommes, bananes et oranges"</span>
<span class="nv">$formatter</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">IntlListFormatter</span><span class="p">(</span><span class="s1">'en_US'</span><span class="p">,</span> <span class="nc">IntlListFormatter</span><span class="o">::</span><span class="no">TYPE_OR</span><span class="p">);</span>
<span class="k">echo</span> <span class="nv">$formatter</span><span class="o">-></span><span class="nf">format</span><span class="p">([</span><span class="s1">'red'</span><span class="p">,</span> <span class="s1">'green'</span><span class="p">,</span> <span class="s1">'blue'</span><span class="p">]);</span>
<span class="c1">// "red, green, or blue"</span>
PrestaShop ? Afficher les attributs de produit proprement :
<span class="nv">$attributes</span> <span class="o">=</span> <span class="nv">$product</span><span class="o">-></span><span class="nf">getAttributeNames</span><span class="p">();</span> <span class="c1">// ['Taille', 'Couleur', 'Matière']</span>
<span class="nv">$formatted</span> <span class="o">=</span> <span class="p">(</span><span class="k">new</span> <span class="nc">IntlListFormatter</span><span class="p">(</span><span class="nv">$locale</span><span class="p">))</span><span class="o">-></span><span class="nf">format</span><span class="p">(</span><span class="nv">$attributes</span><span class="p">);</span>
<span class="c1">// FR: "Taille, Couleur et Matière"</span>
<span class="c1">// EN: "Size, Color, and Material"</span>
locale_is_right_to_left() : Support RTL
Détecter automatiquement les langues RTL :
<span class="k">if</span> <span class="p">(</span><span class="nf">locale_is_right_to_left</span><span class="p">(</span><span class="s1">'ar_SA'</span><span class="p">))</span> <span class="p">{</span>
<span class="k">echo</span> <span class="s1">'<body dir="rtl">'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">echo</span> <span class="s1">'<body dir="ltr">'</span><span class="p">;</span>
<span class="p">}</span>
Fini les mappings hardcodés ! PHP connaît les locales RTL (arabe, hébreu, persan…).
grapheme_levenshtein() : Distance Unicode
Calculer la similarité entre chaînes avec accents et emojis :
<span class="c1">// levenshtein() classique (octets)</span>
<span class="k">echo</span> <span class="nb">levenshtein</span><span class="p">(</span><span class="s1">'café'</span><span class="p">,</span> <span class="s1">'cafe'</span><span class="p">);</span> <span class="c1">// 2 (é = 2 octets UTF-8)</span>
<span class="c1">// grapheme_levenshtein() (graphèmes)</span>
<span class="k">echo</span> <span class="nf">grapheme_levenshtein</span><span class="p">(</span><span class="s1">'café'</span><span class="p">,</span> <span class="s1">'cafe'</span><span class="p">);</span> <span class="c1">// 1 (é = 1 graphème)</span>
<span class="c1">// Avec emojis</span>
<span class="k">echo</span> <span class="nf">grapheme_levenshtein</span><span class="p">(</span><span class="s1">'hello👋'</span><span class="p">,</span> <span class="s1">'hello'</span><span class="p">);</span> <span class="c1">// 1 (pas 4 !)</span>
Recherche tolérante aux fautes : suggérer “téléphone” quand l’utilisateur tape “telephone”.
Cookies Partitionnés (CHIPS) : Confidentialité Moderne
Le Problème des Cookies Tiers
Les cookies tiers (cross-site) permettent le tracking publicitaire. Les navigateurs les bloquent de plus en plus.
CHIPS (Cookies Having Independent Partitioned State) est le nouveau standard : les cookies tiers sont isolés par top-level site.
Implémentation en PHP 8.5
<span class="c1">// Cookie partitionné</span>
<span class="nb">setcookie</span><span class="p">(</span><span class="s1">'tracking'</span><span class="p">,</span> <span class="s1">'value'</span><span class="p">,</span> <span class="p">[</span>
<span class="s1">'expires'</span> <span class="o">=></span> <span class="nb">time</span><span class="p">()</span> <span class="o">+</span> <span class="mi">3600</span><span class="p">,</span>
<span class="s1">'path'</span> <span class="o">=></span> <span class="s1">'/'</span><span class="p">,</span>
<span class="s1">'secure'</span> <span class="o">=></span> <span class="kc">true</span><span class="p">,</span>
<span class="s1">'httponly'</span> <span class="o">=></span> <span class="kc">true</span><span class="p">,</span>
<span class="s1">'samesite'</span> <span class="o">=></span> <span class="s1">'None'</span><span class="p">,</span>
<span class="s1">'partitioned'</span> <span class="o">=></span> <span class="kc">true</span> <span class="c1">// 🆕 PHP 8.5</span>
<span class="p">]);</span>
<span class="c1">// Cookie de session partitionné</span>
<span class="nb">session_set_cookie_params</span><span class="p">([</span>
<span class="s1">'lifetime'</span> <span class="o">=></span> <span class="mi">0</span><span class="p">,</span>
<span class="s1">'path'</span> <span class="o">=></span> <span class="s1">'/'</span><span class="p">,</span>
<span class="s1">'secure'</span> <span class="o">=></span> <span class="kc">true</span><span class="p">,</span>
<span class="s1">'httponly'</span> <span class="o">=></span> <span class="kc">true</span><span class="p">,</span>
<span class="s1">'samesite'</span> <span class="o">=></span> <span class="s1">'None'</span><span class="p">,</span>
<span class="s1">'partitioned'</span> <span class="o">=></span> <span class="kc">true</span> <span class="c1">// 🆕</span>
<span class="p">]);</span>
<span class="nb">session_start</span><span class="p">();</span>
Quand l’utiliser ? Si votre app PrestaShop est embeddée en iframe sur un autre domaine (ex: widget de panier), les cookies partitionnés garantissent le fonctionnement sans compromettre la vie privée.
Dépréciations : Nettoyer le Passé
PHP 8.5 déprrécie une cinquantaine d’éléments. Voici les plus impactants :
Fermeture Manuelle de Ressources
<span class="c1">// ❌ Déprécié en 8.5</span>
<span class="nb">curl_close</span><span class="p">(</span><span class="nv">$ch</span><span class="p">);</span>
<span class="nb">imagedestroy</span><span class="p">(</span><span class="nv">$img</span><span class="p">);</span>
<span class="nb">finfo_close</span><span class="p">(</span><span class="nv">$finfo</span><span class="p">);</span>
<span class="nb">xml_parser_free</span><span class="p">(</span><span class="nv">$parser</span><span class="p">);</span>
<span class="c1">// ✅ Rien à faire, destructeur automatique</span>
<span class="c1">// Les objets se nettoient tout seuls</span>
Raison : Depuis PHP 7.4, les ressources sont des objets. La fermeture manuelle est redondante et source d’erreurs (double-free, leaks).
Casts Non Canoniques
<span class="c1">// ❌ Déprécié</span>
<span class="nv">$bool</span> <span class="o">=</span> <span class="p">(</span><span class="n">boolean</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
<span class="nv">$int</span> <span class="o">=</span> <span class="p">(</span><span class="n">integer</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
<span class="nv">$float</span> <span class="o">=</span> <span class="p">(</span><span class="n">double</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
<span class="nv">$str</span> <span class="o">=</span> <span class="p">(</span><span class="n">binary</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
<span class="c1">// ✅ Utiliser les formes standard</span>
<span class="nv">$bool</span> <span class="o">=</span> <span class="p">(</span><span class="n">bool</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
<span class="nv">$int</span> <span class="o">=</span> <span class="p">(</span><span class="n">int</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
<span class="nv">$float</span> <span class="o">=</span> <span class="p">(</span><span class="n">float</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
<span class="nv">$str</span> <span class="o">=</span> <span class="p">(</span><span class="n">string</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
Opérateur Backtick
<span class="c1">// ❌ Déprécié</span>
<span class="nv">$output</span> <span class="o">=</span> <span class="sb">`ls -la`</span><span class="p">;</span>
<span class="c1">// ✅ Utiliser shell_exec() explicitement</span>
<span class="nv">$output</span> <span class="o">=</span> <span class="nb">shell_exec</span><span class="p">(</span><span class="s1">'ls -la'</span><span class="p">);</span>
Sécurité : Le backtick était une source de confusion et d’injections shell. shell_exec() est explicite.
__sleep() et __wakeup()
<span class="c1">// ⚠️ Soft-dépréciation : encore supporté mais découragé</span>
<span class="kd">class</span> <span class="nc">User</span> <span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">__sleep</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">[</span><span class="s1">'name'</span><span class="p">,</span> <span class="s1">'email'</span><span class="p">];</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">__wakeup</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Init logic</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// ✅ Préférer __serialize() / __unserialize()</span>
<span class="kd">class</span> <span class="nc">User</span> <span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">__serialize</span><span class="p">():</span> <span class="kt">array</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">[</span><span class="s1">'name'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="n">name</span><span class="p">,</span> <span class="s1">'email'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="n">email</span><span class="p">];</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">__unserialize</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$data</span><span class="p">):</span> <span class="kt">void</span> <span class="p">{</span>
<span class="nv">$this</span><span class="o">-></span><span class="n">name</span> <span class="o">=</span> <span class="nv">$data</span><span class="p">[</span><span class="s1">'name'</span><span class="p">];</span>
<span class="nv">$this</span><span class="o">-></span><span class="n">email</span> <span class="o">=</span> <span class="nv">$data</span><span class="p">[</span><span class="s1">'email'</span><span class="p">];</span>
<span class="p">}</span>
<span class="p">}</span>
Avantage : __serialize() supporte les objets imbriqués et évite les pièges de __sleep().
PrestaShop et PHP 8.5 : Un Combo Gagnant
Gains de Performance Concrets
PrestaShop 8.x charge environ 150-300 classes par requête front-office. Avec OPcache obligatoire :
- Temps de compilation : 0 (bytecode caché)
- Temps de réponse : -30% en moyenne
- Capacité serveur : +50% de requêtes/seconde
Les optimisations JIT (+5-10%) s’ajoutent pour les pages complexes (listing produits, filtres).
Sécurité Renforcée
PrestaShop manipule :
- URLs de redirection (redirections ouvertes possibles)
- Webhooks de paiement (parsing critiques)
- Deep links mobiles
L’extension URI standardise et sécurise ces traitements :
<span class="c1">// Avant : parse_url() fragile</span>
<span class="nv">$redirect</span> <span class="o">=</span> <span class="nv">$_GET</span><span class="p">[</span><span class="s1">'redirect'</span><span class="p">];</span>
<span class="nv">$parsed</span> <span class="o">=</span> <span class="nb">parse_url</span><span class="p">(</span><span class="nv">$redirect</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$parsed</span><span class="p">[</span><span class="s1">'host'</span><span class="p">]</span> <span class="o">===</span> <span class="s1">'monshop.com'</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">header</span><span class="p">(</span><span class="s2">"Location: </span><span class="nv">$redirect</span><span class="s2">"</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// ⚠️ Contournable avec Unicode, encodages ambigus</span>
<span class="c1">// Après : Uri sécurisée</span>
<span class="k">try</span> <span class="p">{</span>
<span class="nv">$uri</span> <span class="o">=</span> <span class="nc">Uri\Rfc3986\Uri</span><span class="o">::</span><span class="nf">parse</span><span class="p">(</span><span class="nv">$_GET</span><span class="p">[</span><span class="s1">'redirect'</span><span class="p">]);</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$uri</span><span class="o">-></span><span class="nf">getHost</span><span class="p">()</span> <span class="o">===</span> <span class="s1">'monshop.com'</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">header</span><span class="p">(</span><span class="s2">"Location: "</span> <span class="mf">.</span> <span class="nv">$uri</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nc">ValueError</span> <span class="nv">$e</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// URL malformée = rejet</span>
<span class="p">}</span>
Système d’Overrides Sécurisé
PrestaShop permet aux modules d’override des classes core. L’attribut #[Override] détecte les incompatibilités :
<span class="c1">// Dans un module</span>
<span class="kd">class</span> <span class="nc">Product</span> <span class="kd">extends</span> <span class="nc">ProductCore</span> <span class="p">{</span>
<span class="c1">#[\Override]</span>
<span class="k">public</span> <span class="kt">string</span> <span class="nv">$reference</span><span class="p">;</span> <span class="c1">// ✅ Existe dans ProductCore</span>
<span class="c1">#[\Override]</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">getPrice</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// ✅ Méthode parente existe</span>
<span class="k">return</span> <span class="k">parent</span><span class="o">::</span><span class="nf">getPrice</span><span class="p">()</span> <span class="o">*</span> <span class="mf">0.9</span><span class="p">;</span> <span class="c1">// 10% discount</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Si le core change, compilation error immédiat</span>
<span class="c1">// Au lieu d'un bug silencieux en prod</span>
Internationalisation Améliorée
PrestaShop supporte 75+ langues. Les améliorations Intl facilitent :
<span class="c1">// Affichage d'attributs de produit</span>
<span class="nv">$attributes</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'XL'</span><span class="p">,</span> <span class="s1">'Rouge'</span><span class="p">,</span> <span class="s1">'Coton'</span><span class="p">];</span>
<span class="nv">$formatter</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">IntlListFormatter</span><span class="p">(</span><span class="nv">$customer</span><span class="o">-></span><span class="nf">getLocale</span><span class="p">());</span>
<span class="k">echo</span> <span class="nv">$formatter</span><span class="o">-></span><span class="nf">format</span><span class="p">(</span><span class="nv">$attributes</span><span class="p">);</span>
<span class="c1">// FR: "XL, Rouge et Coton"</span>
<span class="c1">// AR: "XL، أحمر و قطن" (ordre RTL automatique)</span>
<span class="c1">// Détection RTL pour thèmes</span>
<span class="k">if</span> <span class="p">(</span><span class="nf">locale_is_right_to_left</span><span class="p">(</span><span class="nv">$language</span><span class="o">-></span><span class="n">locale</span><span class="p">))</span> <span class="p">{</span>
<span class="nv">$smarty</span><span class="o">-></span><span class="nf">assign</span><span class="p">(</span><span class="s1">'text_direction'</span><span class="p">,</span> <span class="s1">'rtl'</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Recherche tolérante</span>
<span class="nv">$query</span> <span class="o">=</span> <span class="s1">'telefone'</span><span class="p">;</span> <span class="c1">// faute de frappe</span>
<span class="nv">$suggestions</span> <span class="o">=</span> <span class="nb">array_filter</span><span class="p">(</span><span class="nv">$products</span><span class="p">,</span> <span class="k">function</span><span class="p">(</span><span class="nv">$p</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$query</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nf">grapheme_levenshtein</span><span class="p">(</span><span class="nv">$query</span><span class="p">,</span> <span class="nv">$p</span><span class="o">-></span><span class="n">name</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">});</span>
<span class="c1">// Suggère "téléphone" même avec accents manquants</span>
Migration et Compatibilité
Checklist de Migration
1. Vérifier les dépréciations
<span class="c"># Analyser le code avec PHPStan</span>
composer require <span class="nt">--dev</span> phpstan/phpstan
vendor/bin/phpstan analyse <span class="nt">--level</span><span class="o">=</span>8 src/
<span class="c"># Ou Rector pour automatiser les fixes</span>
composer require <span class="nt">--dev</span> rector/rector
vendor/bin/rector process src/ <span class="nt">--dry-run</span>
2. Remplacer les fonctions dépréciées
Déprécié Remplacer par curl_close($ch) Rien (auto) imagedestroy($img) Rien (auto) (boolean) $x (bool) $x cmd shell_exec('cmd') __sleep() / __wakeup() __serialize() / __unserialize() #### 3. Tester les edge cases
<span class="c1">// Backtraces : vérifier vos handlers</span>
<span class="nb">register_shutdown_function</span><span class="p">(</span><span class="k">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nv">$error</span> <span class="o">=</span> <span class="nb">error_get_last</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$error</span> <span class="o">&&</span> <span class="k">isset</span><span class="p">(</span><span class="nv">$error</span><span class="p">[</span><span class="s1">'trace'</span><span class="p">]))</span> <span class="p">{</span>
<span class="c1">// Nouveau en 8.5, adapter votre code</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="c1">// URI : remplacer parse_url()</span>
<span class="c1">// Avant</span>
<span class="nv">$parts</span> <span class="o">=</span> <span class="nb">parse_url</span><span class="p">(</span><span class="nv">$url</span><span class="p">);</span>
<span class="c1">// Après</span>
<span class="k">try</span> <span class="p">{</span>
<span class="nv">$uri</span> <span class="o">=</span> <span class="nc">Uri\Rfc3986\Uri</span><span class="o">::</span><span class="nf">parse</span><span class="p">(</span><span class="nv">$url</span><span class="p">);</span>
<span class="nv">$parts</span> <span class="o">=</span> <span class="p">[</span>
<span class="s1">'scheme'</span> <span class="o">=></span> <span class="nv">$uri</span><span class="o">-></span><span class="nf">getScheme</span><span class="p">(),</span>
<span class="s1">'host'</span> <span class="o">=></span> <span class="nv">$uri</span><span class="o">-></span><span class="nf">getHost</span><span class="p">(),</span>
<span class="c1">// ...</span>
<span class="p">];</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nc">ValueError</span> <span class="nv">$e</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Gestion d'erreur propre</span>
<span class="p">}</span>
4. Optimiser avec les nouveautés
- Remplacer
reset() / end()pararray_first() / array_last() - Adopter l’opérateur pipe pour les transformations de données
- Utiliser
clone withpour les objets immuables
Compatibilité avec PHP 8.4
PHP 8.5 est rétrocompatible avec 8.4. Votre code 8.4 tourne sur 8.5 (sauf dépréciations qui émettent des warnings).
Stratégie de migration progressive :
- Dev/Staging : PHP 8.5 dès décembre 2025
- Tests intensifs : janvier-février 2026
- Prod : mars 2026 (après PHP 8.5.1-8.5.2 pour les bugfixes)
Environnement Docker
<span class="k">FROM</span><span class="s"> php:8.5-fpm-alpine</span>
<span class="c"># OPcache déjà là, juste configurer</span>
<span class="k">RUN </span><span class="nb">echo</span> <span class="s2">"opcache.memory_consumption=256"</span> <span class="o">>></span> /usr/local/etc/php/conf.d/opcache.ini <span class="o">&&</span> <span class="se">\
</span> <span class="nb">echo</span> <span class="s2">"opcache.max_accelerated_files=20000"</span> <span class="o">>></span> /usr/local/etc/php/conf.d/opcache.ini <span class="o">&&</span> <span class="se">\
</span> <span class="nb">echo</span> <span class="s2">"opcache.validate_timestamps=0"</span> <span class="o">>></span> /usr/local/etc/php/conf.d/opcache.ini
<span class="c"># Extension URI : incluse par défaut</span>
<span class="c"># Intl : installer si non présent</span>
<span class="k">RUN </span>apk add <span class="nt">--no-cache</span> icu-dev <span class="o">&&</span> <span class="se">\
</span> docker-php-ext-install intl
<span class="c"># Autres extensions classiques</span>
<span class="k">RUN </span>docker-php-ext-install pdo_mysql gd zip
Conclusion
PHP 8.5 n’est pas une révolution spectaculaire. C’est une évolution chirurgicale qui corrige 20 ans de frustrations mineures accumulées.
L’opérateur pipe transforme votre code verbeux en pipelines expressifs. Le clone with rend l’immuabilité enfin praticable. OPcache obligatoire garantit la performance partout. Les backtraces sur erreurs fatales divisent vos temps de debug par 10. L’extension URI élimine une classe entière de failles de sécurité.
Chaque nouveauté répond à un pain point réel. Pas de gadgets, pas de hype. Juste des solutions pragmatiques pour écrire du code plus sûr, plus rapide, plus maintenable.
Pour PrestaShop et les applications e-commerce, PHP 8.5 est un no-brainer : gains de performance mesurables, sécurité renforcée, internationalisation facilitée, debugging amélioré. Les quelques heures d’adaptation (dépréciations à corriger) sont largement compensées par les bénéfices à long terme.
Mon conseil ? Testez PHP 8.5 dès sa sortie en novembre 2025. Adoptez-le en prod après les premières versions correctives (8.5.1-8.5.2, probablement janvier 2026). Vous ne reviendrez pas en arrière.
PHP 8.5 prouve une chose : la maturité d’un langage ne se mesure pas à ses révolutions, mais à sa capacité d’évolution continue sans casser l’existant. Et de ce point de vue, PHP est au sommet de son art.
Article publié le 16 novembre 2025 par Nicolas Dabène - Expert PHP & PrestaShop avec 15+ ans d’expérience