Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PS Hook Inspector

A PrestaShop module that answers two questions every developer asks when working on someone else's shop: "where did this block come from?" and "why isn't this block showing up?"

When enabled, a logged-in employee sees a toggle on the front office. Clicking it outlines every module-rendered region on the page, labels it with the module name and hook, and opens a side panel listing every hook on the current page — including the modules that are attached but rendered nothing.

The problem

PrestaShop merges the output of every module attached to a hook into a single string. Once it reaches the browser, there is nothing left to tell you which markup came from which module. The usual answer is grepping through theme templates and module directories until something matches.

The reverse case is worse. A block that should be there and isn't gives you no signal at all — the module might be disabled, unhooked, excluded on this controller, hidden for this device, or simply returning an empty string.

What it does

Attribution. Each module's output is wrapped at render time, so the mapping between markup and module is a fact, not a guess.

Absence reporting. The registry in the database is compared against what actually rendered. The difference is the most useful thing this module produces:

  • module attached to the hook but returned no output
  • module excluded on this controller
  • module disabled shop-wide
  • hook itself inactive
  • anything else — reported as unexplained rather than guessed (device visibility isn't decoded yet, see Limitations)

Ordering. Modules within a hook are listed in their configured position order.

How it works

Three points intercept HookCore via override/classes/Hook.php:

  • coreCallHook() and coreRenderWidget() — the two places Hook::exec() actually calls into a module, for classic hookXxx() methods and WidgetInterface::renderWidget() respectively. Each module's output is captured here and wrapped in HTML comments:

    <!-- psi:start mod="ps_featuredproducts" hook="displayHome" -->
    ...
    <!-- psi:end mod="ps_featuredproducts" -->

    A module invoked directly via {widget name='...'} in a template — bypassing the hook system entirely — is captured the same way, marked widget="1" instead of a hook name.

  • exec() itself — a thin passthrough that records which hooks a page invoked at all, including ones with zero effect (no modules attached, or the hook itself inactive). coreCallHook() and coreRenderWidget() never see those cases, since Hook::exec() filters them out internally before reaching either.

On the client, views/js/inspector.js locates each marker pair, builds a Range between them, and outlines it using Range.getBoundingClientRect() — one box per region. (An earlier version used getClientRects(), one rectangle per line box, specifically to handle text wrapping precisely — but on a real shop that meant every individual inline element in a region, every link in a menu for instance, got its own separate outline instead of one outline around the module's output. One box per region reads far better in practice.)

A second, server-side pass compares what actually rendered against PrestaShop's own hook registry to explain modules that never rendered anything — excluded on this controller, disabled shop-wide, or the hook itself inactive. This diff is emitted as a small JSON blob near the end of the page and read by the same front-end script, which groups the side panel by hook, ordered by where each hook actually renders on the page rather than by the order PHP happened to call Hook::exec() — with a collapsible group per hook, a filter box, and a click on any rendered entry that scrolls to and highlights its outline.

Outlines and the panel live in a separate overlay layer above the page. Nothing on the page has its styles modified, so the theme layout is never disturbed by the inspector itself.

Tested configuration

  • PrestaShop: 8.2.6
  • PHP: 8.2
  • Theme: classic
  • Multistore: not verified

Requirements

  • A signed, time-limited inspection link issued from the module's own back-office page (see Security). Being logged into the back office alone does not make markers appear — PrestaShop's front office and back office don't share a session, so there's no "is an employee logged in" check the front office could perform on its own.
  • No other module overriding coreCallHook(), coreRenderWidget(), or exec() on Hook (see Limitations).

Installation

  1. Copy the module directory to modules/pshookinspector/.
  2. Install it from Back Office → Modules, or via CLI.
  3. Enable inspection under the module's own Configure page.
  4. Click "Open front office in inspection mode" on that same page — it opens the front with a signed, time-limited token that authorizes markers for that browser. Being logged into the back office alone is not enough; the front office cannot see that session (see Security).

Usage

  • Toggle — turns outlines and the panel on and off, remembered for the current tab.
  • Outline labels — module name, and hook name where applicable.
  • Side panel — grouped by hook, ordered by where each hook actually renders on the page. A dedicated "Widgets" group lists modules invoked directly via {widget}. Click any rendered entry to scroll to and highlight its outline. A filter box narrows the list by hook or module name.
  • Status per module — rendered, empty output, excluded on this controller, disabled, hook inactive, or unexplained.

Security

The inspector exposes the full module and hook structure of the shop, which is exactly the kind of reconnaissance data that should not be available publicly. Three safeguards apply:

  1. Rendering is gated on a signed, time-limited token — issued only from an authenticated back-office action, and verified by its signature, not by the mere presence of the cookie carrying it. A forged or expired cookie is rejected server-side. (PrestaShop's front office and back office don't share a session, so a plain "is an employee logged in" check on the front is not possible — this token is the bridge.)
  2. Inspection mode is off by default and must be enabled explicitly in the back office.
  3. When inspection mode is disabled, no markers are emitted at all.

Treat inspection mode as a development setting. Leaving it on in production is not a supported configuration.

Caching

If Smarty caching or full-page caching is active, markers are stored in the cache along with the rest of the output. Disabling inspection would then leave the comments in production HTML.

The module invalidates the relevant caches when inspection mode is toggled. If you use an external cache layer (Varnish, a CDN, a third-party cache module), purge it manually after switching the mode.

Limitations and known issues

  • Overrides collide. PrestaShop allows only one override per class. If another module already overrides Hook, installation will conflict and must be resolved manually.
  • Widgets called directly in templates. {widget name='...'} bypasses the hook system entirely. Such output is detected separately and is labelled as a widget, without hook or position data.
  • Action hooks. Hooks that perform work rather than returning markup are filtered out and never wrapped.
  • Hook aliases. Modules may be registered under a legacy hook name. Aliases are resolved through hook_alias; if a module appears missing from the mapping, an unresolved alias is the first thing to check.
  • Multistore. Status data is per shop. Results are filtered by the current id_shop.
  • Device visibility isn't decoded. Whether a module was hidden for the current device class isn't checked — the exact mechanism was never confirmed against PrestaShop's own source, and guessing wrong would produce a false diagnosis, which is worse than none. Such modules currently show as unexplained rather than a specific reason.
  • No fallback if markers are stripped. A minifier or an aggressive cache layer that strips HTML comments would silently empty out the panel rather than falling back to a DOM-heuristic guess with an uncertainty label, which was the original design.

Roadmap

  • Export the current page's hook map as JSON
  • Template origin — which template file issued the hook call
  • Render timing per module
  • Filter the panel by module

License

MIT License. See LICENSE.

About

See which module rendered which block on a PrestaShop page — and why the missing ones didn't

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages