Knowledge Base Guide

Hooks & Overrides PrestaShop : Guide de référence développeur

Référence développeur pour les hooks et overrides PrestaShop — hooks d'affichage, hooks d'action, hooks personnalisés, système d'override et migration.

Qu’est-ce que les hooks et pourquoi ils sont importants

Les hooks constituent le système d’extension de PrestaShop — le mécanisme par lequel les modules interagissent avec la plateforme principale sans modifier les fichiers du cœur. Chaque module que vous installez s’appuie sur des hooks pour se connecter au bon endroit, au bon moment.

Si vous vous êtes déjà demandé comment un module « sait » afficher quelque chose sous le prix du produit, ou comment il réagit lorsqu’un client passe une commande — la réponse, ce sont les hooks.

Les hooks sont à PrestaShop ce que les prises électriques sont à une maison. Le cœur fournit des prises à des emplacements précis, et les modules s’y branchent. Vous ne refaites pas le câblage de la maison — vous vous branchez là où vous avez besoin de courant.

Hooks d’affichage et hooks d’action

PrestaShop dispose de deux types de hooks :

  • Les hooks d’affichage (préfixés par display) — Points d’insertion visuels. Lorsqu’ils sont déclenchés, ils collectent le HTML des modules enregistrés et l’affichent. Exemple : displayProductAdditionalInfo ajoute du contenu sous le prix du produit.
  • Les hooks d’action (préfixés par action) — Notifications d’événements. Ils ne produisent pas de sortie — ils informent les modules que quelque chose s’est produit. Exemple : actionCartSave se déclenche après la mise à jour d’un panier.

Fonctionnement du Hook Dispatcher

Lorsque PrestaShop atteint un point de hook, il appelle Hook::exec(), qui recherche les modules enregistrés dans ps_hook_module, les trie par position, appelle la méthode de hook de chaque module avec un tableau $params, et pour les hooks d’affichage concatène le HTML retourné.

Les hooks essentiels que tout développeur devrait connaître

Hooks d’affichage Front Office

Nom du hookQuand il se déclencheCas d’utilisation typique
displayHeaderDans le <head> de chaque pageBalises meta, scripts de suivi
displayFooterPied de pageWidgets de pied de page, analytics
displayHomeZone de contenu de la page d’accueilBannières, produits mis en avant
displayProductAdditionalInfoSous « Ajouter au panier »Estimations de livraison, guides des tailles
displayShoppingCartPage récapitulative du panierVentes croisées, estimateurs de livraison
displayOrderConfirmationAprès la validation de la commandeSuivi des conversions
displayCustomerAccountPage « Mon compte »Sections personnalisées du compte
displayBannerHaut de page, au-dessus de l’en-têteAnnonces de promotions

Hooks d’affichage Back Office

Nom du hookQuand il se déclencheCas d’utilisation typique
displayBackOfficeHeader<head> de l’administrationCSS/JS d’administration, notifications
displayAdminOrderPage de détail de la commandePanneaux personnalisés, intégrations de transporteurs
displayAdminProductsExtraOnglet de l’éditeur de produitChamps produit personnalisés

Hooks d’action

Nom du hookQuand il se déclencheCas d’utilisation typique
actionCartSavePanier créé ou mis à jourRéservation de stock, analytics
actionOrderStatusUpdateChangement de statut de commandeNotifications, synchronisation ERP
actionProductUpdateProduit enregistré dans l’administrationSynchronisation de catalogue externe
actionCustomerAccountAddNouveau client inscritE-mail de bienvenue, synchronisation CRM
actionValidateOrderCommande validéeTraitement du paiement, mise à jour du stock
actionFrontControllerSetMediaLe contrôleur front charge les ressourcesEnregistrement de CSS/JS
actionAdminControllerSetMediaLe contrôleur admin charge les ressourcesEnregistrement de CSS/JS d’administration

Enregistrement des ressources

Depuis PS 1.7, la méthode recommandée pour ajouter du CSS/JS passe par l’enregistrement des ressources dans actionFrontControllerSetMedia :

