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 :displayProductAdditionalInfoajoute 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 :actionCartSavese 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 hook | Quand il se déclenche | Cas d’utilisation typique |
|---|---|---|
displayHeader | Dans le <head> de chaque page | Balises meta, scripts de suivi |
displayFooter | Pied de page | Widgets de pied de page, analytics |
displayHome | Zone de contenu de la page d’accueil | Bannières, produits mis en avant |
displayProductAdditionalInfo | Sous « Ajouter au panier » | Estimations de livraison, guides des tailles |
displayShoppingCart | Page récapitulative du panier | Ventes croisées, estimateurs de livraison |
displayOrderConfirmation | Après la validation de la commande | Suivi des conversions |
displayCustomerAccount | Page « Mon compte » | Sections personnalisées du compte |
displayBanner | Haut de page, au-dessus de l’en-tête | Annonces de promotions |
Hooks d’affichage Back Office
| Nom du hook | Quand il se déclenche | Cas d’utilisation typique |
|---|---|---|
displayBackOfficeHeader | <head> de l’administration | CSS/JS d’administration, notifications |
displayAdminOrder | Page de détail de la commande | Panneaux personnalisés, intégrations de transporteurs |
displayAdminProductsExtra | Onglet de l’éditeur de produit | Champs produit personnalisés |
Hooks d’action
| Nom du hook | Quand il se déclenche | Cas d’utilisation typique |
|---|---|---|
actionCartSave | Panier créé ou mis à jour | Réservation de stock, analytics |
actionOrderStatusUpdate | Changement de statut de commande | Notifications, synchronisation ERP |
actionProductUpdate | Produit enregistré dans l’administration | Synchronisation de catalogue externe |
actionCustomerAccountAdd | Nouveau client inscrit | E-mail de bienvenue, synchronisation CRM |
actionValidateOrder | Commande validée | Traitement du paiement, mise à jour du stock |
actionFrontControllerSetMedia | Le contrôleur front charge les ressources | Enregistrement de CSS/JS |
actionAdminControllerSetMedia | Le contrôleur admin charge les ressources | Enregistrement 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énario | Approche |
|---|---|
| Ajouter du contenu à une page | Hook d’affichage |
| Réagir à un événement | Hook d’action |
| Ajouter du CSS/JS | Hook d’enregistrement de ressources |
| Modifier un calcul du cœur | Override (si aucun hook n’existe) |
| Modifier une requête SQL du cœur | Override (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.phppeut 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ôme | Cause | Correction |
|---|---|---|
| La méthode n’est jamais appelée | Non enregistré sur le hook | Vérifiez ps_hook_module ; réinitialisez le module |
| Retourne une chaîne vide | Template introuvable | Vérifiez le chemin du template, activez les erreurs |
| Se déclenche sur les mauvaises pages | Pas de vérification de page | Ajoutez instanceof ProductController |
| Se déclenche deux fois | Enregistré deux fois | Vérifiez les doublons dans la table |
| $params['product'] est un tableau | PS 1.7+ utilise des tableaux | Utilisez $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.