Knowledge Base Guide

PrestaShop Hooks & Overrides: Entwickler-Referenz

Entwickler-Referenz für PrestaShop Hooks und Overrides — Display-Hooks, Action-Hooks, eigene Hooks, das Override-System und Migration zu modernen Patterns.

Was sind Hooks und warum sie wichtig sind

Hooks sind das Erweiterungssystem von PrestaShop — der Mechanismus, über den Module mit der Kernplattform interagieren, ohne Kerndateien zu verändern. Jedes Modul, das Sie installieren, nutzt Hooks, um sich an der richtigen Stelle zum richtigen Zeitpunkt einzuklinken.

Wenn Sie sich jemals gefragt haben, wie ein Modul „weiß“, dass es etwas unterhalb des Produktpreises anzeigen soll, oder wie es reagiert, wenn ein Kunde eine Bestellung aufgibt — die Antwort lautet: Hooks.

Hooks sind für PrestaShop das, was Steckdosen für ein Haus sind. Der Kern stellt Steckdosen an bestimmten Stellen bereit, und Module stecken sich dort ein. Sie verkabeln nicht das ganze Haus neu — Sie stecken den Stecker dort ein, wo Sie Strom brauchen.

Display Hooks vs Action Hooks

PrestaShop verfügt über zwei Arten von Hooks:

  • Display Hooks (mit dem Präfix display) — Visuelle Einfügepunkte. Wenn sie ausgelöst werden, sammeln sie HTML von registrierten Modulen und rendern es. Beispiel: displayProductAdditionalInfo fügt Inhalte unterhalb des Produktpreises hinzu.
  • Action Hooks (mit dem Präfix action) — Ereignisbenachrichtigungen. Sie erzeugen keine Ausgabe — sie teilen Modulen mit, dass etwas passiert ist. Beispiel: actionCartSave wird ausgelöst, nachdem ein Warenkorb aktualisiert wurde.

Wie der Hook Dispatcher funktioniert

Wenn PrestaShop einen Hook-Punkt erreicht, ruft es Hook::exec() auf, das registrierte Module in ps_hook_module nachschlägt, nach Position sortiert, die Hook-Methode jedes Moduls mit einem $params-Array aufruft und bei Display Hooks das zurückgegebene HTML zusammenfügt.

Wichtige Hooks, die jeder Entwickler kennen sollte

Front Office Display Hooks

Hook-NameWann er ausgelöst wirdTypischer Anwendungsfall
displayHeaderInnerhalb von <head> auf jeder SeiteMeta-Tags, Tracking-Skripte
displayFooterSeiten-FooterFooter-Widgets, Analytics
displayHomeInhaltsbereich der StartseiteBanner, hervorgehobene Produkte
displayProductAdditionalInfoUnterhalb von „In den Warenkorb“Lieferschätzungen, Größenratgeber
displayShoppingCartWarenkorb-ÜbersichtsseiteCross-Selling, Versandrechner
displayOrderConfirmationNach Aufgabe der BestellungConversion-Tracking
displayCustomerAccount„Mein Konto“-SeiteIndividuelle Kontobereiche
displayBannerSeitenoberkante, über dem HeaderAktionsankündigungen

Back Office Display Hooks

Hook-NameWann er ausgelöst wirdTypischer Anwendungsfall
displayBackOfficeHeaderAdmin <head>Admin-CSS/JS, Benachrichtigungen
displayAdminOrderBestelldetailseiteIndividuelle Panels, Versandintegrationen
displayAdminProductsExtraProdukt-Editor-TabBenutzerdefinierte Produktfelder

Action Hooks

Hook-NameWann er ausgelöst wirdTypischer Anwendungsfall
actionCartSaveWarenkorb erstellt oder aktualisiertBestandsreservierung, Analytics
actionOrderStatusUpdateBestellstatus ändert sichBenachrichtigungen, ERP-Synchronisation
actionProductUpdateProdukt im Admin gespeichertExterne Katalogsynchronisation
actionCustomerAccountAddNeuer Kunde registriert sichWillkommens-E-Mail, CRM-Synchronisation
actionValidateOrderBestellung validiertZahlungsabwicklung, Bestandsaktualisierung
actionFrontControllerSetMediaFront-Controller lädt AssetsCSS/JS registrieren
actionAdminControllerSetMediaAdmin-Controller lädt AssetsAdmin-CSS/JS registrieren

Asset-Registrierung

