PrestaShop 9.0 : migration Swift Mailer vers Symfony Mailer

Guide pratique : migration de Swift Mailer vers Symfony Mailer pour PrestaShop 9.0 — impacts techniques, correspondances API, configuration DSN, tests, Messenger et plan de rollback.

Écran d'ordinateur affichant un schéma de migration et du code.

Table des matières :

  1. Swift Mailer hors-jeu : implications concrètes en PrestaShop 9.0
  2. Flux d’envoi email dans PrestaShop : ce qui reste stable, ce qui casse
  3. Migrer un module : du monde Swift_* vers Email/MailerInterface
  4. Configuration des transports : DSN, SMTP, TLS, secrets et pièges de prod
  5. Fiabiliser : tests, envoi asynchrone (Messenger), observabilité et plan de rollback

Swift Mailer hors-jeu : implications concrètes en PrestaShop 9.0

La migration Swift Mailer vers Symfony Mailer n’est pas un « refacto cosmétique » : elle est imposée par l’état de maintenance de Swift Mailer et par l’alignement de PrestaShop 9 sur l’écosystème Symfony moderne (Symfony 6.4 côté core). Swift Mailer est aujourd’hui un cul-de-sac technique : dépendances figées, intégrations obsolètes, et un coût croissant à maintenir des contournements (plugins, spools, transports custom) dans des boutiques qui, elles, doivent continuer à évoluer.

Le point clé est la dette de dépendance. Le dépôt officiel Swift Mailer affiche explicitement sa fin de vie : « Swiftmailer is deprecated and no longer maintained. Use Symfony Mailer instead. » (SwiftMailer GitHub, README) — source : https://github.com/swiftmailer/swiftmailer. Concrètement, dès que vous migrez une boutique vers PrestaShop 9.0+, tout module qui instancie directement Swift_Message, Swift_Attachment, Swift_SmtpTransport ou dépend de swiftmailer/swiftmailer via Composer devient un candidat à la casse (erreurs de classe introuvable, conflits de versions, ou comportements divergents au runtime).

Côté Symfony, le basculement a été acté depuis longtemps : « The Mailer component is the next generation of the Swiftmailer library. » (Symfony Blog, “New in Symfony 4.3: Mailer”) — source : https://symfony.com/blog/new-in-symfony-4-3-mailer-component. PrestaShop 9 ne fait que rattraper ce train. En 2026, vous n’avez pas vraiment le choix : soit vous migrez vos modules, soit vous maintenez un fork de dépendances email (avec le risque de sécurité et d’incompatibilité que ça implique).

Pour éviter les surprises, faites un inventaire “anti‑Swift” avant même d’ouvrir un IDE :

  • Recherche de symboles dans vos modules : Swift_ / swiftmailer (y compris dans les vendors embarqués).
  • Vérification de composer.json/composer.lock : présence de swiftmailer/swiftmailer ou d’un bundle historique lié à Swift.
  • Recherche d’usages “indirects” : libs d’envoi de facture PDF, connecteurs ERP/CRM, modules marketplace (certains ont longtemps pris Swift en dépendance sans passer par Mail::send()).
  • Contrôle des overrides : un override de Mail ou d’une classe de notification qui manipule l’objet message est typiquement l’endroit où la migration casse “silencieusement”.

Mini‑scénario très courant en production : une boutique ajoute un BCC “compta@…” sur les confirmations de commande via un override qui récupère un objet Swift_Message. Après migration PS9, l’override charge toujours… mais le BCC ne part plus (ou une exception est levée). Résultat : la commande est validée côté client, mais le back‑office perd une copie d’email utilisée comme preuve opérationnelle. La correction se fait, mais après que l’incident ait généré du support et des écarts de process.

Flux d’envoi email dans PrestaShop : ce qui reste stable, ce qui casse

Même si le moteur d’envoi change, le flux fonctionnel PrestaShop reste globalement identique : génération d’un sujet + rendu des templates /mails/ (HTML et TXT) + envoi via transport configuré (PHP mail, SMTP, etc.). Historiquement, la classe Mail::send() masque une partie de cette complexité et offre des garde‑fous utiles (langue, multiboutique, surcharges de templates, variables Smarty/assign). Sur PrestaShop 9, le core peut conserver cette façade, mais les objets transport/message sous‑jacents ne sont plus les mêmes.

