Table des matières :
- Cadrer une migration PHP 8.5 sur PrestaShop : compatibilité réelle, périmètre, risques
- Mettre en place un environnement de test reproductible (local + CI) en PHP 8.5
- PHPStan : rendre le code “prouvable” avant de toucher à la prod
- PHPUnit : verrouiller le comportement fonctionnel avant l’upgrade PHP
- Stratégie de remédiation : réduire le risque, corriger vite, livrer proprement
Cadrer une migration PHP 8.5 sur PrestaShop : compatibilité réelle, périmètre, risques
Une migration PHP 8.5 sur PrestaShop ne se “fait” pas, elle se prouve. La difficulté n’est pas PHP lui‑même, c’est l’écosystème : cœur PrestaShop (legacy + Symfony), thème, overrides, modules tiers, et intégrations (ERP, paiement, logistique). En 2026, la plupart des régressions viennent moins d’une incompatibilité directe avec le core que d’un code applicatif qui a survécu à plusieurs générations de PHP (type juggling, propriétés dynamiques, @ partout, SQL construit à la volée, etc.). Avant même de compiler une image PHP 8.5, vous avez besoin d’une cartographie : versions exactes (PrestaShop, PHP, Symfony, MySQL/MariaDB), liste des modules actifs, overrides, hooks critiques (checkout, paiement, panier, prix).
Le piège classique : confondre “ça s’installe” avec “c’est compatible”. Une boutique peut afficher la home en PHP 8.5 et casser uniquement sur des chemins métier (création de commande, recalcul de frais de port, synchronisation ERP) — donc hors de votre parcours de test “à la main”.
Check-list de cadrage (pratique, rapide, actionnable)
- Versionner la réalité (pas des suppositions) :
php -v,php -m(extensions réellement chargées)composer show -D(dépendances exactes)composer check-platform-reqs(écarts d’extensions / versions)grep -n '"php"' composer.json(contrainte PHP déclarée)- Inventorier ce qui peut casser :
- overrides (souvent le plus fragile)
- modules de paiement / taxes / transport (chemins critiques)
- jobs CRON / imports (régressions “invisibles” en front)
- scripts “one-shot” (ex. réindexation, migration de données)
- Définir un périmètre testable :
- parcours commande (1 produit, 1 remise, 1 transporteur, 1 paiement)
- tunnel B2B si applicable (TVA intra, règles de prix, “paiement à 30 jours”)
- back-office : création produit, règle de panier, génération facture/avoir
Pré‑requis minimaux côté process : un staging cloné (code + média + DB) et un pipeline qui rejoue les checks à l’identique. Si vous n’avez pas ça, vous êtes dans la migration “au clic” et vous finirez dans une situation type page blanche / 500. Pour cadrer proprement :
- verrouillez les versions (Composer, sous‑modules, modules vendus en ZIP) ;
- geler l’état DB et médias (snapshot) ;
- isoler ce qui est “core” vs “custom” (thème et modules maison).
Sur les risques, soyez explicite : l’upgrade PHP casse souvent des hypothèses silencieuses (types retournés, warnings devenus erreurs, fonctions internes plus strictes). Le plus efficace est d’activer une stratégie “fail fast” : en staging, mettez display_errors=0 mais logguez tout (error_reporting=E_ALL, y compris E_DEPRECATED).
Quelques catégories de régressions qui reviennent (et qui se traitent très bien avec une approche outillée) :
| Zone | Symptôme en migration | Cause fréquente | Impact typique |
|---|---|---|---|
| Typage / retours | TypeError, ArgumentCountError |
signatures implicites, null non prévu, retours tantôt int tantôt string |
checkout/paiement cassé |
| Dépréciations | logs saturés, comportements divergents | anciennes APIs Symfony/Doctrine, propriétés dynamiques | dette qui explose en staging |
| Legacy “permissif” | warnings devenus bloquants | accès tableau sans clé, count() sur null, conversions implicites |
pages “aléatoires” en erreur |
| Intégrations | timeouts, erreurs API | clients HTTP vieillissants, TLS/ciphers, extensions manquantes | perte commandes / synchro |
Les erreurs fatales typiques se diagnostiquent vite avec des logs applicatifs et PHP-FPM ; si vous n’avez pas encore une méthode reproductible côté logs, commencez par lire Erreur 500 PrestaShop : 10 causes fréquentes et solutions rapides et PrestaShop page blanche : diagnostiquer l’erreur fatale PHP rapidement.
Enfin, gardez une contrainte “terrain” : le choix d’une version PHP n’est pas qu’un choix technique, c’est aussi un choix de support sécurité. Le planning de migration est plus simple quand vous alignez votre cible sur les versions officiellement supportées côté PHP (correctifs de sécurité). Référence utile : php.net — Supported versions
Mettre en place un environnement de test reproductible (local + CI) en PHP 8.5
Si vous migrez vers PHP 8.5, votre premier livrable n’est pas un serveur : c’est un environnement identique entre laptop, CI et staging. En pratique : conteneurs (Docker/Compose) ou VM, un composer.lock committé, et une matrice CI qui exécute au moins : installation dépendances, analyse statique, tests, et un smoke test HTTP. Pour garder un modèle stable, vous pouvez forcer Composer à raisonner “comme si” vous étiez en 8.5 via le champ config.platform (utile quand la CI n’a pas encore l’image PHP 8.5 finalisée) :
{
"config": {
"platform": {
"php": "8.5.0"
}
}
}
Côté images, vous voulez un set minimal : php:8.5-fpm, un Nginx/Apache, et une base (MySQL 8 / MariaDB 11). Pour éviter les faux positifs, épinglez aussi les extensions requises (intl, gd, zip, soap selon modules) et activez OPcache pour se rapprocher du comportement prod.
Une bonne pratique simple : déclarer explicitement les extensions attendues par votre projet (et pas seulement “ça marche chez moi”). Exemple de commande utile à lancer dans vos conteneurs et en CI :
php -v
php -m | sort
composer check-platform-reqs
Si votre infra de référence est déjà structurée (Nginx + PHP‑FPM + Redis + CDN), recyclez la même approche de staging ; l’article Hébergement PrestaShop géré : migration sans coupure, staging et maintenance donne une logique de workflow, sans bullshit.
Docker/Compose : viser le “suffisamment proche prod”
Sans tomber dans un docker-compose.yml géant, le minimum utile est : même PHP-FPM, même serveur web, même base, et des variables d’environnement explicites (timezone, memorylimit, uploadmax_filesize). Exemple volontairement compact :
services:
php:
image: php:8.5-fpm
volumes:
- ./:/var/www/html
environment:
TZ: Europe/Paris
db:
image: mariadb:11
environment:
MARIADB_DATABASE: prestashop
MARIADB_USER: prestashop
MARIADB_PASSWORD: prestashop
MARIADB_ROOT_PASSWORD: root
Deux points qui évitent des surprises en migration :
- Timezone / locales : certains calculs (périodes de promo, exports) changent si la timezone n’est pas la même entre dev/staging/prod.
- Extensions conditionnelles : un module peut activer une feature seulement si
soapouintlest présent — et casser silencieusement si l’extension manque.
Enfin, mettez un garde-fou CI : une matrice de versions PHP (ex. 8.2 / 8.4 / 8.5) pour mesurer l’écart, pas uniquement “ça passe en 8.5”.
Exemple GitHub Actions (simplifié) :
name: qa
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php: ['8.2', '8.4', '8.5']
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: composer:v2
extensions: intl, gd, mbstring, zip
- run: composer install --no-interaction --prefer-dist
- run: vendor/bin/phpstan analyse -c phpstan.neon
- run: vendor/bin/phpunit -c phpunit.xml
Ajoutez ensuite un smoke test très simple (qui évite de valider “vert” alors que Nginx/PHP-FPM répond en 500) : un curl sur une URL de staging (ou un conteneur web dans la CI) qui doit renvoyer 200 sur 2–3 pages clés (home, produit, panier). Ce n’est pas un test E2E complet, mais c’est un excellent détecteur de “page blanche” liée à un include fatal.
Pour aller plus loin (tests sur énormément de versions/environnements), vous pouvez vous inspirer de ChaosLab : tester modules PrestaShop sur 23 environnements PHP. L’idée est la même : automatiser la preuve de compatibilité, au lieu de discuter sur des suppositions.
PHPStan : rendre le code “prouvable” avant de toucher à la prod
PHPStan est l’arme la plus rentable pour préparer une migration PHP 8.5, parce qu’il vous force à matérialiser des incohérences de types et des chemins d’exécution morts. La définition officielle est sans ambiguïté : « PHPStan focuses on finding errors in your code without actually running it. » (PHPStan). Sur PrestaShop, la friction vient du legacy : beaucoup de mixed, de tableaux associatifs sans forme, et des classes historiques qui vivent hors du conteneur Symfony. Votre objectif n’est pas d’atteindre “level max” dès le jour 1 ; votre objectif est de créer un signal stable et actionnable.
Installation standard côté module ou projet (dans un repo Composer) :
composer require --dev phpstan/phpstan phpstan/phpstan-symfony
Ensuite, un phpstan.neon pragmatique (niveau intermédiaire pour démarrer, exclusions, bootstrap) :
includes:
- vendor/phpstan/phpstan-symfony/extension.neon
parameters:
level: 6
paths:
- src
- modules/monmodule/src
excludePaths:
- var
- cache
- vendor
bootstrapFiles:
- tools/phpstan/bootstrap.php
treatPhpDocTypesAsCertain: false
Le point clé est le bootstrap : PHPStan n’exécute pas votre application, mais il doit comprendre l’autoload et certaines constantes. Dans un contexte PrestaShop, vous pouvez charger l’autoloader et définir quelques constantes attendues par vos classes (sans inclure tout config.inc.php, sinon vous déclenchez DB + config prod). Exemple minimal :
<?php
// tools/phpstan/bootstrap.php
require __DIR__ . '/../vendor/autoload.php';
define('_PS_MODE_DEV_', true);
// Définissez uniquement ce dont VOTRE code a besoin.
Ce que PHPStan vous fait gagner, concrètement (cas typique PrestaShop)
Dans beaucoup de modules, on trouve des patterns du style :
- une valeur récupérée via
Tools::getValue()(souventstring|null|mixed) - des comparaisons laxistes (
==,empty()) - puis un passage direct à une API qui attend
intoustringnon vide
En PHP “tolérant”, ça “marche” jusqu’au jour où un changement de version rend le comportement plus strict et vous vous retrouvez avec des TypeError en prod.
La remédiation “propre” est souvent simple et rentable :
- typer les entrées en bordure (cast + validation)
- documenter les tableaux via array shapes dans PHPDoc quand c’est pertinent
- isoler les accès “legacy” derrière une classe d’adaptation
Exemple de règle d’or pour rendre les analyses et les migrations moins risquées : “un cast n’est pas une validation”. Un (int) peut transformer une entrée vide en 0 et déclencher un chemin métier non prévu (référence produit 0, id_carrier 0, etc.). PHPStan vous aide à repérer ces zones où votre code “fait comme si” tout était conforme.
Baseline : éviter l’effet “10 000 erreurs”
Pour un code legacy très bruité, utilisez une baseline (fichier d’erreurs connues) afin d’éviter l’effet “10 000 erreurs” qui tue toute adoption. PHPStan fournit --generate-baseline : vous figez l’existant, puis vous faites baisser la baseline au fil des tickets.
Un workflow qui marche bien en migration :
- Générer la baseline sur PHP actuel (ex. 8.2).
- Verrouiller la baseline en CI (interdire qu’elle augmente).
- Passer la CI en PHP 8.5 : toute nouvelle erreur devient une tâche de migration prioritaire.
- Réduire la baseline progressivement, module par module.
C’est exactement la mécanique qui rend la migration PHP 8.5 industrialisable : vous empêchez les régressions typées d’entrer, pendant que vous remontez progressivement la qualité.
PHPUnit : verrouiller le comportement fonctionnel avant l’upgrade PHP
L’analyse statique ne garantit pas le comportement. Pour ça, vous avez besoin de tests. PHPUnit le résume clairement : « PHPUnit is a programmer-oriented testing framework for PHP. » (PHPUnit Manual). Sur PrestaShop, le cœur du problème n’est pas “écrire un test”, c’est de choisir le bon niveau : unit tests purs (rapides), tests d’intégration (DB + services), et éventuellement tests end‑to‑end (Playwright/Cypress) pour le checkout. Pour une migration PHP 8.5, l’approche efficace est : peu de tests, mais sur les chemins critiques.
Une façon pragmatique de sélectionner ces chemins critiques : prenez vos logs (ou votre analytics) et listez ce qui génère le plus de chiffre, d’incidents, ou de tickets support :
- paiement principal (PSP #1) + fallback
- transporteur principal + règles de frais (paliers, franco)
- règles de panier (codes promo, cadeaux, cumul)
- calcul TVA (B2C/B2B, selon votre contexte)
Pour un module, structure minimale recommandée : src/ (code), tests/Unit/ (unit), tests/Integration/ (intégration), plus un phpunit.xml. Exemple :
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/bootstrap.php" colors="true" cacheDirectory="var/.phpunit.cache">
<testsuites>
<testsuite name="unit">
<directory>tests/Unit</directory>
</testsuite>
<testsuite name="integration">
<directory>tests/Integration</directory>
</testsuite>
</testsuites>
</phpunit>
Le tests/bootstrap.php doit être explicite sur ce qu’il charge. Pour des unit tests, ne bootez pas PrestaShop : vous testez une classe en isolation, vous mockez ce qui doit l’être. Pour des tests d’intégration, vous pouvez charger un PrestaShop “staging” mais uniquement avec une configuration dédiée (DB de test, clés distinctes). Exemple de bootstrap d’intégration (à adapter, et jamais avec les secrets prod) :
<?php
// tests/bootstrap.php
require __DIR__ . '/../vendor/autoload.php';
$psRoot = getenv('PS_ROOT');
if ($psRoot && is_dir($psRoot)) {
define('_PS_ROOT_DIR_', $psRoot);
require $psRoot . '/config/config.inc.php';
}
Rendre les tests “CI-friendly” (sinon ils seront ignorés)
Pour que ça tienne en CI, vous devez gérer l’état : base vide initialisée via dump, ou base reconstruite à chaque job. Les tests qui modifient la DB doivent être isolés (transaction rollback si possible, sinon reset dataset).
Quelques règles simples qui augmentent fortement la fiabilité :
- Données de test minimales : 1 devise, 1 langue, 2 produits, 1 transporteur, 1 règle de taxe (si nécessaire).
- Aucun état partagé entre tests d’intégration (éviter qu’un test prépare “par chance” la DB pour le suivant).
- Désactiver les dépendances externes (API paiement, ERP) en tests : mock côté HTTP, ou environnement “sandbox”.
- Fixer
TZet locale (cf. export, arrondis, dates de facture).
Si vous êtes déjà dans une logique de déploiement automatisé, branchez ces tests dans votre pipeline (voir PrestaShop : modernisation et reprise de boutique avec déploiements automatisés et PrestaShop CI/CD : générer des démos à la demande en minutes).
Stratégie de remédiation : réduire le risque, corriger vite, livrer proprement
Une migration PHP 8.5 réussie ressemble à une suite de petits deltas, pas à un “big bang”. Commencez par faire tourner PHPStan + PHPUnit sur la version PHP actuelle (ex. 8.2/8.3 selon votre prod), puis exécutez exactement les mêmes checks en 8.5. La différence de sorties (nouvelles erreurs, nouveaux warnings, tests rouges) vous donne la roadmap réelle.
Une manière efficace de piloter : transformer la migration en backlog avec catégories (core/custom/modules/infra) et priorisation par risque business (checkout > BO > pages CMS).
Si vous avez des overrides, traitez‑les comme du code applicatif à risque : l’override casse la modularité et rend les upgrades plus fragiles (voir PrestaShop 9.2 : bonnes pratiques d’override et tests de compatibilité et Module PrestaShop sans override : méthode de cadrage, tests et livraison du code).
Ensuite, mettez en place des “quality gates” simples mais non négociables :
- PHPStan ne doit pas augmenter son nombre d’erreurs (baseline stable),
- PHPUnit doit rester vert,
- pas de nouvelles dépréciations bloquantes dans les logs en staging.
Pour rendre ça pilotable, vous pouvez formaliser une grille de sortie (exemple) :
| Garde-fou | Mesure | Seuil de passage |
|---|---|---|
| Analyse statique | delta erreurs PHPStan vs baseline | 0 nouvelle erreur |
| Tests | suites unit + intégration | 100% vert |
| Dépréciations | E_DEPRECATED en staging sur parcours checkout |
0 nouvelle dépréciation introduite par vos commits |
| Exploitation | HTTP 5xx sur smoke test | 0 sur 3 pages clés |
| Réversibilité | rollback appliqué sur staging | rollback validé (temps + procédure) |
Pour les dépréciations Symfony, si votre partie Back Office Symfony est significative, regardez l’approche standard de symfony/phpunit-bridge (détection/filtrage de dépréciations) ; même si PrestaShop n’embrasse pas toujours les meilleures pratiques Symfony partout, vous pouvez au moins éviter de faire entrer de nouvelles dépréciations “gratuites”.
Mini-scenario réaliste (ce qui évite les “migrations surprises”)
- Boutique PrestaShop avec 40 modules, dont 6 critiques (paiement, transport, ERP, avis).
- Vous passez PHPStan : 1200 erreurs historiques → baseline.
- En PHP 8.5 : +35 erreurs nouvelles, dont 8 sur le module de paiement (types + méthodes obsolètes).
- Vous corrigez d’abord les 8 paiement + 5 panier/prix, vous laissez les 22 restantes dans le backlog (zones non critiques).
- Vous ajoutez 6 tests PHPUnit (2 unit sur calcul de frais, 4 intégration sur création commande + facture).
- Résultat : vous pouvez livrer la migration en production avec une preuve, pas avec une “impression”.
Enfin, n’oubliez pas la partie exploitation : une montée de version PHP change parfois les performances (GC, opcache) et la surface d’erreurs runtime. Mesurez avant/après (TTFB, CPU, erreurs 5xx) et automatisez l’observabilité (logs PHP‑FPM, slowlog, APM).
- OPcache : réglez-le comme en production (sinon vous validez des comportements qui changent dès que le cache est actif).
- Logs exploitables : assurez-vous d’avoir le couple correlation id (ou équivalent) + contexte (idcart/idorder) pour relier une erreur PHP à un impact métier.
Pour la méthodologie de preuve terrain (pas des impressions), lisez Audit performance PrestaShop : méthode reproductible et preuves terrain et, si vous devez refactorer des hotspots avant la migration, Optimisation code source : audit performance PHP/Symfony avec profilage.
Le résultat attendu est concret : un pipeline qui prouve que votre code et vos modules restent corrects en PHP 8.5, et un plan de corrections borné — pas une nuit blanche sur un serveur prod.




