Plugins Magento 2 : Méthodes Before, After & Around Expliquées
Les plugins Magento 2 (aussi appelés intercepteurs) vous permettent de personnaliser le comportement du cœur ou de modules tiers sans modifier le code source original — une exigence cruciale pour rester compatible avec les mises à jour. Ce guide détaille les trois types de plugins — before, after et around — avec des exemples concrets, la syntaxe di.xml, les pièges courants et les règles appliquées par Magento en interne.
En résumé : Un plugin Magento 2 intercepte une méthode publique d’une classe pour exécuter du code avant, après ou autour de celle-ci, sans modifier la classe d’origine. Utilisez
beforepour altérer les arguments,afterpour modifier le résultat, etarounduniquement quand vous avez besoin des deux — ou de bloquer complètement l’exécution.
Table des matières
- Qu’est-ce qu’un Plugin Magento 2 (Intercepteur) ?
- Les 3 Types de Plugins : Before, After, Around
- Plugin Before : Modifier les Arguments Avant l’Exécution
- Plugin After : Transformer le Résultat
- Plugin Around : Contrôle Total (À Utiliser avec Prudence)
- Déclaration dans di.xml : Syntaxe et sortOrder
- Limitations et Méthodes Interdites
- Plugins vs Observateurs d’Événements : Quand Choisir Quoi ?
- Bonnes Pratiques et Performance
- FAQ Technique
Qu’est-ce qu’un Plugin Magento 2 (Intercepteur) ?
Un plugin, aussi appelé intercepteur, est une classe qui modifie le comportement des fonctions publiques d’une classe en interceptant l’appel de fonction et en exécutant du code avant, après ou autour de cet appel.
Contrairement aux réécritures de classe (class preferences), les plugins ne modifient pas la classe cible elle-même. Ils permettent d’étendre ou de modifier le code central de manière sûre et compatible avec les mises à jour. Adobe Commerce et Magento Open Source exécutent ces intercepteurs séquentiellement selon un sortOrder configuré, évitant ainsi les conflits entre extensions.
Les 3 Types de Plugins : Before, After, Around
| Type | Préfixe | Moment d’Exécution | Cas d’Usage Principal | Impact Performance |
|---|---|---|---|---|
| Before | before + NomMéthode | Avant la méthode observée | Modifier les arguments d’entrée | Faible |
| After | after + NomMéthode | Après la méthode observée | Modifier le résultat retourné | Faible |
| Around | around + NomMéthode | Avant ET après | Contrôle total, conditionnel | ⚠️ Élevé |
Plugin Before : Modifier les Arguments Avant l’Exécution
Les plugins Before s’exécutent en premier, avant la méthode observée. Ils doivent porter le préfixe before suivi du nom exact de la méthode cible.
Cas d’Usage Typique
Modifier le groupe de clients en fonction du domaine email avant l’enregistrement.
<?php
declare(strict_types=1);
namespace Vendor\Module\Plugin;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Api\Data\CustomerInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
class SetCustomerGroupByEmailDomain
{
private const XML_PATH_SPECIAL_DOMAINS = 'customer/groups/special_email_domains';
private const SPECIAL_GROUP_ID = 2;
public function __construct(
private ScopeConfigInterface $scopeConfig,
) {}
public function beforeSave(
CustomerRepositoryInterface $subject,
CustomerInterface $customer,
$passwordHash = null,
) {
$email = $customer->getEmail();
$domains = $this->getSpecialDomains();
foreach ($domains as $domain) {
if (str_ends_with($email, '@' . trim($domain))) {
$customer->setGroupId(self::SPECIAL_GROUP_ID);
break;
}
}
return [$customer, $passwordHash];
}
private function getSpecialDomains(): array
{
$domainsString = $this->scopeConfig->getValue(self::XML_PATH_SPECIAL_DOMAINS);
return $domainsString ? explode(',', $domainsString) : [];
}
}
Règles des Plugins Before
- Retournez un tableau des arguments modifiés dans le même ordre que la signature de la méthode originale.
- Si vous ne modifiez rien, retournez
null(pas un tableau vide). - Si un paramètre est optionnel (
= null) dans la méthode originale, il doit être optionnel dans le plugin aussi.
Plugin After : Transformer le Résultat
Les plugins After interviennent juste après l’exécution de la méthode observée. Ils reçoivent le résultat original et peuvent le modifier avant de le retourner.
Exemple : Réduction de Fidélité sur le Prix d’un Produit
<?php
declare(strict_types=1);
namespace Vendor\Module\Plugin;
use Magento\Catalog\Model\Product;
use Magento\Customer\Model\Session as CustomerSession;
class LoggedInCustomerLoyaltyDiscount
{
private const XML_PATH_DISCOUNT = 'sales/loyalty/discount_percent';
public function __construct(
private CustomerSession $customerSession,
private ScopeConfigInterface $scopeConfig,
) {}
public function afterGetPrice(Product $subject, $result)
{
if ($this->customerSession->isLoggedIn()) {
$discount = (float) $this->scopeConfig->getValue(self::XML_PATH_DISCOUNT) ?: 0;
$result = $result * (1 - $discount / 100);
}
return $result;
}
}
Plugin Around : Contrôle Total (À Utiliser avec Prudence)
Le Plugin Around offre un contrôle maximal : il s’exécute avant et après la méthode observée. Il reçoit un callable $proceed qui représente la méthode originale (ou le prochain plugin dans la chaîne).
⚠️ Avertissement Critique
Magento déconseille fortement l’utilisation des plugins Around sauf en cas d’absolue nécessité. Ils :
- Augmentent la taille de la pile d’appels
- Dégradent les performances
- Compliquent le débogage
- Encouragent le code spaghetti
Exemple Valide : Journalisation Conditionnelle
<?php
declare(strict_types=1);
namespace Vendor\Module\Plugin;
use Magento\Catalog\Model\Product;
use Psr\Log\LoggerInterface;
class ProductSaveLogger
{
public function __construct(
private LoggerInterface $logger,
) {}
public function aroundSave(
Product $subject,
callable $proceed,
) {
$this->logger->info('Avant enregistrement : ID ' . $subject->getId());
$result = $proceed(); // Exécute la méthode originale
$this->logger->info('Après enregistrement : ID ' . $subject->getId());
return $result;
}
}
Quand Utiliser Around ?
- Vous devez modifier à la fois les arguments ET le résultat.
- Vous devez conditionner l’exécution de la méthode originale (ex. : feature flag).
- Dans tous les autres cas, préférez
before+after.
Déclaration dans di.xml : Syntaxe et sortOrder
Chaque plugin doit être déclaré dans le fichier etc/di.xml (ou etc/frontend/di.xml, etc/adminhtml/di.xml selon la zone).
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Customer\Api\CustomerRepositoryInterface">
<plugin name="vendor_module_set_customer_group"
type="Vendor\Module\Plugin\SetCustomerGroupByEmailDomain"
sortOrder="10"
disabled="false" />
</type>
<type name="Magento\Catalog\Model\Product">
<plugin name="vendor_module_loyalty_discount"
type="Vendor\Module\Plugin\LoggedInCustomerLoyaltyDiscount"
sortOrder="20" />
</type>
</config>
Attributs du Nœud <plugin>
| Attribut | Requis | Description |
|---|---|---|
name | ✅ | Identifiant unique du plugin (utilisé pour la fusion de config) |
type | ✅ | Classe PHP du plugin (FQCN) |
sortOrder | ❌ | Ordre d’exécution (plus petit = exécuté en premier) |
disabled | ❌ | true pour désactiver un plugin cœur/tiers sans supprimer le code |
🔗 Si vous créez votre première extension, commencez par comprendre les fondamentaux du développement de modules Magento. Suivez notre guide pas à pas sur la création d’un module Magento 2 personnalisé avant d’implémenter des plugins, observateurs ou préférences.
Limitations et Méthodes Interdites
Les plugins ne peuvent pas être utilisés sur :
- ❌ Les méthodes et classes
final - ❌ Les méthodes non publiques (
private,protected) - ❌ Les méthodes statiques (
static) - ❌
__constructet__destruct - ❌ Les types virtuels
- ❌ Les objets instanciés avant l’initialisation de
Magento\Framework\Interception - ❌ Les classes implémentant
Magento\Framework\ObjectManager\NoninterceptableInterface
🚨 Piège Courant : Essayer d’intercepter une méthode
protectedgénère une erreur silencieuse — le plugin est simplement ignoré.
Plugins vs Observateurs d’Événements : Quand Choisir Quoi ?
| Critère | Plugin | Observateur d’Événement |
|---|---|---|
| Cible | Méthode spécifique d’une classe | Événement diffusé dans tout le système |
| Contrôle | Fin (arguments, résultat, flux) | Limité (réaction à un événement) |
| Couplage | Fort (lié à une classe) | Faible (découplé) |
| Performance | Direct | Peut être déclenché plusieurs fois |
Règle de Décision
- Plugin : vous devez modifier le comportement d’une méthode spécifique, ses entrées ou ses sorties.
- Observateur : vous devez réagir à un événement qui peut survenir à plusieurs endroits (ex. :
checkout_cart_save_after).
⚠️ Avertissement : Ne mélangez pas plugins et observateurs pour la même logique sans maîtriser l’ordre d’exécution. Un observateur déclenché avant un plugin
beforequi modifie des données peut causer des incohérences.
Bonnes Pratiques et Performance
1. Préférez Before + After à Around
Chaque plugin Around ajoute une frame à la pile d’appels. Sur un site à fort trafic, cela se traduit par une latence mesurable.
2. Respectez sortOrder
L’ordre d’exécution suit ces règles :
- Tous les plugins
befores’exécutent du plus petit au plus grandsortOrder - Ensuite les plugins
around(première moitié →$proceed()→ seconde moitié) - Enfin les plugins
afterdu plus petit au plus grandsortOrder
3. Nommez vos Plugins Explicitement
<!-- ❌ Mauvais -->
<plugin name="my_plugin" ... />
<!-- ✅ Bon -->
<plugin name="vendor_module_customer_group_by_email_domain" ... />
4. Évitez les Plugins sur les Méthodes Fréquemment Appelées
Ne surchargez pas getPrice(), getName() ou getId() sur des collections entières. Préférez les événements ou les réécritures de modèle si nécessaire.
5. Testez avec les Plugins Désactivés
<plugin name="vendor_module_logger" disabled="true" />
Cela permet de vérifier rapidement si un bug provient de votre interception.
FAQ Technique
Peut-on empiler plusieurs plugins sur la même méthode ?
Oui. Magento les chaîne automatiquement selon leur sortOrder. Si deux plugins ont le même sortOrder, l’ordre de chargement des modules (défini dans module.xml) détermine la séquence.
Un plugin peut-il empêcher l’exécution de la méthode originale ?
Seul un plugin Around peut le faire — en n’appelant pas $proceed(). C’est déconseillé sauf dans des cas exceptionnels (feature flags, mode maintenance).
Pourquoi mon plugin ne s’exécute-t-il pas ?
Vérifiez dans cet ordre :
- La méthode est-elle
public? - La classe est-elle
final? - Le cache est-il vidé (
bin/magento cache:clean) ? - Le
di.xmlest-il dans le bon répertoire (etc/vsetc/frontend/) ? - Y a-t-il une erreur de syntaxe dans le FQCN du
type?
Quelle est la différence entre un plugin et une préférence de classe ?
Une préférence (<preference>) remplace entièrement la classe cible. Un plugin intercepte sans remplacer. Préférez toujours les plugins pour la rétrocompatibilité.
Résumé et Prochaines Étapes
| Ce Que Vous Avez Appris | Action Immédiate |
|---|---|
| Les 3 types d’intercepteurs (Before, After, Around) | Identifiez une méthode cœur à modifier dans votre projet |
La syntaxe di.xml et sortOrder | Créez votre premier plugin sur un environnement de test |
| Les limitations et pièges de performance | Auditez vos plugins existants — remplacez les plugins around inutiles |
| La différence entre Plugin et Observateur | Documentez votre choix architectural |
Vous avez besoin de modules personnalisés avec des implémentations de plugins propres ? Découvrez mes services de développement de modules personnalisés .