Là où ça casse pour les modules, c’est surtout dans les points d’extension “non officiels” : hooks ou overrides qui manipulent le message Swift avant envoi (ajout de headers, BCC, pièces jointes), custom spools, et intégrations SMTP qui court‑circuitent la conf PrestaShop. Si vous aviez un module qui “patchait” l’email en interceptant un objet Swift_Message, attendez‑vous à devoir le réécrire : Symfony Mailer manipule des objets Symfony\Component\Mime\Email (et des Address) avec une API différente et des contraintes plus strictes sur la validité des en‑têtes.

Pour clarifier l’impact, voici une lecture “ce qui bouge / ce qui ne bouge pas” côté intégration :

Élément En général stable en PS9 À risque lors de la migration
Templates /mails/<lang>/ et variables PrestaShop Oui Non (sauf custom/override exotique)
Appels Mail::send() dans les modules Oui (souvent) Faible à moyen (selon extensions)
Overrides/hacks qui touchent directement Swift_Message Non Élevé
Ajout de pièces jointes / inline images via API Swift Non Élevé
Spool/queue “maison” basé sur Swift Non Élevé
Configuration SMTP via BO uniquement En partie Moyen (mapping vers DSN / secrets)

Avant de toucher au code, verrouillez la méthode : migration en staging reproduisant la prod (mêmes versions PHP, mêmes extensions, mêmes secrets, même MTA/SMTP). Si vous n’avez pas un environnement fidèle, vous allez confondre des erreurs de migration mailer avec des divergences d’infra. Référence utile côté site : Staging PrestaShop : créer un environnement Docker fidèle à la production et, pour la bascule globale, Mise à jour PrestaShop : checklist sauvegarde, pré‑production et tests.

Point “terrain” (souvent oublié) : en France / UE, les emails transactionnels sont fréquemment utilisés comme traces (SAV, litiges, confirmations). Si vous logguez des contenus complets d’emails (HTML inclus) pour debug, vous augmentez la surface de données personnelles (RGPD). Profitez de la migration pour cadrer : logs structurés, minimisés, et rotation/TTL cohérents.

Migrer un module : du monde Swift_* vers Email/MailerInterface

Le pattern le plus propre en PrestaShop 9 est de ne plus dépendre de Swift Mailer et de viser l’API Symfony : Symfony\Component\Mailer\MailerInterface + Symfony\Component\Mime\Email (ou TemplatedEmail si vous utilisez Twig). Pour un module, ça veut dire : (1) supprimer les imports Swift, (2) arrêter d’instancier des transports SMTP à la main, (3) récupérer le mailer depuis le conteneur Symfony (autowiring), et (4) gérer les exceptions TransportExceptionInterface.

<?php

namespace MyVendor\MyModule\Service;

use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Address;

final class OrderNotifier
{
    public function __construct(private readonly MailerInterface $mailer) {}

    public function send(string $to, string $subject, string $html, string $text = ''): void
    {
        $email = (new Email())
            ->from(new Address('[email protected]', 'Boutique'))
            ->to($to)
            ->subject($subject)
            ->html($html);

        if ($text !== '') {
            $email->text($text);
        }

        try {
            $this->mailer->send($email);
        } catch (TransportExceptionInterface $e) {
            // À minima : log structuré + remontée applicative.
            throw $e;
        }
    }
}

Dans un module PrestaShop “moderne”, l’autowiring se fait généralement en déclarant votre service dans le conteneur Symfony. Selon votre structure, cela ressemble souvent à une déclaration de service (ex. config/services.yml) :

services:
  MyVendor\MyModule\Service\OrderNotifier:
    autowire: true
    autoconfigure: true

Le remplacement des pièces jointes/inline content se fait aussi différemment. Swift Mailer utilisait Swift_Attachment::fromPath() et embed(). Symfony Mailer/Mime fournit des méthodes de plus haut niveau : attachFromPath() et embedFromPath(). Exemple :

$email
  ->attachFromPath(_PS_ROOT_DIR_.'/var/invoices/'.$pdf, 'facture.pdf', 'application/pdf')
  ->embedFromPath(_PS_THEME_DIR_.'assets/logo.png', 'logo');

Pour les headers personnalisés (tracking interne, routage, intégration ESP), la logique est aussi différente : au lieu de getHeaders()->addTextHeader(...) côté Swift, on passe par les en‑têtes MIME de Symfony :

$email->getHeaders()->addTextHeader('X-Module', 'mymodule');
$email->getHeaders()->addTextHeader('X-Order-Id', (string) $orderId);

Table de correspondance utile pour “dé‑Swiftiser” vite une base de code (sans prétendre couvrir tous les cas) :

