7.8 KiB
Architecture
Analysis Date: 2026-05-10
Pattern Overview
Overall: PrestaShop 1.7.8.x MVC e-commerce monolith with hook-based module extensibility and a Symfony admin layer.
Key Characteristics:
- Front-office: legacy PrestaShop dispatcher + Smarty templates
- Back-office (admin): Symfony 4-style kernel (
app/AppKernel.php) at non-standard pathiadmin/ - Hook-based publish/subscribe extension model — modules attach to named hooks
- Core class overrides via
override/classes/(monkey-patch layer) - Multi-domain (
drmaterac.pl+lulandia.pl) served from a single install via.htaccessrules
Layers
Entry / Routing Layer:
- Purpose: Route HTTP requests via mod_rewrite to dispatcher / Symfony kernel
- Contains:
.htaccess(429 lines),index.php(front),iadmin/index.php(back-office) - Used by: All HTTP requests
- Key class:
classes/Dispatcher.php(overridden inoverride/classes/Dispatcher.php)
Controller Layer:
- Purpose: Handle request-specific logic (cart, product, checkout, search)
- Contains:
controllers/front/*.php(CartController, OrderController, ProductController…) - Base classes:
classes/controller/FrontController.php,ModuleFrontController.php,AdminController.php,ProductListingFrontController.php - Overrides:
override/classes/controller/FrontController.php,override/classes/controller/ProductListingFrontController.php
Model / Domain Layer (ObjectModel):
- Purpose: ORM-style domain entities (Product, Cart, Order, Customer, Category…)
- Contains:
classes/Product.php,classes/Cart.php,classes/Order.php,classes/Customer.php, etc. - Base:
classes/ObjectModel.php - Overrides: 31 files in
override/classes/(e.g.override/classes/Product.phpadds Google Shopping fields;override/classes/Cart.phpextendsgetOrderTotal()for ets_promotion pricing)
Module Layer (extensibility):
- Purpose: Optional features attached via hooks; first-class extension mechanism
- Contains:
modules/*/(50+ installed modules, ~138 entries on disk) - Custom in this project:
modules/crosssellpro/(Cross Sell PRO carousel),modules/caraty/ - Active third-party:
modules/paynow/,modules/santandercredit/,modules/empikmarketplace/,modules/dpdpoland/,modules/pdgoogleanalytycs4pro/,modules/fbpixel/,modules/ekomiratingsandreviews/,modules/ceneo_trustedreviews/, etc.
Theme / Presentation Layer:
- Purpose: Page templates and visual assets
- Contains:
themes/leo_gstore/(active child theme, Leo Gstore v1.4) - Templates:
themes/leo_gstore/templates/*.tpl(layout, cart, checkout, product…) - Per-module overrides:
themes/leo_gstore/modules/<module>/views/templates/hook/*.tpl - Assets:
themes/leo_gstore/assets/cache/(bundled JS/CSS)
Cross-cutting / Hook System:
classes/Hook.php+override/classes/Hook.php(PageCache module integration)- Hooks dispatch from controllers/templates → fire each registered module's
hook<Name>()method - Registration table:
ps_hook_module
Data Flow
HTTP Request lifecycle (front-office):
- Apache receives request →
.htaccessrewrite rule (line ~353) routes non-physical URLs toindex.php Dispatcher::getInstance()->dispatch()resolves URL to a controller (e.g.CartController)- Controller
init()/initContent()runs; sets upContext(cart, customer, shop, language) - Controller dispatches display hooks (
displayHeader,displayShoppingCartFooter,actionFrontControllerSetMedia, etc.) - Each subscribing module's
hook<Name>()method runs and may render Smarty partials - Smarty renders the theme template (
themes/leo_gstore/templates/cart.tpl) injecting hook output - Response (HTML + CSS + JS) returned to client
Admin lifecycle:
iadmin/index.phpbootsapp/AppKernel.php(Symfony) —iadmin/is the renamed admin folder (security-by-obscurity)- Symfony routes to controllers; falls back to legacy
Dispatcherif Symfony routing fails (iadmin/index.phpline ~93)
API:
webservice/dispatcher.php— REST endpoint reached via.htaccessline ~323
State Management:
- Per-request state held in
classes/Context.phpsingleton ($context->cart,$context->customer,$context->shop,$context->controller,$context->language) - Persistent state in MySQL (
materac_*tables) - Caching configured but disabled (Memcached driver inactive)
Key Abstractions
Module:
- Purpose: Attachable feature unit registering hook callbacks
- Examples:
modules/crosssellpro/crosssellpro.php,modules/paynow/paynow.php,modules/fbpixel/fbpixel.php - Pattern: subclasses
Module(classes/Module.php);install()registers hooks;hook<Name>()methods are invoked dynamically
ObjectModel:
- Purpose: ORM base for domain entities — handles
add(),update(),delete(), hydration, validation - Examples:
classes/Product.php,classes/Cart.php,classes/Order.php - Pattern: declarative
$definitionarray describes table + fields
Hook (publish/subscribe):
- Purpose: Decouple core from extensions
- Examples:
displayShoppingCartFooter,displayCheckoutSummaryTop,displayHeader,actionFrontControllerSetMedia - Pattern:
Hook::exec('hookName', ...)dispatches;$module->hookHookName(...)receives
Context:
- Purpose: Per-request global container (cart, customer, shop, language, controller)
- Pattern: Singleton —
Context::getContext()
Override:
- Purpose: Patch core class behavior without editing core
- Examples:
override/classes/Cart.php(custom totals),override/classes/Hook.php(PageCache hooks) - Pattern: Files in
override/classes/<X>.phpoverrideclasses/<X>.phpvia PrestaShop autoloader
Entry Points
Front-office:
- Location:
index.php(root) — invoked implicitly via.htaccess - Triggers: Any non-physical URL on the public domains
- Responsibilities: Bootstrap → Dispatcher → controller dispatch
Back-office:
- Location:
iadmin/index.php(custom-named admin folder) - Triggers: Admin URL access
- Responsibilities: Boot
AppKernel, fall back to legacy dispatcher when needed
Web service:
- Location:
webservice/dispatcher.php - Triggers: REST API calls
Custom Cross-Sell module:
- Location:
modules/crosssellpro/crosssellpro.php - Hooks registered (in
install()):displayShoppingCartFooter,displayCheckoutSummaryTop,displayHeader,actionFrontControllerSetMedia - Key methods:
hookDisplayShoppingCartFooter(),hookDisplayCheckoutSummaryTop(),buildCrossSellProducts(),collectAccessoryIds(),getCombinationFlags(),presentProducts() - Templates:
modules/crosssellpro/views/templates/hook/cartCrossSell.tpl,…/checkoutCrossSell.tpl - Front assets:
modules/crosssellpro/views/css/cartCrossSell.css,modules/crosssellpro/views/js/cartCrossSell.js
Error Handling
Strategy: PrestaShop legacy approach — Tools::displayError(), exceptions logged to iadmin/errors.log and errors.log (root). Exceptions in hooks may silently degrade module output.
Patterns:
try/catchrare in custom code;import-product.phplacks transaction wrapping/rollback- Mail send paths in
buy-by-phone.phpminimally handle PHPMailer failures errors.logat repo root +iadmin/errors.log(5 MB historical) — both inside webroot (concern, seeconcerns.md)
Cross-Cutting Concerns
Logging:
- File-based:
errors.log(root),iadmin/errors.log - Symfony Monolog wired in admin via
MonologBundle
Caching:
pagecachemodule- Memcached configured but disabled (
app/config/parameters.php) - Smarty compiled templates:
themes/leo_gstore/cache/
Authentication:
- Customer auth: PrestaShop core (cookies + DB)
- Admin auth: Symfony Security via
iadmin/
Image handling:
- WebP delivery via
.htaccesscontent negotiation +modules/x13webp/ - Lazy-loading image override (
pshowlazyimgoverride)
Architecture analysis: 2026-05-10 Update when major patterns change