Плагины MODX и системные события: типичные ошибки
Плагин MODX может сработать не там, не тогда и не один раз. Разбираем привязку к событиям, параметры, порядок выполнения, работу с кэшем и ошибки, которые особенно дорого обходятся на рабочем сайте.

В MODX один и тот же плагин может выглядеть исправным в админке, но не срабатывать на нужной странице, запускаться дважды или сохранять данные только до очистки кэша. Чаще всего причина не в PHP-коде, а в неправильном системном событии, неверно прочитанных параметрах и отсутствии проверки контекста. Ниже — практический разбор для MODX Revolution 2.8.x и MODX 3.x: что проверять в первую очередь, какие подходы выбирать и где плагины ломаются в продакшене.
Сначала определите момент запуска
Плагин MODX не запускается сам по себе. Он привязывается к одному или нескольким системным событиям, а MODX вызывает его, когда доходит до соответствующего этапа запроса или действия в менеджере.
Это важное различие. Если нужно изменить данные ресурса при сохранении, событие рендера страницы не подходит. Если нужно добавить заголовок в готовый HTML, событие сохранения ресурса тоже не поможет.
| Задача | Подходящее направление | Цена ошибки |
|---|---|---|
| Проверить или изменить данные при сохранении ресурса | События сохранения ресурса, например OnDocFormSave | Изменения не применятся или будут записаны повторно |
| Изменить поведение запроса до загрузки страницы | События обработки запроса, например OnHandleRequest | Редиректы и проверки могут сработать слишком поздно |
| Изменить готовый HTML перед отправкой | События рендера или завершения страницы | Можно получить неполный HTML или лишнюю нагрузку на каждый запрос |
| Реагировать на создание или изменение пользователя | События сохранения пользователя | Обработчик начнёт реагировать на ресурсы или другие объекты |
Системное событие — точка в жизненном цикле MODX, в которой платформа передаёт управление подключённым плагинам. У события есть имя и набор параметров; эти параметры различаются в зависимости от события.
Главная ошибка в MODX — писать код до того, как вы проверили, какое событие его запускает и какие данные оно действительно передаёт.
Ошибка 1. Один плагин подписан на всё
Вкладка с системными событиями позволяет отметить несколько пунктов. Это удобно для небольшого обработчика, но опасно для плагина с бизнес-логикой. Один файл начинает выполняться при загрузке страницы, сохранении ресурса и действиях в менеджере. В итоге появляются лишние запросы к базе, повторная обработка и трудно объяснимые побочные эффекты.
Для небольшого проекта допустим общий плагин с разветвлением по имени события. Для нескольких независимых задач лучше создать отдельные плагины. Так проще отключить один сценарий, посмотреть журнал и понять, что именно вызвало ошибку.
<?php
switch ($modx->event->name) {
case 'OnDocFormSave':
// Логика только для сохранения ресурса.
break;
case 'OnHandleRequest':
// Логика только для обработки входящего запроса.
break;
}
Такой шаблон подходит для MODX Revolution 2.8.x и MODX 3.x, но названия событий и параметры нужно сверять с документацией вашей версии. Если обработчик подписан только на одно событие, проверка через switch всё равно полезна: она защищает от случайной повторной привязки.
Ошибка 2. Плагин читает не те параметры
Событие передаёт параметры через $scriptProperties, но состав параметров зависит от точки запуска. Для сохранения ресурса обычно нужен его идентификатор, а для обработки запроса могут быть важны данные текущего контекста и объекта запроса. Нельзя переносить имена переменных из одного плагина в другой без проверки.
Минимальный безопасный каркас для обработчика сохранения ресурса выглядит так:
<?php
if ($modx->event->name !== 'OnDocFormSave') {
return;
}
$resourceId = (int) ($scriptProperties['id'] ?? 0);
if ($resourceId === 0) {
$modx->log(modX::LOG_LEVEL_ERROR, 'OnDocFormSave: не передан id ресурса');
return;
}
$resource = $modx->getObject('modResource', $resourceId);
if (!$resource) {
$modx->log(modX::LOG_LEVEL_ERROR, 'OnDocFormSave: ресурс не найден');
return;
}
// Здесь размещается только логика, связанная с сохранённым ресурсом.
Код не должен считать, что ресурс всегда найден, идентификатор всегда числовой, а сохранение всегда произошло в веб-контексте. В пакетной обработке, импорте или работе менеджера набор параметров может отличаться. Для конкретного события проверяйте документацию и временно записывайте в журнал только имена полученных параметров, не содержимое паролей и токенов.
Ошибка 3. В обработчике нет защиты от повторного запуска
Сохранение ресурса может запускать дополнительную логику, которая снова меняет и сохраняет тот же объект. Если плагин подписан на событие сохранения, возникает цикл: сохранение вызывает плагин, плагин снова сохраняет ресурс, а событие запускается повторно.
Частая ошибка — безусловно вызывать $resource->save() внутри обработчика, даже если значение не изменилось. Сначала сравните старое и новое значение. Если изменение не нужно, завершите обработку без записи.
Не рассчитывайте только на глобальную переменную или флаг в памяти процесса. В обычных PHP-запросах следующий запрос получит новое состояние. Для защиты от повторной обработки используйте проверку изменения, отдельное поле состояния или очередь — конкретный вариант зависит от сценария.
Ошибка 4. Плагин работает в кэше и ломает ожидания
Кэш MODX может скрыть результат корректного обработчика. Вы изменили ресурс, но на странице видите старое значение, потому что уже сохранённый HTML или фрагмент не был инвалидирован. Обратная ситуация тоже встречается: очистка кэша заставляет тяжёлый плагин выполниться сразу на большом числе запросов.
- Не используйте событие вывода страницы для записи данных в базу, если задачу можно выполнить при сохранении.
- После изменения шаблона, чанка или ресурса отдельно проверяйте, очищается ли нужный кэш.
- Не вызывайте принудительную очистку всего кэша на каждый просмотр страницы.
- Проверяйте результат в приватном окне и в том же контексте, где работает пользователь.
- Если значение зависит от прав пользователя, языка или контекста, не храните один общий закэшированный результат для всех.
Инвалидация кэша — отдельная тема. Здесь важен принцип: событие изменения данных и событие формирования ответа не должны подменять друг друга.
Ошибка 5. Тяжёлая логика выполняется на каждом запросе
Плагин на событии рендера страницы может запускаться для каждого посетителя. Один запрос к базе ещё не выглядит проблемой, но запрос к внешнему сервису, перебор большого списка ресурсов или разбор изображения быстро превращаются в задержку для всего сайта.
Разделяйте синхронную и фоновую работу:
- В запросе пользователя выполните только проверку, без которой нельзя сформировать ответ.
- Соберите идентификаторы и параметры операции.
- Передайте тяжёлую обработку в cron, консольную команду или очередь, если это предусмотрено вашей инфраструктурой.
- Запишите длительность и результат, чтобы видеть не только факт ошибки, но и рост времени выполнения.
Если фоновый механизм не настроен, лучше явно ограничить объём работы и поставить тайм-аут внешнего запроса. Плагин не должен ждать внешний сервис бесконечно.
Как выбирать архитектуру плагина
| Вариант | Когда выбирать | Риск |
|---|---|---|
| Один плагин и один обработчик события | Простая локальная логика: проверка поля или небольшое изменение | Плагин быстро разрастается, если добавлять новые сценарии |
Один плагин с switch по событиям | Несколько коротких действий, связанных с одним доменом | Общие переменные и побочные эффекты между ветками |
| Несколько плагинов по зонам ответственности | Разные процессы: ресурсы, пользователи, вывод страницы | Сложнее найти весь процесс без документации и соглашения об именах |
| Плагин как тонкий слой над отдельным классом или сервисом | Логика используется в нескольких местах и требует тестирования | Нужно отдельно загрузить и поддерживать код сервиса |
Критерий простой: если обработчик нельзя проверить без запуска полного запроса MODX или в нём появились несколько независимых веток, пора выносить логику из тела плагина.
Что ломается чаще всего
- Плагин не срабатывает. Проверьте, включён ли он, отмечено ли нужное системное событие и работает ли сайт в ожидаемом контексте.
- Срабатывает не там. Уберите лишние события и добавьте журналирование имени события и идентификатора объекта.
- Ошибка «Call to a member function ... on null». Объект не найден или параметр события отсутствует. Проверяйте результат
getObject()до вызова метода. - Данные меняются дважды. Ищите повторный вызов
save(), несколько привязанных плагинов и рекурсивное событие сохранения. - На рабочем сайте всё медленно. Измерьте время обработчика, число запросов к базе и обращения к внешним сервисам отдельно для менеджера и сайта.
Что проверить перед публикацией
- Зафиксируйте версию MODX, PHP и имя каждого системного события.
- Запишите, какие параметры событие передаёт в вашей версии.
- Проверьте обработчик на отсутствующем объекте, пустом поле и повторном запуске.
- Убедитесь, что плагин не пишет в базу на каждом просмотре страницы без необходимости.
- Проверьте работу после очистки кэша и без неё.
- Посмотрите журнал ошибок на сценариях менеджера, сайта и консольного запуска.
Что делать дальше
Сначала составьте карту событий для своего плагина: когда он запускается, какие параметры получает и что меняет. Затем добавьте измерение длительности и протестируйте два сценария — обычное сохранение и повторный вызов. Для точных названий событий, параметров и различий между MODX Revolution 2.8.x и MODX 3.x сверяйтесь с официальной документацией конкретной версии, а не с фрагментами из старых сниппетов.
Проектная работа: От проблемы до защиты: продукт, этапы, вопросы комиссии
Спросить за 15 ₽Источники: MODX Documentation: Plugins, MODX Documentation: System Events
Частые вопросы
Почему плагин MODX не срабатывает после сохранения ресурса?
Можно ли привязать один плагин сразу к нескольким событиям?
Как избежать повторного запуска плагина при сохранении ресурса?
Почему после работы плагина на странице остаются старые данные?
Что делать, если у меня другая версия MODX или PHP?
Что делать, если плагин работает в менеджере, но не на сайте?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.