Swift Mailer Symfony Mailer / Mime
Swift_Message Symfony\Component\Mime\Email
Swift_Attachment::fromPath() $email->attachFromPath()
embed() / Swift_Image $email->embedFromPath()
setBody($html, 'text/html') $email->html($html)
addPart($txt, 'text/plain') $email->text($txt)
setReplyTo() $email->replyTo()
addBcc() $email->bcc()
Swift_SmtpTransport::newInstance() configuration DSN + MailerInterface

Ensuite, le vrai sujet PrestaShop n’est pas “envoyer un mail”, c’est “envoyer un mail avec la logique templates/langues PrestaShop”. Si votre module s’appuyait sur Mail::send() pour gérer les templates /mails/<lang>/…, conservez ce point d’entrée tant que possible : il encapsule des comportements historiques (fallback de langue, surcharge de thème, multiboutique) que vous n’avez pas envie de recoder.

En pratique, une bonne stratégie est souvent hybride :

  • Cas 1 (transactionnel standard PrestaShop) : continuez à appeler Mail::send() et adaptez seulement ce qui “touchait Swift”.
  • Cas 2 (emails “métier” spécifiques, hors templates PrestaShop) : basculez sur MailerInterface + Email et gérez vous‑même le rendu (HTML simple, ou Twig si votre module l’implique).
  • Cas 3 (gros volume / criticité élevée) : adoptez MailerInterface + Messenger pour mettre l’envoi en file, tout en conservant la génération des contenus via les mécanismes PrestaShop si c’est indispensable.

La migration Swift Mailer vers Symfony Mailer devient alors surtout une question de compatibilité des hooks/overrides : ne cherchez pas à accéder à des classes Swift depuis ces hooks, et attendez‑vous à devoir adapter la signature/les données exposées si le core change ce qu’il passe aux hooks.

Configuration des transports : DSN, SMTP, TLS, secrets et pièges de prod

Symfony Mailer standardise la configuration via un DSN (MAILER_DSN) au lieu d’un patchwork de paramètres. Dans PrestaShop 9, vous allez souvent devoir mapper l’ancienne configuration SMTP du BO (host, port, encryption, user, password) vers une chaîne DSN. Pour le SMTP, le cas le plus courant ressemble à :

MAILER_DSN=smtp://user:[email protected]:587?encryption=tls

Mapping rapide (pour éviter les erreurs “bêtes”) :

BO / paramètre historique DSN Symfony Mailer
Hôte SMTP @smtp.exemple.tld
Port :587 (souvent STARTTLS) ou :465 (souvent TLS implicite)
Utilisateur smtp://user:...@
Mot de passe smtp://...:pass@ (URL‑encodé si besoin)
Chiffrement ?encryption=tls (STARTTLS) ou schéma smtps:// selon le cas

Attention aux caractères spéciaux dans le mot de passe : en DSN, c’est une URL, donc il faut URL‑encoder (@ → %40, : → %3A, etc.). Deuxième piège : la négociation TLS. En prod, une chaîne “fonctionnelle” en dev peut échouer derrière un proxy sortant, un firewall, ou une librairie OpenSSL plus restrictive (CA manquante, TLS trop ancien, SNI…). Si vous migrez aussi l’hébergement ou PHP en même temps, isolez les variables : une migration mailer propre se fait sur une base d’infra stable. Côté optimisation plateforme, vous pouvez recouper avec Performance PHP LiteSpeed : optimiser LSAPI, cache opcode et concurrence pour éviter de tirer des conclusions biaisées (timeouts qui ressemblent à des erreurs SMTP, mais qui sont en réalité des saturations PHP).

Troisième piège : les secrets en clair. En 2026, mettre des identifiants SMTP dans la base (ou dans un fichier commité) est une faute opérationnelle, pas un “choix”. Sur des déploiements conteneurisés, injectez MAILER_DSN via secrets (Kubernetes, Swarm, CI/CD). Si votre stack est déjà orchestrée, alignez cette migration sur votre stratégie globale : cf. Migration PrestaShop Kubernetes : TLS, DNS et configuration multiboutique et, sur l’aspect durcissement Symfony/serveur, Symfony : sécuriser un serveur PHP avec hardening et gestion CVE.

Enfin, quand vous diagnostiquez une panne d’envoi, pensez “protocole” : les codes SMTP et leurs familles (4xx retry / 5xx hard fail) restent une grille de lecture incontournable. Référence normative (utile pour cadrer un runbook) : https://www.rfc-editor.org/rfc/rfc5321.

Fiabiliser : tests, envoi asynchrone (Messenger), observabilité et plan de rollback