public function hookActionFrontControllerSetMedia($params)
{
    $this->context->controller->registerStylesheet(
        'my-module-style',
        'modules/' . $this->name . '/views/css/front.css',
        ['media' => 'all', 'priority' => 150]
    );

    $this->context->controller->registerJavascript(
        'my-module-script',
        'modules/' . $this->name . '/views/js/front.js',
        ['position' => 'bottom', 'priority' => 150]
    );
}

Cela permet l’optimisation CCC (Combine, Compress, Cache) — bien mieux que d’injecter des balises brutes via displayHeader.

Comment utiliser les hooks dans un module

Enregistrer les hooks lors de l’installation

public function install()
{
    return parent::install()
        && $this->registerHook('displayProductAdditionalInfo')
        && $this->registerHook('actionCartSave')
        && $this->registerHook('actionFrontControllerSetMedia');
}

Chaque appel insère une ligne dans ps_hook_module. Si vous oubliez d’enregistrer le hook, la méthode ne sera jamais appelée — même si elle existe.

Source fréquente de maux de tête lors du débogage : la méthode du hook existe et porte le bon nom, mais le module ne s’est jamais enregistré sur le hook lors de l’installation. Si vous ajoutez un hook après la première version, vous avez besoin d’un script de mise à jour ou d’une réinitialisation du module.

Implémenter la méthode du hook

Convention de nommage : hook + nom du hook avec la première lettre en majuscule.

public function hookDisplayProductAdditionalInfo($params)
{
    $product = $params['product'];

    $this->context->smarty->assign([
        'delivery_estimate' => $this->calculateDeliveryEstimate($product),
    ]);

    return $this->display(__FILE__, 'views/templates/hook/product-info.tpl');
}

// Hook d'action — pas de valeur de retour
public function hookActionCartSave($params)
{
    if (!isset($params['cart'])) {
        return;
    }
    $this->logCartActivity($params['cart']->id);
}

Le tableau $params

Le contenu varie selon le hook. Les plus courants : $params['cart'], $params['order'], $params['product'], $params['customer']. Pour voir ce qu’un hook transmet, ajoutez un log temporaire :

file_put_contents(
    _PS_ROOT_DIR_ . '/var/logs/hook_debug.log',
    print_r(array_keys($params), true),
    FILE_APPEND
);

Trouver les hooks disponibles

1. Mode debug

Activez-le dans Paramètres avancés → Performances → Mode debug : OUI. Le front office affichera les noms des hooks aux points d’insertion. Sous PS 8+, la barre de débogage Symfony affiche tous les hooks déclenchés par requête.

N’activez jamais le mode debug sur une boutique en production. Cela expose des données internes et ralentit le site.

2. Requête en base de données

-- All hooks with "product" in the name
SELECT name, title FROM ps_hook WHERE name LIKE '%product%' ORDER BY name;

-- Which modules are on a specific hook
SELECT m.name, hm.position FROM ps_hook_module hm
JOIN ps_hook h ON h.id_hook = hm.id_hook
JOIN ps_module m ON m.id_module = hm.id_module
WHERE h.name = 'displayProductAdditionalInfo'
ORDER BY hm.position;

3. Recherche dans les templates et le code source

# Display hooks in theme templates
grep -rn "{hook " themes/your-theme/templates/

# Action hooks in PHP core
grep -rn "Hook::exec" classes/ controllers/ src/

Créer des hooks personnalisés

Lorsque les hooks intégrés ne se déclenchent pas au moment précis dont vous avez besoin, créez les vôtres :

// Dispatch a custom hook
$hookResult = Hook::exec('actionMyModuleBeforeProcess', [
    'order_id' => $orderId,
    'custom_data' => $myData,
]);

// For display hooks — true returns array per module
$extraHtml = Hook::exec('displayMyModuleExtraContent', [
    'product' => $product,
], null, true);

Enregistrez-le dans install() comme vous le feriez pour n’importe quel hook. Pour lui donner un nom convivial dans les Positions :

$hook = new Hook();
$hook->name = 'displayMyModuleExtraContent';
$hook->title = 'My Module - Extra Content Area';
$hook->add();

Créez des hooks personnalisés lorsque votre module fournit des points d’extension dont d’autres modules ont besoin. Ne les créez pas pour de la logique interne uniquement — utilisez des méthodes classiques.