Seit PS 1.7 ist der korrekte Weg, CSS/JS hinzuzufügen, die Asset-Registrierung innerhalb von 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]
    );
}

Dies ermöglicht die CCC-Optimierung (Combine, Compress, Cache) — deutlich besser als das Einfügen von rohen Tags über displayHeader.

Hooks in einem Modul verwenden

Hooks während der Installation registrieren

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

Jeder Aufruf fügt eine Zeile in ps_hook_module ein. Wenn Sie die Registrierung vergessen, wird die Hook-Methode nie ausgeführt — selbst wenn die Methode existiert.

Häufige Fehlerquelle beim Debugging: Die Hook-Methode existiert und ist korrekt benannt, aber das Modul hat sich während der Installation nie auf dem Hook registriert. Wenn Sie einen Hook nach der Erstveröffentlichung hinzufügen, benötigen Sie ein Upgrade-Skript oder einen Modul-Reset.

Die Hook-Methode implementieren

Namenskonvention für Methoden: hook + Hook-Name mit großem Anfangsbuchstaben.

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');
}

// Action Hook — kein Rückgabewert
public function hookActionCartSave($params)
{
    if (!isset($params['cart'])) {
        return;
    }
    $this->logCartActivity($params['cart']->id);
}

Das $params-Array

Der Inhalt variiert je nach Hook. Häufig verwendet: $params['cart'], $params['order'], $params['product'], $params['customer']. Um zu sehen, was ein Hook übergibt, fügen Sie ein temporäres Logging hinzu:

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

Verfügbare Hooks finden

1. Debug-Modus

Aktivieren Sie ihn unter Erweiterte Einstellungen → Leistung → Debug-Modus: JA. Das Front Office zeigt dann Hook-Namen an den Einfügepunkten an. In PS 8+ zeigt die Symfony-Debug-Toolbar alle pro Anfrage ausgelösten Hooks an.

Aktivieren Sie niemals den Debug-Modus in einem Produktiv-Shop. Er legt interne Daten offen und verlangsamt die Seite.

2. Datenbankabfrage

-- 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. Templates und Quellcode durchsuchen

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

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

Eigene Hooks erstellen

Wenn integrierte Hooks nicht genau an der benötigten Stelle auslösen, erstellen Sie eigene:

// 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);

Registrieren Sie ihn in install() wie jeden anderen Hook. Um ihm einen benutzerfreundlichen Namen unter „Positionen“ zu geben:

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

Erstellen Sie eigene Hooks, wenn Ihr Modul Erweiterungspunkte bereitstellt, die andere Module benötigen. Erstellen Sie sie nicht für rein interne Logik — verwenden Sie dafür reguläre Methoden.

Das Override-System

Overrides ersetzen Kernklassen und Controller, ohne Kerndateien zu verändern. Sie waren die primäre Anpassungsmethode in PS 1.5/1.6, bevor Hooks umfassend verfügbar waren.

Wie Overrides funktionieren

Ein Override erweitert eine Kernklasse und ersetzt bestimmte Methoden. Der Autoloader prüft zuerst 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;
    }
}

Die Verzeichnisstruktur spiegelt den Kern wider: override/classes/, override/controllers/front/, override/controllers/admin/.

Override vs Hook

SzenarioAnsatz
Inhalt zu einer Seite hinzufügenDisplay Hook
Auf ein Ereignis reagierenAction Hook
CSS/JS hinzufügenAsset-Registrierung-Hook
Eine Kernberechnung ändernOverride (falls kein Hook existiert)
Eine Kern-SQL-Abfrage ändernOverride (falls kein Hook existiert)

Die Probleme mit Overrides

  • Konflikte: Zwei Module können nicht dieselbe Methode überschreiben. Das zweite scheitert bei der Installation.
  • Upgrade-Brüche: Kern-Methodensignaturen ändern sich zwischen Versionen. Ein Override für PS 1.7.7 kann unter 1.7.8 zu Abstürzen führen.
  • Verstecktes Verhalten: Overrides sind im Back Office nicht sichtbar, was das Debugging erschwert.
  • Index-Beschädigung: Das class_index.php-Mapping kann beschädigt werden und den gesamten Shop lahmlegen.
Das PrestaShop-Kernteam rät seit Version 1.7 aktiv von Overrides ab. Wenn Sie zu einem Override greifen möchten, prüfen Sie zunächst, ob ein Hook oder eine Symfony-basierte Alternative existiert.