La migration Swift Mailer vers Symfony Mailer se valide avec des tests qui ressemblent à la réalité : capturer les emails en staging (MailHog/Mailpit/Mailcatcher), vérifier le rendu HTML/TXT (encodage, liens, images embarquées), et mesurer le taux d’échec transport (codes SMTP, latence, timeouts). Un indicateur simple à poser : p95 de latence d’envoi sur une rafale de 100 emails transactionnels (inscription, reset password, confirmation commande). Si vous faites du B2B avec de grosses pièces jointes, testez aussi la taille : certains SMTP coupent à 10–25 MB, et Symfony Mailer échouera correctement… mais votre UX/BO doit gérer l’erreur.

Check‑list de tests “pratiques” (qui attrapent la majorité des régressions) :

  • Un email sans HTML (texte seul) et un email HTML + texte : vérifier l’encodage (accents), les retours à la ligne, l’affichage mobile.
  • Une pièce jointe PDF + une image inline : vérifier que l’inline ne se transforme pas en pièce jointe “classique”.
  • Vérification des en‑têtes visibles : From, Reply‑To, To, Cc/Bcc (et cohérence avec la configuration boutique).
  • Vérification des liens “critiques” (reset password, suivi commande) : pas de double encodage, pas de liens relatifs.
  • Test de panne : identifiants SMTP invalides / port filtré / DNS cassé → l’erreur doit être explicite et loggée, et l’expérience client ne doit pas “pendre” indéfiniment.

Pour les boutiques à volume, le sujet n’est pas “Mailer vs Swift Mailer” mais “synchrone vs asynchrone”. Swift Mailer proposait des spools ; Symfony Mailer s’appuie plutôt sur Symfony Messenger (file d’attente + workers) pour découpler le front (checkout) de l’envoi. Techniquement, vous activez l’envoi via bus Messenger dans la conf Symfony, puis vous exécutez un worker (systemd, Supervisor, Kubernetes Deployment). La doc officielle Symfony Mailer détaille l’intégration : https://symfony.com/doc/current/mailer.html. Si vous n’avez jamais industrialisé Messenger, l’article interne Symfony Messenger : configuration avancée, choix du transport et performances est le bon point de départ.

Deux points d’attention concrets quand vous passez en asynchrone :

  • Idempotence : un retry Messenger peut renvoyer un email si votre logique n’a pas de garde‑fou (ex. “email déjà envoyé pour cette commande”). C’est souvent acceptable pour certains emails, catastrophique pour d’autres (double facture, double relance).
  • Gestion des échecs : prévoyez une stratégie claire (nombre de retries, délai, dead‑letter queue) et une UI/process interne pour re‑traiter.

Sur l’observabilité, ne vous contentez pas de “ça envoie”. Ajoutez un subscriber d’événements Mailer (ou au minimum du logging autour de MailerInterface::send()), corrélez les erreurs transport avec vos logs applicatifs, et exposez un compteur (ex. Prometheus) des envois OK/KO par type d’email. Ce n’est pas du confort : c’est ce qui vous évite de découvrir une panne SMTP via des tickets clients 48 h plus tard. Si vous avez déjà une stratégie d’alerting applicative, pensez à intégrer les erreurs d’envoi mail dans votre pipeline (cf. PrestaShop : module d’alerting temps réel Slack, Discord et email).

Enfin, prévoyez un rollback réaliste. Revenir à Swift Mailer en PrestaShop 9 n’est généralement pas une option propre (dépendances, conflits, core). Le rollback doit plutôt être fonctionnel : basculer de transport (SMTP secondaire, API provider), réduire l’impact (mise en file d’attente, retry), et désactiver temporairement des emails non critiques.

Un plan de rollback “prêt à exécuter” ressemble souvent à :

  • Transport secondaire prêt (DNS, credentials, tests déjà faits) + procédure de bascule (variable d’environnement / secret).
  • Mode dégradé documenté : quelles notifications sont coupées temporairement (ex. emails marketing/relance), lesquelles restent obligatoires (ex. reset password).
  • Point de contrôle délivrabilité : comparaison des headers (From/Reply‑To/Return‑Path), conformité SPF/DKIM/DMARC côté domaine, et vérification des templates sur toutes les langues actives.
  • Runbook incident : qui est alerté, où regarder (logs applicatifs, logs worker Messenger, logs MTA), et comment qualifier un 4xx vs 5xx (retry vs correction).

Pour un plan global de migration vers PS9, recoupez avec Migration PrestaShop 1.6/1.7/8 vers PrestaShop 9 sans coupure : la partie “email” est souvent le premier endroit où une boutique “a l’air OK” mais casse dès les premiers vrais événements clients.



À lire aussi