Универсальная отладка Cotonti: подробное руководство
универсальный способ отладки: единый обработчик, который регистрируется один раз в точке входа и собирает полную картину сбоя. Он не трогает ядро, не мешает выводу страницы и включается одним переключателем.
Универсальная отладка Cotonti: подробное руководство
Содержание
- Введение
- Проблемная ситуация
- Почему стандартные средства не помогают
- Что даёт универсальный обработчик
- Требования
- Установка: пошагово
- Настройка: флаги и режимы
- Где лежит лог-файл
- Структура отчёта: поле за полем
- Практика: чтение отчёта
- Типовые сценарии
- Отключение и удаление
- Частые вопросы
1. Введение
Cotonti — модульный фреймворк. Помимо ядра в нём работают десятки расширений: модули, плагины, темы, шаблоны. Когда что-то ломается, источник проблемы часто находится не там, где о ней сообщает PHP. Это делает диагностику неочевидной.
Особенно, с ног сбивает
Deprecated: str_replace(): Passing null to parameter #3 ($subject) of type array|string is deprecated in /system/cotemplate.php on line 1923
Данное руководство описывает универсальный способ отладки: единый обработчик, который регистрируется один раз в точке входа и собирает полную картину сбоя. Он не трогает ядро, не мешает выводу страницы и включается одним переключателем.
Руководство рассчитано на администратора сайта или разработчика, знакомого с базовыми понятиями PHP и структурой Cotonti. Специальных навыков не требуется.
2. Проблемная ситуация
Классический случай, ради которого обычно и начинается диагностика:
Deprecated: str_replace(): Passing null to parameter #3 ($subject)
of type array|string is deprecated
in /path/to/site/system/cotemplate.php on line 1923Суть проблемы. Шаблон содержит тег — например {PHP.c}, {MARKET_SOMETHING} или любой другой. Тегу не было присвоено значение. Шаблонизатор при обработке возвращает null. Дальше null передаётся в функцию str_replace() внутри ядра. Начиная с PHP 8.1 передача null в эту функцию считается устаревшей конструкцией. Отсюда предупреждение.
Похожие ситуации:
Warning: Undefined array keyиз глубокого вызова — невозможно понять, откуда пришёл вызов.Deprecatedв модуле, но сообщение указывает на функцию ядра.- Ошибка в плагине, но в тексте — только имя общей функции.
Общее у всех: сообщение показывает место сбоя, но не показывает причину и виновника. Между этими двумя понятиями в Cotonti может быть 5–10 уровней вызовов.
3. Почему стандартные средства не помогают
Обычные подходы и их ограничения:
Правка шаблона. Проблема не в шаблоне, а в том, что код модуля не присвоил тегу значение. Правка шаблона тут ничего не даёт.
Постановка var_dump. Требует правки конкретного файла. Если файл неизвестен — надо править по очереди ядро, шаблонизатор, плагины, шаблоны. Каждая правка — временная, потом откатывать. При нескольких ошибках процесс затягивается на часы.
Правка ядра. Универсальнее, но при обновлении Cotonti все правки теряются. Плюс изменённое ядро — источник будущих проблем, которые сложно отследить.
Встроенные логи PHP. Логируют факт ошибки, но не дают контекст: ни URL, ни стека, ни имени тега.
Отладчик типа Xdebug. Мощный инструмент, но требует настройки сервера, IDE и воспроизведения сбоя в отладочной сессии. Для боевого сайта это часто неприемлемо — сбой возникает у клиента, а не у разработчика.
Плагины для разработчика. Есть готовые, но обычно они либо перегружены, либо завязаны на конкретную версию Cotonti. Собственный обработчик проще и надёжнее.
4. Что даёт универсальный обработчик
Вместо всех перечисленных способов — один перехватчик ошибок PHP, который:
Регистрируется один раз. В index.php, в точке входа. Больше нигде правок не требуется.
Не трогает ядро. Файлы system/ и все модули остаются в исходном виде. Обновление Cotonti не сломает отладку.
Работает на всех страницах. Любой запрос, любой модуль, любой плагин.
Не вмешивается в вывод. Отчёт пишется в отдельный файл. Вёрстка страницы остаётся нетронутой, AJAX работает, посетители ничего не замечают.
Включается и выключается одной переменной. Ничего резать и вставлять не нужно.
Настраивается по задаче. Ловить ошибки из одного файла, из всего ядра или из всего проекта.
Собирает контекст. Имя тега, файл шаблона, файл-виновник, имя модуля, стек вызовов, URL, GET/POST, окружение — всё попадает в отчёт.
Отсеивает шум. По умолчанию реагирует только на нужный класс ошибок, не засоряя лог случайными предупреждениями.
5. Требования
- Cotonti версии, поддерживающей PHP 8.x.
- Доступ на редактирование файла
index.phpв корне сайта. - Права на запись в директорию, где лежит
index.php, для пользователя веб-сервера. - Понимание базовой структуры Cotonti: где
index.php, гдеsystem/, гдеplugins/, гдеmodules/. - Никаких сторонних библиотек и расширений устанавливать не требуется.
6. Установка: пошагово
Шаг 1. Открыть index.php.
Файл находится в корне сайта. Открывается любым редактором с подсветкой PHP.
Шаг 2. Найти точку вставки.
В файле есть две строки, между которыми вставляется блок отладки:
- строка
const COT_CODE = true;— верхняя граница; - строка
require_once './datas/config.php';— нижняя граница.
Эти строки не меняются. Они остаются на месте. Между ними — пустое место, куда вставляется новый код.
Шаг 3. Вставить блок отладки.
Между двумя указанными строками вставить код обработчика (предоставляется отдельным файлом). При вставке следить за отступами — они должны совпадать с окружающим кодом, для читаемости.
Шаг 4. Проверить права.
Убедиться, что пользователь веб-сервера имеет право на запись в директорию, где лежит index.php. Это нужно для создания лог-файла. Если по каким-то причинам запись в корень запрещена, файл лога можно создать заранее вручную и назначить ему владельца.
Шаг 5. Сохранить файл.
Сохранить index.php. Никаких других файлов менять не нужно.
Шаг 6. Открыть любую страницу сайта.
Перейти на страницу, где возникает ошибка, или просто открыть главную — обработчик начнёт работать с первого же запроса. Первые записи появятся сразу, как только сработает условие.
Шаг 7. Проверить, что лог создан.
В директории с index.php должен появиться файл cot_index_debug.log. Если он не появился — проверить права (см. Шаг 4). Если появился и не пуст — обработчик работает.
7. Настройка: флаги и режимы
В начале блока отладки — три переменные. Ими управляется всё поведение. Ничего больше трогать не нужно.
Переменная включения. Мастер-флаг. Значение true — отладка работает. Значение false — обработчик не регистрируется, блок фактически отключён. Это позволяет оставить код в index.php насовсем и включать его только при необходимости.
Переменная режима отбора. Определяет, из каких файлов ловить ошибки:
'list'— ловить только из файлов, перечисленных в списке (см. ниже);'all'— ловить из всех файлов проекта: ядро, модули, плагины, темы.
Список файлов. Используется только в режиме 'list'. Это массив имён файлов, которые нужно мониторить. Сравнение идёт по частичному совпадению пути, поэтому достаточно указать имя без директории — например cotemplate.php поймает файл с таким именем в любой папке.
Логика применения:
- Одна ошибка в шаблонизаторе → режим
'list', в списке толькоcotemplate.php. - Аудит ядра → режим
'list', в списке все файлы изsystem/. - Полная диагностика по всем расширениям → режим
'all'.
Переключение делается заменой одного символа в переменной режима. Ничего резать, ничего вставлять — только менять значение.
8. Где лежит лог-файл
Файл: cot_index_debug.log. Расположение: в той же директории, что index.php. То есть в корне сайта.
Режим записи — дописывание. Это означает, что при каждом срабатывании новый блок добавляется в конец файла. Старые записи не стираются. Если нужно прочитать свежие данные — смотреть в конец файла. Если нужно понять, повторяется ли одна и та же ошибка — искать по всему файлу.
Права: файл должен быть доступен на запись пользователю веб-сервера. Если сайт работает под www-data, файл и директория должны принадлежать ему или быть доступными на запись.
Размер: при активном мониторинге лог растёт быстро, особенно в режиме 'all'. Рекомендуется периодически его очищать. Содержимое можно удалять без последствий — при следующем срабатывании он создастся заново. Если файл нужно временно «заморозить», не удаляя, — достаточно выключить отладку флагом включения.
9. Структура отчёта: поле за полем
Каждое срабатывание — это отдельный блок, обрамлённый разделителями. Внутри — фиксированный набор полей.
Дата и время. Момент срабатывания. Полезно для сопоставления с другими логами: журналом веб-сервера, логами PHP, логами сторонних сервисов.
Код ошибки. Числовой код, который PHP присвоил ошибке. Для Deprecated это 8192. Для Warning — 2. Позволяет отличить типы сообщений друг от друга, если фильтрация была ослаблена.
Текст сообщения. Полная формулировка от PHP. То самое сообщение, которое видно на экране, но здесь — с сохранением всей строки целиком.
Файл ядра и строка. Место, где PHP зафиксировал ошибку. Обычно это либо строка в cotemplate.php, либо в другой функции ядра. Это место сбоя, но не место причины. На этот файл смотреть не надо — он в 99% случаев правильный.
URL. Адрес страницы, на которой произошло срабатывание, вместе с query-строкой. Даёт понимание, при каких запросах воспроизводится проблема.
HTTP-метод. GET, POST и т.д. Иногда помогает понять природу проблемы — например, если ошибка возникает только при POST-запросах.
Referer. Откуда пользователь пришёл. Полезно, если ошибка связана с конкретным переходом внутри сайта.
IP-адрес. Клиент, при запросе которого сработала ошибка. Даёт возможность отфильтровать трафик по конкретному пользователю или сессии.
User-Agent. Строка идентификации браузера или бота. Если ошибка воспроизводится только для определённых клиентов, будет видно здесь.
Имя тега. Ключевое поле при проблемах с шаблонами. Это имя тега, который вернул null. Оформлено в фигурных скобках — чтобы визуально отличалось от прочего текста.
Файл шаблона. Путь к .tpl, в котором встретился проблемный тег. По нему понятно, какой именно шаблон надо открыть.
Файл-виновник. Ключевое поле при диагностике вообще. Это первый PHP-файл вне ядра, откуда пришёл вызов парсинга. Чаще всего именно здесь и находится причина проблемы — забытое присвоение тега, неправильный вызов шаблонизатора и так далее. Указано в формате файл:строка.
Имя модуля или плагина. Определяется по сегменту пути plugins/<name>/ или modules/<name>/. Если ошибка пришла вне плагина или модуля — пишется core. Это даёт быстрое понимание, к какой части проекта относится проблема.
Стек вызовов. Полная цепочка вызовов — от точки ошибки до точки входа. Каждая строка — отдельный фрейм с указанием файла, строки, функции. Для методов указывается класс. Этот блок — самый информативный, но и самый объёмный. Используется, когда остальные поля не дали ответа. Первый файл после ядра в стеке — почти всегда и есть виновник.
Снимок окружения. Тип расширения, локация, ID пользователя. Полезно для понимания, при каких условиях воспроизводится сбой.
GET и POST. Параметры запроса в формате JSON. Иногда источник ошибки лежит именно в данных запроса — например, если клиент передал значение, которого код не ожидал.
Для разных типов ошибок часть полей может быть пустой. Это нормально. Обработчик пытается собрать максимум, но некоторые данные доступны не всегда. При проблемах с шаблоном не будет никакой технической информации о модуле. При ошибках в ядре — не будет имени тега. Смотреть надо на то, что заполнено.
10. Практика: чтение отчёта
Порядок работы с одним блоком.
Шаг 1. Открыть cot_index_debug.log. Перейти в конец файла — там свежие записи.
Шаг 2. Прочитать поле Текст сообщения. Понять класс проблемы: str_replace, Undefined, что-то ещё.
Шаг 3. Прочитать поле Файл-виновник. Это отправная точка. Открыть указанный файл, перейти на указанную строку.
Шаг 4. Посмотреть, что в этой строке происходит. Обычно это вызов $t->parse(...), $t->assign(...) или аналогичная операция с шаблоном. Рядом — код, который формирует данные для этого вызова.
Шаг 5. Если в файле-виновнике всё выглядит нормально — смотреть Имя модуля или плагина. Возможно, проблема в цепочке: файл-виновник вызывается из другого модуля, и там пропущено присвоение.
Шаг 6. Если не помогло — открыть Стек вызовов. Найти первый файл после ядра. Часто он и есть настоящий источник.
Шаг 7. Прочитать Имя тега и Файл шаблона. Убедиться, что в указанном шаблоне действительно есть такой тег, и что в соответствующем PHP-файле он не присвоен.
Шаг 8. Внести исправление. Сохранить. Обновить страницу. Проверить, что новый блок в логе не появился.
11. Типовые сценарии
Ругается шаблон, Deprecated от str_replace. Режим 'list', в списке только cotemplate.php. В отчёте будет полный набор: имя тега, файл шаблона, файл-виновник. Открыть виновника по указанной строке, найти место, где не присвоен тег, добавить присвоение. Обновить страницу — блок из лога должен пропасть.
Плагин падает при определённом запросе. Режим 'all'. В отчёте — URL, HTTP-метод, стек. Найти в стеке файл плагина, открыть, посмотреть указанную строку. Скорее всего, отсутствует проверка входных данных или неверный тип аргумента.
Аудит всего ядра. Режим 'list' со списком всех файлов из system/. Видно все Deprecated и Warning из ядра. Обрабатывать по одному: смотреть поле «Файл-виновник», исправлять, проверять, что запись в логе больше не появляется.
Ошибка воспроизводится только у конкретного пользователя. Режим 'all'. В отчёте — IP-адрес и User-Agent. Сопоставить с известными пользователями или сессиями. По URL понять, на какой странице сбой.
Ошибка возникает на каждой странице. Режим 'all'. В отчёте — стек вызовов. Искать общий шаблон: header, footer, sidebar. Проблема, вероятно, в одной из глобальных переменных или в плагине, подключаемом на всех страницах.
Периодические сбои без явной причины. Режим 'all'. Оставить отладку работать на несколько дней. По дате и времени сопоставить сбои с другими событиями на сервере: нагрузкой, действиями других админов, обновлениями кеша. Иногда причина окажется внешней — не в коде, а в данных.
12. Отключение и удаление
Временное отключение. Установить флаг включения в значение false. Обработчик перестаёт регистрироваться. Код в index.php остаётся на месте, лог не растёт.
Повторное включение. Заменить false на true. Всё снова работает.
Полное удаление. Открыть index.php. Удалить весь вставленный блок целиком — от начального комментария до закрывающего. Файл вернётся в исходный вид. Движок и остальные файлы не затронуты.
Удаление лог-файла. Файл cot_index_debug.log можно удалить в любой момент, независимо от того, работает отладка или нет. При следующем срабатывании он создастся заново.
13. Частые вопросы
Лог не создаётся. Проблема с правами. Проверить, под каким пользователем работает веб-сервер, и дать этому пользователю право на запись в директорию. Альтернатива — создать пустой cot_index_debug.log вручную и назначить владельца.
Лог растёт слишком быстро. Переключить режим с 'all' на 'list' и оставить в списке только нужные файлы. Это резко сократит объём записей.
В логе нет моей ошибки. Проверить флаг включения. Проверить, что ошибка соответствует фильтру по тексту сообщения (по умолчанию — только str_replace). Если нужно ловить более широкий класс ошибок — расширить или снять фильтр по тексту.
Ошибка есть, но в отчёте пусто в ключевых полях. Возможно, ошибка возникла вне цепочки парсинга шаблона. В этом случае смотреть URL, стек и окружение — там будет достаточно данных для диагностики.
Обработчик конфликтует с другими плагинами. Возможна ситуация, когда в проекте уже стоит свой set_error_handler. В этом случае наш обработчик заменит его. Порядок: наш колбэк возвращает false, что означает «не подавлять ошибку» — она уйдёт дальше в стандартный обработчик PHP. Функциональность других обработчиков при этом не страдает.
Как получить более широкий класс ошибок. Снять фильтр по тексту сообщения. Тогда обработчик будет реагировать на все ошибки, попадающие в error_reporting(). Это полезно для полного аудита, но лог растёт очень быстро.
Как исключить конкретный файл из мониторинга. В режиме 'list' файла просто нет в списке — он и не мониторится. В режиме 'all' исключить отдельный файл можно, добавив дополнительную проверку в начале колбэка.
Можно ли использовать обработчик на development-копии сайта. Да, он универсален. На боевом сайте тоже работает, но нужно помнить о росте лог-файла и следить за правами.
Как быстро найти самую свежую запись. Открыть файл и перейти в конец. Разделитель из символов = отделяет один блок от другого. Последний разделитель перед концом файла — начало свежего блока.
Этого достаточно для работы с отладкой в типовых ситуациях. Если понадобится расширить руководство — например, добавить раздел про конкретные виды ошибок или про интеграцию с внешними системами мониторинга — это можно сделать отдельным дополнением.
Reviews
No reviews yet
Comments (0)
Article multicategories
Additional categories where this article is shown as similar.Related Posts
Similar pages
Content author
Offline
Sodium Carbonate
Last logged: 2026-09-15 15:01
- Page published: 2026-09-15 03:51
- Last update: 2026-09-15 04:42