Aller au contenu
Rechercher
Discutons
Menu

Dossier module PrestaShop · chapitre 3 sur 6

Les crochets d'un module PrestaShop : se brancher sans toucher au cœur

Un crochet, c'est un rendez-vous que PrestaShop donne aux modules à un moment précis. Markdown Twin en utilise quatre, et l'un d'eux lui permet de remplacer toute la page HTML juste avant son envoi, sans toucher une ligne du cœur. Voici lesquels, où PrestaShop les appelle, et ce que le module y fait.

Par Sébastien GrillotTesté au labo : 19 octobre 2026Publié : 19 octobre 2026
Testé sur
PrestaShop 9.2.0 · PHP 8.3.32
Et sur
PrestaShop 1.7.8.11 · PHP 7.4.33
Crochets utilisés
moduleRoutes · actionDispatcherBefore · actionOutputHTMLBefore · displayHeader

L'essentiel en 4 points

  1. Un crochet « action » réagit à un événement, un crochet « display » ajoute du contenu à la page. Ta méthode s'appelle hook suivi du nom du crochet.
  2. actionOutputHTMLBefore reçoit la page HTML complète, par référence : un module peut la modifier, ou la remplacer, juste avant l'envoi.
  3. Quand une même adresse peut répondre en HTML ou en Markdown, l'en-tête Vary: Accept est obligatoire, sinon un cache peut servir l'un à la place de l'autre.
  4. Dans Apparence › Positions, les crochets de Markdown Twin n'apparaissent qu'avec la case « Afficher les points d'accroche invisibles ».

3.1Un crochet, c'est quoi ?

J'aime bien commencer par une image. Imagine PrestaShop qui construit une page. À certains moments bien précis, il s'arrête et demande « Quelqu'un a quelque chose à faire ici ? ». Chaque module branché sur ce moment répond à son tour. Ce moment, c'est un crochet, un hook en anglais, et un « point d'accroche » dans l'administration en français.

Il y en a de deux sortes, et leur nom te dit laquelle. Pratique ! La documentation de PrestaShop pose la règle : « Hook names are prefixed with “action” or “display”. »

  • Un crochet action réagit à un événement. Une commande vient d'être passée, un produit vient d'être enregistré, la page est prête à partir.
  • Un crochet display ajoute du contenu à un endroit de la page, l'en-tête, la colonne de gauche, la fiche produit.

