Задача
Ускорить витрину магазина, не залезая в шаблоны темы. Правки в чужой теме живут до первого обновления, а на платных темах ещё и лишают поддержки.
Решение через событие
Модуль вешается на событие catalog/view/*/after и правит уже готовый HTML страницы. Тема не трогается вообще, модуль работает с любой — включая платные.
Ловушка, из-за которой такие модули молча не работают
Ядро OpenCart 4 триггерит событие как view/(маршрут)/after, без префикса. Но в базе триггер должен храниться с префиксом catalog/: файл catalog/controller/startup/event.php снимает первый сегмент при регистрации и по нему же отличает события витрины от админских. Ошибка в любую сторону — событие не срабатывает, и никакого сообщения об ошибке вы не увидите. Я проверил это по исходникам 4.1.0.3, а не по документации.
Три решения, которые отличают работающий модуль от вредного
Не все картинки ленивые. Повесить loading="lazy" на всё — типичная ошибка: отложенная картинка в первом экране замедляет страницу, а не ускоряет. Первые изображения остаются жадными, число настраивается.
Размеры берутся с диска. width и height читаются из настоящего файла, а не выдумываются. Браузер резервирует место заранее, и макет не прыгает при загрузке.
WebP через <picture>, а не подменой src. Исходный <img> остаётся внутри и служит запасным вариантом: браузер без поддержки WebP покажет обычный файл. Подмена src такой гарантии не даёт.
Чужие атрибуты не переписываются: если в теме уже задан loading или проставлены размеры, значит у разработчика была причина.
Как проверено
Логика вынесена в класс, который ничего не знает про OpenCart, — поэтому её можно гонять без магазина и без базы. 27 проверок: подстановка атрибутов, чтение размеров с диска, оборачивание в <picture>, сохранность классов темы и alt, работа магазина в подпапке, отказ выходить за корень сайта по ../, разметка карточки товара и корректность самозакрывающихся тегов.
Отдельно сверено с исходниками 4.1.0.3: формат триггера, наличие используемых методов моделей, разбор маршрута с точкой, константы путей. Шаблон настроек проверен разбором через Twig 3.
Собственный проект. В установленном магазине модуль не запускался — это указано и в документации к нему.