Le système d’override

Les overrides remplacent les classes et contrôleurs du cœur sans modifier les fichiers principaux. C’était la méthode de personnalisation principale sous PS 1.5/1.6, avant que les hooks ne soient exhaustifs.

Fonctionnement des overrides

Un override étend une classe du cœur et remplace des méthodes spécifiques. L’autoloader vérifie d’abord le répertoire override/ :

// override/classes/Cart.php
class Cart extends CartCore
{
    public function getOrderTotal($with_taxes = true, $type = Cart::BOTH,
        $products = null, $id_carrier = null, $use_cache = false)
    {
        $total = parent::getOrderTotal($with_taxes, $type, $products, $id_carrier, $use_cache);
        if ($this->hasSpecialProducts()) {
            $total += $this->calculateSurcharge();
        }
        return $total;
    }
}

La structure des répertoires reflète celle du cœur : override/classes/, override/controllers/front/, override/controllers/admin/.

Override ou hook

ScénarioApproche
Ajouter du contenu à une pageHook d’affichage
Réagir à un événementHook d’action
Ajouter du CSS/JSHook d’enregistrement de ressources
Modifier un calcul du cœurOverride (si aucun hook n’existe)
Modifier une requête SQL du cœurOverride (si aucun hook n’existe)

Les problèmes liés aux overrides

  • Conflits : Deux modules ne peuvent pas faire un override de la même méthode. Le second échoue à l’installation.
  • Ruptures lors des mises à jour : Les signatures des méthodes du cœur changent entre les versions. Un override pour PS 1.7.7 peut provoquer un crash sous 1.7.8.
  • Comportement caché : Les overrides sont invisibles depuis le Back Office, ce qui complique le débogage.
  • Corruption de l’index : Le fichier de correspondance class_index.php peut se corrompre, ce qui casse entièrement la boutique.
L’équipe du cœur PrestaShop décourage activement l’utilisation des overrides depuis la version 1.7. Si vous êtes tenté d’utiliser un override, vérifiez d’abord si un hook ou une alternative basée sur Symfony existe.

Alternatives aux overrides (PS 8+)

Décorateurs de services Symfony

Encapsulez les services du cœur avec votre propre logique — plusieurs décorateurs peuvent s’enchaîner sans conflit :

# modules/mymodule/config/services.yml
services:
    mymodule.decorated_calculator:
        class: MyModule\Service\CalculatorDecorator
        decorates: 'prestashop.core.cart.calculator'
        arguments:
            - '@mymodule.decorated_calculator.inner'

Écouteurs d’événements Doctrine

Réagissez aux changements d’entités sans faire d’override sur ObjectModel :

# modules/mymodule/config/services.yml
services:
    mymodule.product_listener:
        class: MyModule\EventListener\ProductListener
        tags:
            - { name: doctrine.event_listener, event: postUpdate }

Commandes et requêtes CQRS

Branchez-vous sur les opérations d’administration via des handlers de commandes/requêtes. C’est l’approche la plus moderne, qui nécessite une bonne connaissance de l’architecture Symfony de PrestaShop.

Résoudre les conflits d’overrides

Lorsque deux modules font un override de la même méthode, vous verrez :

The method Cart::getOrderTotal is already overridden by module "othermodule".

La solution consiste à fusionner manuellement — combiner la logique des deux modules dans un seul fichier d’override. Après modification, reconstruisez l’index des classes :

# Delete class index — PrestaShop rebuilds automatically
rm var/cache/prod/class_index.php
rm var/cache/dev/class_index.php
La fusion manuelle des overrides est fragile. Lorsque l’un des modules est mis à jour, vous devrez peut-être fusionner à nouveau. Documentez ce que vous avez fusionné et pourquoi.

Si un module utilise un override là où un hook fonctionnerait, contactez le développeur et demandez une solution basée sur les hooks.

Priorité des hooks et ordre d’exécution

Lorsque plusieurs modules sont enregistrés sur le même hook d’affichage, la position détermine l’ordre. Gérez cela dans Apparence → Positions où vous pouvez faire glisser-déposer les modules, les désaccrocher ou les greffer sur d’autres hooks.