Côté code, c'est d'une simplicité redoutable, et je trouve que c'est ce que PrestaShop a de mieux. Tu enregistres le crochet à l'installation (on l'a vu dans le chapitre sur le squelette), puis tu écris une méthode qui porte son nom, précédé de hook. Pour displayHeader, c'est hookDisplayHeader(). Elle reçoit un seul argument, un tableau avec ce que PrestaShop veut bien lui confier à ce moment-là.

3.2Les quatre crochets de Markdown Twin

Qu'est-ce que Markdown Twin doit apprendre à PrestaShop ? Trois choses. Accepter des adresses qui finissent par .md, remplacer la page HTML par sa version Markdown, et annoncer cette version dans chaque page. Quatre crochets suffisent.

Les crochets de Markdown Twin, et l'endroit où PrestaShop les appelle (code de la 1.7.8.11 et de la 9.2.0, lu le 19 octobre 2026)
CrochetAppelé parCe que le module y fait
moduleRoutesDispatcher::loadRoutes(), au chargement des règles d'adresseajoute les adresses en .md et /llms.txt
actionDispatcherBeforeDispatcher::dispatch(), avant de lancer le contrôleur (ligne 364 en 1.7, 342 en 9.2)repère une demande de jumeau, et la fait passer pour la page HTML
actionOutputHTMLBeforeFrontController, juste avant d'envoyer la page (ligne 730 en 1.7, 776 en 9.2)remplace la page par son jumeau, ou lui ajoute l'en-tête Link
displayHeaderFrontController (ligne 636 en 9.2), que le thème affiche dans le <head>ajoute la balise <link rel="alternate" type="text/markdown">

Les deux premiers concernent les adresses, et je leur ai réservé un chapitre, celui sur les routes. Ici, je m'occupe des deux derniers.

Tu veux voir ces crochets dans ton administration ? Va dans Apparence › Positions, et choisis le module dans la liste « Montrer ».

Au labo, en 9.2.0, la page est restée vide !

Il a fallu cocher « Afficher les points d'accroche invisibles » pour voir apparaître les quatre crochets du module, chacun avec sa description.

Les crochets actionDispatcherBefore et actionOutputHTMLBefore de Markdown Twin dans Apparence › Positions de PrestaShop 9.2.0
Apparence › Positions, en 9.2.0, avec les points d'accroche invisibles : chaque crochet a sa description, ici en anglais

Les descriptions viennent de PrestaShop, et elles sont restées en anglais même dans une administration en français. Pas grave, je te traduis la plus importante. Celle de actionOutputHTMLBefore dit tout : « This hook is used to filter the whole HTML page before it is rendered (only front) ».

3.3Remplacer toute la page juste avant l'envoi

C'est mon crochet préféré, parce qu'il fait tout le travail visible de Markdown Twin. Dans le code de PrestaShop, il est appelé ainsi, à l'identique en 1.7.8.11 et en 9.2.0.

Hook::exec('actionOutputHTMLBefore', ['html' => &$html]);
classes/controller/FrontController.php de PrestaShop 9.2.0, ligne 776

Tu as vu le & ? La page est passée par référence. Ton module ne reçoit pas une copie, mais la page elle-même, et ce qu'il y écrit, c'est ce que le visiteur recevra !

Je te montre ce que Markdown Twin en fait, en version courte.

public function hookActionOutputHTMLBefore($params)
{
    if (headers_sent()) {
        return;
    }
    $supported = Renderer::supported();
    if ('' === self::$mode || ('accept' === self::$mode && !$supported)) {
        if ($supported && Settings::get('accept')) {
            header('Vary: Accept', false);
        }
        if ($supported && Settings::get('discovery')) {
            $twin = Renderer::twinUrl(Renderer::currentUrl());
            if ($twin) {
                header('Link: <' . $twin . '>; rel="alternate"; type="text/markdown"', false);
            }
        }

        return;
    }
    // [...] page absente ou en erreur : une 404 en Markdown
    try {
        $md = Renderer::build((string) $params['html']);
    } catch (\Throwable $e) {
        // [...] une 500 en Markdown
    }
    Renderer::headers(Renderer::$canonical);
    $params['html'] = $md;
}
markdowntwin.php : le crochet actionOutputHTMLBefore de Markdown Twin 1.0.0 (extrait, les coupes sont marquées [...])

Deux cas, donc, et je les trouve très lisibles : pour une page HTML normale, le module la laisse partir telle quelle, et pose seulement deux en-têtes. Pour une demande de jumeau, il lit la page HTML que PrestaShop vient de construire, la convertit en Markdown, pose ses propres en-têtes, et remplace la page.

Pourquoi partir de la page HTML, au lieu de relire le produit en base ? Parce que la page, c'est exactement ce que le visiteur voit, et c'est un choix que je referais. Le thème, les modules qui ajoutent des blocs, les traductions, tout y est déjà. Le jumeau ne peut pas raconter autre chose que la page. Enfin, en théorie, et la relecture du module a trouvé une exception de taille, le prix, que je te raconte dans le chapitre sur les tests.

Les en-têtes du jumeau, mesurés au labo 9.2.0 le 19 octobre 2026 (fiche /19-customizable-mug.html.md)

  • Content-Type: text/markdown; charset=utf-8
  • X-Robots-Tag: noindex, follow, pour que les moteurs de recherche gardent la page HTML.
  • Link: <http://127.0.0.1:8092/19-customizable-mug.html>; rel="canonical", vers la page HTML.
  • Vary: Accept et Cache-Control: public, max-age=600.
  • X-Content-Type-Options: nosniff.

3.4La même adresse, deux réponses : la négociation

Le jumeau a sa propre adresse, en .md. Mais un agent peut aussi demander l'adresse normale en disant qu'il préfère le Markdown, par l'en-tête Accept: text/markdown. Markdown Twin lui répond alors en Markdown, sur l'adresse HTML.

Les navigateurs, eux : jamais de Markdown.

Tes visiteurs ne voient aucune différence.

Mais un cache, si. Le scénario catastrophe : imagine un cache devant ta boutique, qui garde une copie de chaque page pour la servir plus vite. Un agent demande la fiche du mug en Markdown, le cache garde la réponse. Le visiteur suivant demande la même adresse avec son navigateur… et reçoit du Markdown :(

L'en-tête Vary: Accept règle ça, et je le considère comme obligatoire dès qu'une adresse a deux réponses possibles. Il dit au cache que la réponse dépend de l'en-tête Accept, et qu'il doit garder une copie par valeur. Markdown Twin le pose sur toutes les pages qui ont un jumeau. Au labo, la page HTML du mug est partie avec Vary: Accept,Accept-Encoding.

Et par prudence, la réponse négociée elle-même ne se garde jamais. Au labo, la page Livraison demandée avec Accept: text/markdown est revenue en Markdown, avec Cache-Control: no-cache, no-store, must-revalidate, private. Seules les adresses en .md acceptent d'être mises en cache.

3.5Annoncer le jumeau dans le head

Comment un agent sait-il qu'un jumeau existe ? Tout simplement, il le lit dans la page. displayHeader est le crochet qui ajoute des éléments dans le <head>, et Markdown Twin y glisse une ligne.

public function hookDisplayHeader($params)
{
    if ('' !== self::$mode || !Settings::get('discovery') || !Renderer::supported()) {
        return '';
    }
    $twin = Renderer::twinUrl(Renderer::currentUrl());

    return $twin ? '<link rel="alternate" type="text/markdown" href="' . htmlspecialchars($twin, ENT_QUOTES, 'UTF-8') . '" title="Markdown version">' . "\n" : '';
}
markdowntwin.php : le crochet displayHeader de Markdown Twin 1.0.0

Un crochet display renvoie du HTML, et c'est le thème qui le place. En 9.2.0, PrestaShop range tout ce que renvoie displayHeader dans une variable, $HOOK_HEADER, que les thèmes Classic et Hummingbird affichent dans leur head.tpl. Résultat mesuré dans la page du mug :

<link rel="alternate" type="text/markdown" href="http://127.0.0.1:8092/19-customizable-mug.html.md" title="Markdown version">
Dans le head de /19-customizable-mug.html, labo 9.2.0

Pourquoi aussi un en-tête HTTP Link, en plus de la balise ? Parce que la balise dépend du thème. Un thème maison qui oublie $HOOK_HEADER dans son head.tpl, et la balise disparaît. L'en-tête, lui, est posé par le module, quel que soit le thème. Ceinture et bretelles ;)

3.6Et les overrides, alors ?

Tu croiseras des modules qui ne passent pas par les crochets, j'en suis sûr. Ils remplacent carrément une classe du cœur par la leur, c'est un override. Ça marche… jusqu'au jour où deux modules veulent remplacer la même classe :(

La documentation de PrestaShop est très claire là-dessus. « Overrides in PrestaShop are exclusive. » Un seul module peut remplacer une classe, et le deuxième casse le premier, ou se fait casser. Elle conclut : « There are limitations and risks of using overrides. Keep them for your own shop. »

Pour apprendre au routeur de PrestaShop les adresses en .md, un override du routeur aurait été la solution de facilité. Markdown Twin n'en a pas eu besoin. Le routeur a son crochet, moduleRoutes, et c'est l'objet du chapitre suivant.

Sources

Historique de ce chapitre

  • 19 octobre 2026 : première version, crochets relevés dans le code de PrestaShop 9.2.0 et 1.7.8.11.
Sébastien Grillot

Co-fondateur de Kōeki · Consultant SEO et IA

Sébastien Grillot

Consultant SEO depuis 2009 : c'est ici, sur Inside da WeB, qu'il a monté sa première agence, spécialisée dans l'e-commerce. Il construit aujourd'hui ses propres outils avec l'IA, modules PrestaShop compris.

FermerLes crochets actionDispatcherBefore et actionOutputHTMLBefore de Markdown Twin dans Apparence › Positions de PrestaShop 9.2.0