Override-Alternativen (PS 8+)

Symfony Service Decorators

Umhüllen Sie Kerndienste mit Ihrer eigenen Logik — mehrere Decorators können ohne Konflikte verkettet werden:

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

Doctrine Event Listeners

Reagieren Sie auf Entity-Änderungen, ohne ObjectModel zu überschreiben:

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

CQRS Commands und Queries

Klinken Sie sich über Command/Query-Handler in Admin-Operationen ein. Der modernste Ansatz, der Vertrautheit mit der Symfony-Architektur von PrestaShop erfordert.

Override-Konflikte lösen

Wenn zwei Module dieselbe Methode überschreiben, sehen Sie:

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

Die Lösung ist manuelles Zusammenführen — kombinieren Sie die Logik beider Module in einer einzigen Override-Datei. Bauen Sie nach der Bearbeitung den Klassenindex neu auf:

# Delete class index — PrestaShop rebuilds automatically
rm var/cache/prod/class_index.php
rm var/cache/dev/class_index.php
Manuelles Zusammenführen von Overrides ist fragil. Wenn eines der Module aktualisiert wird, müssen Sie möglicherweise erneut zusammenführen. Dokumentieren Sie, was Sie zusammengeführt haben und warum.

Wenn ein Modul ein Override verwendet, obwohl ein Hook funktionieren würde, kontaktieren Sie den Entwickler und fordern Sie eine Hook-basierte Lösung an.

Hook-Priorität und Ausführungsreihenfolge

Wenn mehrere Module auf demselben Display Hook registriert sind, bestimmt die Position die Reihenfolge. Verwalten Sie dies unter Design → Positionen, wo Sie Module per Drag-and-Drop verschieben, aushängen oder auf andere Hooks transplantieren können.

Transplantieren

Verschieben Sie ein Modul von einem Hook auf einen anderen über Design → Positionen → Modul transplantieren. Nicht alle Transplantationen werden korrekt dargestellt — das hängt von den Templates des Moduls ab.

Programmatische Positionierung

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

Hooks debuggen

Systematische Prüfungen

-- 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';

Häufige Probleme

SymptomUrsacheLösung
Methode wird nie aufgerufenNicht auf Hook registriertps_hook_module prüfen; Modul zurücksetzen
Gibt leeren String zurückTemplate nicht gefundenTemplate-Pfad prüfen, Fehler aktivieren
Wird auf falschen Seiten ausgelöstKeine Seitenprüfunginstanceof ProductController hinzufügen
Wird zweimal ausgelöstDoppelt registriertAuf doppelte Einträge prüfen
$params['product'] ist ein ArrayPS 1.7+ verwendet Arrays$params['product']['id_product'] verwenden

Hook-Profiling

Aktivieren Sie Profiling, um die Zeitmessung pro Hook zu sehen:

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

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

Änderungen in PrestaShop 9

Neue Hooks

PS 9 fügt Hooks in Bereichen hinzu, die zuvor Overrides erforderten: Erweiterungen des Admin-Produktformulars, Anwendung von Warenkorbpreisregeln, API-Ressourcenoperationen und der E-Mail-Versandprozess.

Veraltete Hooks

Hooks, die an Legacy-Admin-Controller gebunden sind, werden als veraltet markiert, sobald diese Controller auf Symfony migriert werden. Die vollständige Liste finden Sie unter _PS_DEPRECATED_HOOKS_.

Zukunft des Override-Systems

Overrides funktionieren weiterhin für Legacy-ObjectModel-Klassen, aber neue Symfony-Dienste und Admin-Controller können sie nicht verwenden. Das Override-System wird letztendlich entfernt, wenn die Symfony-Migration abgeschlossen ist.

Wenn Sie heute ein neues Modul starten, bauen Sie ausschließlich mit Hooks und Symfony-Diensten. Jedes Override, das Sie jetzt schreiben, ist technische Schuld, die Sie später beseitigen müssen.

Kurzreferenz

// 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;

Beherrschen Sie zuerst Hooks — sie decken die große Mehrheit der Anwendungsfälle ab. Lernen Sie Overrides, um Legacy-Code zu verstehen und Konflikte zu lösen. Für PrestaShop 8+ investieren Sie in Symfony-basierte Alternativen. Das Ökosystem entwickelt sich weiter, und Hooks führen dabei den Weg.

More guides available

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

Loading...
Back to top