Overhaul the Docsify documentation (English + Russian) so it matches the current codebase and follows one consistent pattern. Content accuracy (post-migration): - Rewrite development/autoloader.md to PSR-4 / Composer (the old XC_Autoloader scanner, igbinary tmp/cache/autoload_map and registerDirectories are gone). - PascalCase every source path (src/core -> src/Core, domain/Stream, cli/Commands, public/Controllers, Infrastructure/Redis, ...) across all docs. - Replace the removed autoload.php references with vendor/autoload.php (build_system, bootstrap-contexts, error-handling, modules). - ssl-generation: note that the installer now auto-generates a unique self-signed certificate before Nginx starts. Common pattern (Clean & uniform): - Strip emoji from headings; remove the in-page Navigation blocks (the Docsify sidebar already provides navigation). - One H1 + intro per doc; uniform "Related files" / "Связанные файлы" section, added to the code-centric docs that lacked it. Structure: - Remove the empty stray docs/api/; move updates_checklist.md into builds/; link the previously-orphaned ucs-integration.md. - Regroup the sidebars (split the oversized guides group into Developer Guides / Security & Access / Integrations; fold builds into Build & Release). Augment: - dev-workflow: Local Setup (make dev-tools) + Quality Checks (phpstan, cs, gates). - build_system: Composer Dependencies section (committed prod-only vendor, committed lock, dev tools via composer install, no build-time vendor step). en/ru parity: - Apply the same structure, fixes and pattern to docs/ru/ (translated), including a new Russian ucs-integration.md. The en and ru file sets are now identical.
22 KiB
Модель обработки ошибок
Обработка ошибок в XC_VM имеет три уровня:
- Коды ошибок — что пошло не так (централизованный реестр именованных строк ошибок)
- Обработчики ошибок — как формируется HTTP-ответ клиенту (
generateError(),generate404()) - Подсистема Logger — runtime-перехват ошибок PHP, необработанных исключений и фатальных сбоев
Обзор потока
Код приложения
|
+-- generateError('CODE') // намеренный ответ с ошибкой
| -> debug-режим: стилизованная HTML-страница с кодом + описанием
| -> production: generate404() или явный HTTP-код
|
+-- PHP warning / notice / error // runtime-ошибки
| -> Logger::handleError()
| -> сопоставляет errno с уровнем (ERROR, WARNING, NOTICE, INFO)
| -> пишет base64-encoded JSON в error_log.log
| -> опционально отображает на экране
|
+-- Необработанное исключение // unhandled Throwable
| -> Logger::handleException()
| -> логирует как EXCEPTION с полной цепочкой trace
|
+-- Фатальная ошибка при shutdown // E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR
-> Logger::handleFatal()
-> логирует как FATAL (stack trace недоступен)
Реестр кодов ошибок
Все коды объявляются в src/Core/Error/ErrorCodes.php как глобальный массив $rErrorCodes.
Формат кода:
- Ключ: строка в верхнем регистре (например:
INVALID_CREDENTIALS) - Значение: человекочитаемое описание на английском
Используйте только централизованные определения кодов. Не хардкодьте тексты ошибок в обработчиках endpoint'ов.
Полный список кодов
| Код | Описание |
|---|---|
API_IP_NOT_ALLOWED |
IP не разрешён для доступа к API. |
ARCHIVE_DOESNT_EXIST |
Архивные файлы отсутствуют для этого stream ID. |
ASN_BLOCKED |
ASN заблокирован. |
BANNED |
Линия забанена. |
BLOCKED_USER_AGENT |
User-agent заблокирован. |
CACHE_INCOMPLETE |
Кэш генерируется... |
DEVICE_NOT_ALLOWED |
Устройствам MAG и Enigma запрещён доступ. |
DISABLED |
Линия отключена. |
DOWNLOAD_LIMIT_REACHED |
Достигнут лимит одновременных загрузок. |
E2_DEVICE_LOCK_FAILED |
Проверки device lock не пройдены. |
E2_DISABLED |
Устройство отключено. |
E2_NO_TOKEN |
Токен не указан. |
E2_TOKEN_DOESNT_MATCH |
Токен не совпадает с записями. |
E2_WATCHDOG_TIMEOUT |
Достигнут лимит времени. |
EMPTY_USER_AGENT |
Пустые user-agent запрещены. |
EPG_DISABLED |
EPG отключён. |
EPG_FILE_MISSING |
Кэшированные файлы EPG отсутствуют. |
EXPIRED |
Срок линии истёк. |
FORCED_COUNTRY_INVALID |
Страна не совпадает с forced country. |
GENERATE_PLAYLIST_FAILED |
Не удалось сгенерировать плейлист. |
HLS_DISABLED |
HLS отключён. |
HOSTING_DETECT |
Обнаружен hosting-сервер. |
INVALID_API_PASSWORD |
Неверный пароль API. |
INVALID_CREDENTIALS |
Неверное имя пользователя или пароль. |
INVALID_HOST |
Имя домена не распознано. |
INVALID_STREAM_ID |
Stream ID не существует. |
INVALID_TYPE_TOKEN |
Токены нельзя использовать для этого типа потока. |
IP_BLOCKED |
IP заблокирован. |
IP_MISMATCH |
Текущий IP не совпадает с IP начального подключения. |
ISP_BLOCKED |
ISP заблокирован. |
LB_TOKEN_INVALID |
AES-токен не удаётся расшифровать. |
LEGACY_EPG_DISABLED |
Доступ к legacy epg.php отключён. |
LEGACY_GET_DISABLED |
Доступ к legacy get.php отключён. |
LEGACY_PANEL_API_DISABLED |
Доступ к legacy panel_api.php отключён. |
LINE_CREATE_FAIL |
Не удалось вставить линию в базу данных. |
NO_CREDENTIALS |
Учётные данные не указаны. |
NO_SERVERS_AVAILABLE |
Сейчас нет доступных серверов для этого потока. |
NO_TIMESTAMP |
Не указана временная метка архива. |
NO_TOKEN_SPECIFIED |
Не указан AES-зашифрованный токен. |
NOT_ENIGMA_DEVICE |
Линия не является устройством enigma. |
NOT_IN_ALLOWED_COUNTRY |
Не в списке разрешённых стран. |
NOT_IN_ALLOWED_IPS |
Не в списке разрешённых IP. |
NOT_IN_ALLOWED_UAS |
Не в списке разрешённых user-agent. |
NOT_IN_BOUQUET |
У линии нет доступа к этому stream ID. |
PLAYER_API_DISABLED |
Player API отключён. |
PROXY_ACCESS_DENIED |
Невозможно обратиться к потоку напрямую, пока включён proxy. |
PROXY_DETECT |
Обнаружен proxy. |
PROXY_NO_API_ACCESS |
Нельзя обращаться к API через proxy. |
RESTREAM_DETECT |
Обнаружен рестрим. |
STALKER_CHANNEL_MISMATCH |
Stream ID не совпадает со stalker-токеном. |
STALKER_DECRYPT_FAILED |
Не удалось расшифровать stalker-токен. |
STALKER_INVALID_KEY |
Неверный stalker-ключ. |
STALKER_IP_MISMATCH |
IP не совпадает со stalker-токеном. |
STALKER_KEY_EXPIRED |
Срок stalker-токена истёк. |
STREAM_OFFLINE |
Поток сейчас offline. |
SUBTITLE_DOESNT_EXIST |
Файл субтитров не существует. |
THUMBNAIL_DOESNT_EXIST |
Файл превью не существует. |
THUMBNAILS_NOT_ENABLED |
Превью не включены для этого потока. |
TOKEN_ERROR |
AES-токен содержит неполные данные. |
TOKEN_EXPIRED |
Срок AES-токена истёк. |
TS_DISABLED |
MPEG-TS отключён. |
USER_ALREADY_CONNECTED |
Линия уже подключена с другого IP. |
USER_DISALLOW_EXT |
Расширение не в списке разрешённых. |
VOD_DOESNT_EXIST |
VOD-файл не существует. |
WAIT_TIME_EXPIRED |
Истёк таймаут запуска потока, не удалось запустить. |
Специфичные для стриминга коды (CACHE_INCOMPLETE, SUBTITLE_DOESNT_EXIST, NO_SERVERS_AVAILABLE, PROXY_ACCESS_DENIED) были перенесены из stream/init.php в централизованный реестр.
Обработчики ошибок
Определены в src/Core/Error/ErrorHandler.php. Это обычные функции (а не методы класса), подключаемые рано в bootstrap.
generateError(string $rError, bool $rKill = true, ?int $rCode = null)
Формирует HTTP-ответ с ошибкой. Поведение зависит от настройки debug_show_errors:
если debug_show_errors === true
рендерит стилизованную HTML-страницу с ключом ошибки + описанием
если $rKill -> exit()
иначе (production)
если $rKill
если задан $rCode -> http_response_code($rCode) + exit()
иначе -> generate404()
// если !$rKill, в production-режиме ничего не делает
Параметры:
| Параметр | Тип | По умолчанию | Смысл |
|---|---|---|---|
$rError |
string |
-- | Ключ из $rErrorCodes |
$rKill |
bool |
true |
Завершить скрипт после вывода |
$rCode |
int|null |
null |
Явный HTTP-код ответа (обходит 404 в production) |
Примеры:
generateError('INVALID_CREDENTIALS'); // production: 404 + exit
generateError('API_IP_NOT_ALLOWED', true, 403); // production: 403 + exit
generateError('STREAM_OFFLINE', false); // production: без вывода, без exit
generate404(bool $rKill = true)
Возвращает страницу 404 Not Found в стиле nginx и устанавливает HTTP 404. HTML включает padding-комментарии для подавления «дружелюбных» страниц ошибок в MSIE и Chrome.
generate404(); // 404 + exit
generate404(false); // 404, выполнение продолжается
Подсистема Logger
Определена в src/Core/Logging/Logger.php. Класс final, регистрирующий три глобальных PHP-обработчика для перехвата всех runtime-ошибок и записи их в файл.
Инициализация
Logger::init(bool $showErrors, string $logFile): void
Регистрирует:
set_error_handler([Logger::class, 'handleError'])— PHP warning, notice, errorset_exception_handler([Logger::class, 'handleException'])— необработанныеThrowableregister_shutdown_function([Logger::class, 'handleFatal'])— фатальные ошибки при shutdown
Также настраивает error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED) и устанавливает display_errors / display_startup_errors на основе $showErrors.
Где вызывается Logger::init()
Logger инициализируется в двух местах в зависимости от пути запроса:
| Путь входа | Файл | Как |
|---|---|---|
| Bootstrap (все контексты) | src/bootstrap.php |
XC_Bootstrap::loadConstants() вызывает Logger::init(PHP_ERRORS, LOGS_TMP_PATH . 'error_log.log') |
| Стриминговые endpoint'ы | src/Core/Http/RequestGuard.php |
Загружает настройки из файлового кэша, определяет PHP_ERRORS, затем вызывает Logger::init(PHP_ERRORS, LOGS_TMP_PATH . 'error_log.log') |
В обоих случаях PHP_ERRORS отражает настройку debug_show_errors (по умолчанию false, когда настройки недоступны).
Сопоставление уровней ошибок
Logger::handleError() сопоставляет константы PHP-ошибок строкам уровней через mapErrorLevel():
| Константа(ы) PHP | Уровень лога |
|---|---|
E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR |
ERROR |
E_WARNING, E_USER_WARNING |
WARNING |
E_NOTICE, E_USER_NOTICE |
NOTICE |
Все другие значения errno |
INFO |
Обработчик shutdown (handleFatal()) проверяет error_get_last() на эти фатальные типы и логирует их как FATAL:
| Константа(ы) PHP при shutdown | Уровень лога |
|---|---|
E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR |
FATAL |
Необработанные исключения, логируемые handleException(), всегда используют уровень EXCEPTION.
Ошибки, подавленные оператором @, игнорируются (обработчик проверяет error_reporting() & $errno).
Формат лога
Каждая запись лога пишется одной строкой: base64_encode(json_encode($data)) с переводом строки. Это предотвращает повреждение строк из многострочных сообщений.
Декодированная JSON-структура:
{
"type": "WARNING",
"log_message": "Undefined variable $foo",
"file": "/home/xc_vm/Domain/Stream/StreamService.php",
"line": 142,
"log_extra": "#0 /home/xc_vm/...(line): function()\n#1 ...",
"time": 1716220800,
"env": "fpm-fcgi"
}
| Поле | Содержимое |
|---|---|
type |
Уровень лога: ERROR, WARNING, NOTICE, INFO, EXCEPTION или FATAL |
log_message |
Текст сообщения об ошибке/исключении |
file |
Абсолютный путь к исходному файлу |
line |
Номер строки, где произошла ошибка |
log_extra |
Stack trace (форматированная строка). Пустой для фатальных ошибок. |
time |
Unix timestamp |
env |
Имя PHP SAPI (cli, fpm-fcgi и т.д.) |
Расположение файла лога
Путь по умолчанию: LOGS_TMP_PATH . 'error_log.log'
Если директория лога не существует, Logger создаёт её с правами 0775. При запуске под root (часто в контейнерах) файл chown'ится в xc_vm:xc_vm с режимом 0664.
Вывод на экран
Когда $showErrors = true, Logger также рендерит ошибки напрямую:
- CLI: цветной вывод в терминал (красный для FATAL/ERROR, жёлтый для WARNING, синий для NOTICE)
- Web: inline
<div>с моноширинным шрифтом, красной рамкой и stack trace в блоке<pre>
Конвейер логирования: файл → база данных
Logger пишет в error_log.log на диск. Отдельная подсистема читает этот файл и сохраняет записи в таблицу panel_logs:
- Logger пишет base64-encoded JSON-строки в
error_log.log - FileLogger (
src/Core/Logging/FileLogger.php) предоставляет вторичный логирующий интерфейс для кода приложения (ошибки PDO, ошибки EPG и т.д.), который пишет в тот же файл в том же формате - Записи импортируются в таблицу
panel_logs - DiagnosticsService (
src/Core/Diagnostics/DiagnosticsService.php) читает изpanel_logsдля:downloadPanelLogs()— получает до 1000 свежих не-EPG ошибок, затем truncate'ит таблицуsubmitPanelLogs()— отправляет логи на центральный API-сервер для анализа
- Админ-панель показывает эти логи в Management > Logs > Panel Errors
Шумовая фильтрация FileLogger
FileLogger::log() пропускает записи, соответствующие:
- Сообщения, содержащие
panel_logsв дополнительном поле (предотвращает рекурсивное логирование) - Сообщения, совпадающие с
timeout exceeded,lock wait timeoutилиduplicate entry(шумные MySQL-ошибки)
Другие логгеры
Директория src/Core/Logging/ содержит дополнительные специализированные логгеры:
| Класс | Файл | Назначение |
|---|---|---|
Logger |
Logger.php |
Глобальный обработчик ошибок/исключений/fatal PHP (описан выше) |
FileLogger |
FileLogger.php |
Логирование уровня приложения (ошибки PDO, EPG и т.д.) в error_log.log |
DatabaseLogger |
DatabaseLogger.php |
События клиентских стриминговых запросов в client_request.log (импортируются в таблицу client_logs) |
UpdateLogger |
UpdateLogger.php |
Операции обновления системы в MAIN_HOME/update.log (простой текст, не base64) |
Все логгеры, кроме UpdateLogger, реализуют LoggerInterface и пишут base64-encoded JSON.
Типы исключений в кодовой базе
Кодовая база определяет небольшое число пользовательских классов исключений. Все необработанные исключения перехватываются Logger::handleException(), который логирует полную цепочку исключений (включая getPrevious()).
| Класс исключения | Базовый класс | Расположение |
|---|---|---|
DropboxException |
Exception |
src/Core/Storage/DropboxClient.php |
M3uParser\Exception |
\Exception |
src/Core/Parsing/M3uParser/src/Exception.php |
DataBuildingException |
\RuntimeException |
src/Core/Parsing/PhpM3u8/src/Parser/DataBuildingException.php |
DefinitionException |
\RuntimeException |
src/Core/Parsing/PhpM3u8/src/Definition/DefinitionException.php |
DumpingException |
\RuntimeException |
src/Core/Parsing/PhpM3u8/src/Dumper/DumpingException.php |
Большая часть кода приложения использует выбросы обобщённого Exception или полагается на встроенную систему ошибок PHP. Exception-обработчик Logger принимает любой Throwable.
Debug vs Production
Production (по умолчанию: debug_show_errors = false)
generateError()возвращает универсальную страницу 404 (или явный HTTP-код), скрывая внутреннюю причину сбоя- Logger по-прежнему пишет все ошибки в
error_log.logна диск display_errorsиdisplay_startup_errorsустанавливаются в'0'- Ошибки видны только через админ-панель (страница Panel Errors) или файлы логов
Debug (debug_show_errors = true)
generateError()показывает стилизованную страницу с ключом ошибки и сопоставленным описанием- Logger дополнительно рендерит ошибки на экран (цветной CLI-вывод или inline HTML)
display_errorsиdisplay_startup_errorsустанавливаются в'1'
Не включайте debug-отображение на production-нодах.
Регистрация обработчика ошибок в bootstrap
Инфраструктура обработки ошибок загружается рано в последовательности загрузки:
bootstrap.phpопределяетMAIN_HOMEи регистрирует Composer-автозагрузчикXC_Bootstrap::loadConstants()загружает (в порядке):Core/Error/ErrorCodes.php— заполняет$rErrorCodesCore/Error/ErrorHandler.php— определяетgenerateError()иgenerate404()- Файлы путей и конфигурации
Core/Logging/Logger.php— определение класса
- Вызывается
Logger::init(PHP_ERRORS, LOGS_TMP_PATH . 'error_log.log'), регистрирующий три глобальных обработчика - С этого момента все PHP-ошибки, необработанные исключения и фатальные сбои перехватываются
Для стриминговых endpoint'ов, обходящих полный bootstrap, RequestGuard.php выполняет шаги 2-3 независимо: загружает настройки из файлового кэша, определяет PHP_ERRORS и вызывает Logger::init().
Добавление нового кода ошибки
- Добавьте новый ключ в
src/Core/Error/ErrorCodes.php:
'MY_NEW_ERROR' => 'Human-readable description.',
- Используйте его в коде:
generateError('MY_NEW_ERROR');
Описания должны оставаться на английском для согласованности с существующим реестром.
Связанные файлы
| Файл | Назначение |
|---|---|
src/Core/Error/ErrorCodes.php |
Централизованная карта кодов ошибок ($rErrorCodes) |
src/Core/Error/ErrorHandler.php |
Функции generateError() и generate404() |
src/Core/Logging/Logger.php |
Глобальные обработчики PHP-ошибок, исключений и фатальных |
src/Core/Logging/LoggerInterface.php |
Интерфейс контракта логирования |
src/Core/Logging/FileLogger.php |
Файловое логирование уровня приложения (PDO, EPG и т.д.) |
src/Core/Logging/DatabaseLogger.php |
Логирование событий клиентских стриминговых запросов |
src/Core/Logging/UpdateLogger.php |
Логирование операций обновления системы |
src/Core/Http/RequestGuard.php |
Стриминговый путь: защита от флуда, проверка хоста, инициализация Logger |
src/Core/Diagnostics/DiagnosticsService.php |
Читает таблицу panel_logs для отображения в админке и отправки в API |
src/bootstrap.php |
Подключает слой ошибок и Logger во всех контекстах bootstrap |