Greffe

Déplacez un module d’un hook à un autre via Apparence → Positions → Greffer un module. Toutes les greffes ne s’affichent pas correctement — cela dépend des templates du module.

Positionnement programmatique

// In install() — move to position 1
$this->updatePosition($this->getHookId('displayHome'), false, 1);

Déboguer les hooks

Vérifications systématiques

-- Is the module registered on the hook?
SELECT m.name, hm.position FROM ps_hook_module hm
JOIN ps_hook h ON h.id_hook = hm.id_hook
JOIN ps_module m ON m.id_module = hm.id_module
WHERE h.name = 'displayProductAdditionalInfo' AND m.name = 'your_module_name';

-- Is the module active?
SELECT name, active FROM ps_module WHERE name = 'your_module_name';

Problèmes courants

SymptômeCauseCorrection
La méthode n’est jamais appeléeNon enregistré sur le hookVérifiez ps_hook_module ; réinitialisez le module
Retourne une chaîne videTemplate introuvableVérifiez le chemin du template, activez les erreurs
Se déclenche sur les mauvaises pagesPas de vérification de pageAjoutez instanceof ProductController
Se déclenche deux foisEnregistré deux foisVérifiez les doublons dans la table
$params['product'] est un tableauPS 1.7+ utilise des tableauxUtilisez $params['product']['id_product']

Profilage des hooks

Activez le profilage pour voir le temps d’exécution par hook :

// PS 1.7 — defines.inc.php
define('_PS_DEBUG_PROFILING_', true);

// PS 8+ — .env.local
APP_DEBUG=1
APP_ENV=dev

Changements dans PrestaShop 9

Nouveaux hooks

PS 9 ajoute des hooks dans des zones qui nécessitaient auparavant des overrides : extensions du formulaire produit dans l’administration, application des règles de prix du panier, opérations sur les ressources API et processus d’envoi d’e-mails.

Hooks dépréciés

Les hooks liés aux anciens contrôleurs d’administration sont dépréciés à mesure que ces contrôleurs migrent vers Symfony. Consultez _PS_DEPRECATED_HOOKS_ pour la liste complète.

Avenir du système d’overrides

Les overrides fonctionnent toujours pour les classes ObjectModel héritées, mais les nouveaux services Symfony et contrôleurs d’administration ne peuvent pas les utiliser. Le système d’overrides sera à terme supprimé lorsque la migration vers Symfony sera achevée.

Si vous démarrez un nouveau module aujourd’hui, construisez-le exclusivement avec des hooks et des services Symfony. Chaque override que vous écrivez maintenant est de la dette technique que vous devrez supprimer plus tard.

Référence rapide

// Register hooks (install)
$this->registerHook(['displayHeader', 'actionCartSave']);

// Check if hook exists
$hookId = Hook::getIdByName('displayMyCustomHook');

// Get modules on a hook
$modules = Hook::getHookModuleExecList('displayHeader');

// Execute hook manually
$output = Hook::exec('displayMyHook', ['key' => 'value']);

// Conditional execution
public function hookDisplayHeader($params) {
    if (!($this->context->controller instanceof ProductController)) return '';
    return $this->display(__FILE__, 'views/templates/hook/header.tpl');
}
-- All hooks a module is registered on
SELECT h.name, hm.position FROM ps_hook_module hm
JOIN ps_hook h ON h.id_hook = hm.id_hook
JOIN ps_module m ON m.id_module = hm.id_module
WHERE m.name = 'your_module_name' ORDER BY h.name;

-- Hooks with no modules registered
SELECT h.name FROM ps_hook h
LEFT JOIN ps_hook_module hm ON h.id_hook = hm.id_hook
WHERE hm.id_hook IS NULL ORDER BY h.name;

Maîtrisez d’abord les hooks — ils couvrent la grande majorité des cas d’utilisation. Apprenez les overrides pour comprendre le code existant et résoudre les conflits. Pour PrestaShop 8+, investissez dans les alternatives basées sur Symfony. L’écosystème avance, et les hooks montrent la voie.

More guides available

Browse our knowledge base for more practical PrestaShop tutorials, or reach out if you need help.

Loading...
Back to top