← Tous les articles >
magento 2pluginsinterceptorsbefore after aroundmagento developmentphpe-commercetutorial

Plugins Magento 2 : Méthodes Before, After & Around Expliquées

Share: LinkedIn X Facebook

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 before pour altérer les arguments, after pour modifier le résultat, et around uniquement quand vous avez besoin des deux — ou de bloquer complètement l’exécution.

Table des matières

  1. Qu’est-ce qu’un Plugin Magento 2 (Intercepteur) ?
  2. Les 3 Types de Plugins : Before, After, Around
  3. Plugin Before : Modifier les Arguments Avant l’Exécution
  4. Plugin After : Transformer le Résultat
  5. Plugin Around : Contrôle Total (À Utiliser avec Prudence)
  6. Déclaration dans di.xml : Syntaxe et sortOrder
  7. Limitations et Méthodes Interdites
  8. Plugins vs Observateurs d’Événements : Quand Choisir Quoi ?
  9. Bonnes Pratiques et Performance
  10. 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

TypePréfixeMoment d’ExécutionCas d’Usage PrincipalImpact Performance
Beforebefore + NomMéthodeAvant la méthode observéeModifier les arguments d’entréeFaible
Afterafter + NomMéthodeAprès la méthode observéeModifier le résultat retournéFaible
Aroundaround + NomMéthodeAvant ET aprèsContrô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>

AttributRequisDescription
nameIdentifiant unique du plugin (utilisé pour la fusion de config)
typeClasse PHP du plugin (FQCN)
sortOrderOrdre d’exécution (plus petit = exécuté en premier)
disabledtrue 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)
  • __construct et __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 protected génère une erreur silencieuse — le plugin est simplement ignoré.


Plugins vs Observateurs d’Événements : Quand Choisir Quoi ?

CritèrePluginObservateur d’Événement
CibleMéthode spécifique d’une classeÉvénement diffusé dans tout le système
ContrôleFin (arguments, résultat, flux)Limité (réaction à un événement)
CouplageFort (lié à une classe)Faible (découplé)
PerformanceDirectPeut ê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 before qui 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 :

  1. Tous les plugins before s’exécutent du plus petit au plus grand sortOrder
  2. Ensuite les plugins around (première moitié → $proceed() → seconde moitié)
  3. Enfin les plugins after du plus petit au plus grand sortOrder

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 :

  1. La méthode est-elle public ?
  2. La classe est-elle final ?
  3. Le cache est-il vidé (bin/magento cache:clean) ?
  4. Le di.xml est-il dans le bon répertoire (etc/ vs etc/frontend/) ?
  5. 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 ApprisAction 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 sortOrderCréez votre premier plugin sur un environnement de test
Les limitations et pièges de performanceAuditez vos plugins existants — remplacez les plugins around inutiles
La différence entre Plugin et ObservateurDocumentez 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 .