- Remove TmdbCronJob/TmdbPopularCronJob from LB_FILES_TO_REMOVE - Add comment: modules/ excluded from LB_DIRS (all modules MAIN-only) - MIGRATION.md: update TMDB cron references to modules/tmdb/
42 KiB
XC_VM — План миграции
Этот документ описывает порядок миграции, детали каждой фазы и стратегию управления рисками. Архитектурные принципы, структура проекта и описание компонентов — см. ARCHITECTURE.md.
Содержание
- Принцип миграции
- Фазы 0–9: Завершённые
- Фаза 10: Удаление legacy admin entry-points ✅
- Фаза 11: Унификация API ✅ (11.1–11.4)
- Фаза 12: CLI — единый runner ✅
- Фаза 13: Streaming entry points ✅ (13.1–13.3)
- Фаза 14: CSS/JS partials
- Фаза 15: Удаление includes/admin.php
- Порядок выполнения и риск-матрица
- Стратегия миграции по рискам
1. Принцип миграции
Извлечение → делегирование → замена
Каждый шаг миграции следует одному паттерну:
1. Создать новый класс в целевой директории
2. Перенести в него методы из god-объекта
3. В старом файле оставить proxy-метод:
public static function oldMethod(...$args) {
return NewClass::method(...$args);
}
4. Зарегистрировать класс в autoloader
5. Проверить: система работает как раньше
6. (позже) Обновить вызывающий код → удалить proxy
Так каждый шаг безопасен и обратимо совместим.
2. Фазы 0–9: Завершённые
Фаза 0: Подготовка ✅
Autoload, скелет директорий, bootstrap.php, ServiceContainer, разбиение constants.php → 7 core-файлов.
Фаза 1: Извлечение core/ ✅
Database, Cache, Http/Request, Auth, Process, Util — все базовые компоненты извлечены из god-объектов.
Фаза 1.7: Оставшиеся извлечения core/ ✅
Логирование, SystemInfo, BruteforceGuard, CurlClient, EventDispatcher, Authorization, Authenticator, ImageUtils — 8 шагов завершены.
Фаза 2: Дедупликация CoreUtilities ↔ StreamingUtilities ✅
53 дублированных метода дедуплицированы: Redis/сигналы, трекинг подключений, справочные данные, init().
Фаза 3: Извлечение domain/ — бизнес-логика ✅
12 доменных контекстов: Stream, Vod, Line, User, Device, Server, Bouquet, Epg, Settings/Ticket, Security, Auth, Playlist. Все entity/repository/service извлечены.
Фаза 4: Извлечение streaming/ (hot path) ✅
streaming/Auth, Delivery, Codec, Protection/Health, StreamingBootstrap — лёгкий bootstrap для hot path.
Фаза 5: Вынесение модулей ✅
6 модулей извлечены атомарно: plex, watch, tmdb, ministra, fingerprint/theft-detection/magscan. ModuleInterface + ModuleLoader. Thread/Multithread дедуплицированы в core/Process/.
- 🔲
ministra/*.js→ modules/ministra/assets/ (JS-файлы портала — отложено до Фазы 13.4)
Фаза 6: Контроллеры и Views (admin/reseller) ✅
Шаг 6.1 — Единый layout ✅
Unified wrappers: public/Views/layouts/admin.php + footer.php.
- Admin: 112/112 page-файлов — 100% мигрированы
- Reseller: 22/22 page-файлов — 100% мигрированы
- ⏭️ CSS/JS partials — отложено до Фазы 14 (footer.php: ~800 стр. page-specific inline JS)
Шаг 6.2 — Router + Front Controller ✅
core/Http/Router.php (450 стр.), public/index.php (Front Controller), Request.php, Response.php, RequestGuard.php. Трёхрежимный URL-парсинг (Access Code + XC_SCOPE / Direct URL / fallback). Access Codes поддержаны.
Шаг 6.3 — Конвертация admin-страниц (Controller/View) ✅
111/111 admin-страниц мигрированы: Controller + View + Scripts + routes. Паттерн: Thin Controller → Service → View. BaseAdminController + _scripts_init.php.
Шаг 6.4 — Объединение admin/reseller ✅
22 reseller-страницы мигрированы. BaseResellerController + 22 контроллера/view/маршрута.
Шаг 6.5 — Стабилизация Controller/View контракта ✅
Два прохода стабилизации: viewGlobals расширен, nullable-guards для foreach/in_array/count.
Шаг 6.5b — Reseller view nullable audit ✅
Source-level fixes: getPermissions() → [] fallback, defensive defaults в functions.php/table.php для direct_reports, all_reports, stream_ids, category_ids, series_ids, subresellers. P0/P1 точечные исправления в 10 файлах.
Фаза 7: Миграция admin.php bootstrap
Шаг 7.1 — Вынос inline-данных ✅
Данные bootstrap консолидированы в resources/data/admin_constants.php.
Шаг 7.2 — Замена процедурного bootstrap ✅
8 инкрементов: runtime → bootstrapAdminRuntime() → admin_session.php + admin_runtime.php → XC_Bootstrap::boot(CONTEXT_ADMIN) → фасад admin_bootstrap.php.
Шаг 7.3 — Удаление proxy-обёрток из admin.php ✅
40 proxy-определений удалены, 560+ call-sites заменены на прямые вызовы domain-сервисов. admin.php сокращён с ~4448 до ~3050 строк.
Шаг 7.3.1 — Миграция getCategories/getOutputs ✅
getCategories() → CategoryService::getAllByType() (~75+ call-sites). getOutputs() → OutputFormatRepository::getAll() (3 call-sites). Создан domain/Line/OutputFormatRepository.php.
Шаг 7.4 — Устранение параметра $db из Domain-классов ✅
28 классов (100 методов) → global $db внутри. ~357 call-sites обновлены.
Фаза 8: Ликвидация god-объектов ✅
Цель: Удалить три файла-монолита (CoreUtilities.php, StreamingUtilities.php, admin_api.php), заменив ~7 400 внешних вызовов на прямые обращения к целевым классам.
| Файл | Было строк | Методов | Внешних вызовов | Статус |
|---|---|---|---|---|
admin_api.php |
3 686 | 79 | ~300 | ✅ 8.1 — удалён |
StreamingUtilities.php |
659 | 78 | 1 344 | ✅ 8.2 — удалён |
CoreUtilities.php |
1 971 | 152 | 5 755 | ✅ 8.3–8.11 — удалён |
Шаг 8.1 — admin_api.php ✅
60 PROXY + 18 OWN методов мигрированы в domain-сервисы. Файл удалён.
Шаг 8.2 — StreamingUtilities.php ✅
42 PROXY + 33 OWN методов, 18 свойств. ~314 ссылок заменены в 10 батчах. Файл удалён.
Шаг 8.3 — CoreUtilities методы ✅
81 PROXY + 69 OWN методов извлечены в целевые классы (17 батчей). CU сокращён с 1 971 до 40 строк.
Шаги 8.4–8.6 — Свойства (blocklist, FFmpeg, light) ✅
- 8.4 7 blocklist/allowlist свойств →
BlocklistServicelazy getters (FileCache) - 8.5 3 FFmpeg свойства → value-object
FfmpegPaths::resolve() - 8.6 3 свойства ($rCategories, $rBouquets, $rSegmentSettings) → сервисные геттеры
Шаги 8.7–8.10 — Инфраструктурные свойства → singletons ✅
- 8.7
$rCached→FileCache,$rConfig→ConfigReader,$rServers(184 refs) →ServerRepository::getAll() - 8.8
$db(30 refs) →DatabaseFactory::set()/get(),$redis(62 refs) →RedisManager::instance() - 8.9
$rSettings(819 refs, 221 файл) →SettingsManager::set()/getAll()/get()/update() - 8.10
$rRequest(3863 refs, 166 файлов) →RequestManager::set()/getAll()/get()/update()
Шаг 8.11 — Удаление CoreUtilities.php ✅
6 call-sites CoreUtilities::init() → LegacyInitializer::initCore(). Файл физически удалён.
Эволюция CU:
| Фаза | Строк | Методов | Свойств |
|---|---|---|---|
| До рефакторинга | 1 971 | 152 | 21 |
| После 8.3 | 40 | 3 | 21 |
| После 8.4–8.6 | 19 | 2 | 8 |
| После 8.7–8.8 | 9 | 1 | 2 |
| После 8.9–8.10 | 5 | 1 | 0 |
| 8.11 — УДАЛЁН | 0 | 0 | 0 |
Новые singleton/service классы (Phase 8): SettingsManager, RequestManager, ConfigReader, DatabaseFactory (singleton), RedisManager (singleton), FfmpegPaths, FileCache, DataEncryptor, InputSanitizer, IpUtils, UrlBuilder, ImageUtils, Helpers, ProcessManager, ConnectionManager, BackupService, ProviderService, ProfileService, RadioService, SystemCheck, InputValidator.
Фаза 9: Стабилизация сборки ✅
Цель фазы: после массовых рефакторингов (Phases 7–8) довести проект до состояния «зелёной» сборки: корректный Makefile, стандартизированные PHP-заголовки, унифицированный layout, экспорт глобалов.
Шаг 9.1 — Makefile: LB-сборка для новой архитектуры ✅
Проблема: LB_FILES не включал core/, domain/, streaming/, infrastructure/, resources/, autoload.php, bootstrap.php — LB-сборка не могла работать с мигрированным кодом.
Решение (5 правок в Makefile):
LB_FILES→LB_DIRS(14 dirs) +LB_ROOT_FILES(5 root files)lb_copy_files: второй цикл для root-файлов черезgit ls-files --error-unmatchlb_update_copy_files: каскадная проверка (dirs → root files) для delta-обновленийLB_DIRS_TO_REMOVE: +6 admin-only исключений (includes/bootstrap,domain/User,domain/Device,domain/Auth,resources/langs,resources/libs)set_permissions:core/ domain/ streaming/ infrastructure/ resources/→ dirs:755, files:644; root PHP → 644
Шаг 9.2 — Стандартизация PHP-заголовков (Clean Headers) ✅
Проблема: Admin- и reseller-view-файлы начинались разнородно — невозможно безопасно включать как view-фрагменты из layout-контроллера.
Решение: Guard-условие $__viewMode (скрипт tools/clean_headers.py, 4 итерации):
<?php if (!isset($__viewMode)): ?>
<?php
include 'session.php';
include 'functions.php';
renderUnifiedLayoutHeader('admin');
?>
<?php endif; // !$__viewMode ?>
Масштаб: 112 admin-файлов + 22 reseller-файла.
Шаг 9.3 — Миграция view-layout: renderUnifiedLayoutHeader ✅
Решение: renderUnifiedLayoutHeader($scope, $vars) / renderUnifiedLayoutFooter($scope, $vars) — единая обёртка для подключения header/footer с извлечением 16 глобалов.
Новые файлы:
public/Views/layouts/admin.phppublic/Views/layouts/footer.php
Масштаб: 112 admin + 22 reseller файла переведены.
Шаг 9.4 — Глобальные переменные: экспорт singleton-данных ✅
LegacyInitializer::exportGlobals() — экспортирует singleton-данные в $GLOBALS один раз при bootstrap.
Экспортируемые переменные: $rSettings, $rRequest, $rConfig, $rServers, $rFFPROBE, $rFFMPEG_CPU, $rFFMPEG_GPU, $db + streaming-контекст: $rCached, $rBlockedUA/ISP/IPs/Servers, $rAllowedIPs, $rProxies, $rBouquets, $rSegmentSettings.
Масштаб: 54 замены в 20 файлах.
3. Фаза 10: Удаление legacy admin entry-points ✅
Цель: Все admin-запросы обслуживаются ТОЛЬКО через
public/index.php(Front Controller → Router → Controller → View). Директорияsrc/admin/удалена.
Шаг 10.1 — Устранение fallback в Front Controller ✅
Что сделано:
- Созданы контроллеры для 7 ещё не маршрутизированных страниц:
LogoutController— destroySession + redirectPlayerEmbedController— проксирует admin/player.phpPostController— устанавливает$GLOBALS['__forcePostMode'], проксирует admin/post.phpLoginController— проксирует admin/login.php (собственный HTML-документ)SetupController— методыindex()(setup.php) иdatabase()(database.php)
- Добавлены 7 маршрутов в
public/routes/admin.php(logout, player, post, login, setup, database, index) - Унифицирована секция 4a FC: все scope (admin/reseller/player) используют Router для noBootstrapPages
- Секция 7 (legacy fallback) обёрнута feature flag
$rSettings['use_legacy_fallback'](default: true) - Модифицирован
admin/post.php:$rICountтеперь учитывает$GLOBALS['__forcePostMode']
Шаг 10.2 — Миграция admin/login.php → LoginController ✅
LoginController создан как thin proxy: chdir(admin/) + require login.php. Маршруты login и index зарегистрированы.
Шаг 10.3 — Миграция admin/setup.php, database.php → SetupController ✅
SetupController: методы index() и database() проксируют в legacy-файлы. Маршруты зарегистрированы.
Шаг 10.4 — Миграция admin/api.php → AjaxController ✅
AjaxController — thin proxy: chdir(admin/) + require api.php. Маршрут $router->any('api', ...). FC API-fallback обёрнут feature flag.
Шаг 10.5 — Консолидация session.php + functions.php ✅
Создано:
infrastructure/bootstrap/admin_session_fc.php— clean-версия admin/session.php для FC-путиinfrastructure/bootstrap/admin_functions_fc.php— clean-версия admin/functions.php
FC переключён на infrastructure/bootstrap/admin_*_fc.php. Оригиналы оставлены для direct nginx access.
Шаг 10.6 — Удаление директории src/admin/ ✅
Что сделано:
- 127 PHP-файлов перемещены из
admin/→public/Views/admin/ - 423 статических файла (CSS, fonts, images, JS, libs) перемещены из
admin/assets/→public/assets/admin/ - Директория
src/admin/полностью удалена - Обновлены все ссылки:
- FC (
public/index.php):$adminDir = MAIN_HOME . 'public/Views/admin/' - 5 контроллеров: LoginController, SetupController, PostController, PlayerEmbedController, AjaxController — пути к
public/Views/admin/ - 2 layout-файла:
dirname(__DIR__) . '/admin/header.php'/footer.php - BaseAdminController:
$__viewMode = trueперед view require - 112 view-файлов:
__DIR__ . '/../layouts/'(исправлены относительные пути) - 8 view-файлов:
dirname(__DIR__, 3) . '/modules/'(исправлена глубина) - AuthRepository.php:
$rAliasadmin →'public/Views/admin' - Makefile: permissions
$(TEMP_DIR)/public/assets/admin
- FC (
- nginx template (
bin/nginx/conf/codes/template) уже корректный:alias /home/xc_vm/public/assets/#TYPE#/ - Syntax check: 126/127 pass (review.php — pre-existing bug, не регрессия)
4. Фаза 11: Унификация API — внешние и внутренние ✅ (11.1–11.4)
Цель: Единый API-слой через
public/Controllers/Api/с Router-маршрутизацией. Удаление самостоятельных PHP-файлов вsrc/www/(player_api.php,enigma2.php,epg.php,playlist.php,xplugin.php).
Шаг 11.1 — Player API → PlayerApiController ✅
Результат: public/Controllers/Api/PlayerApiController.php (843 строки, 10 actions). Bootstrap через stream/init.php (hot path). Auth через UserRepository::getStreamingUserInfo(). Standalone — НЕ наследует BaseApiController.
www/player_api.php → thin proxy (7 строк).
Шаг 11.2 — Enigma2 API → Enigma2ApiController + XPluginApiController ✅
Результат: Разделены (вместо объединения по плану) — разные auth, output format, domain:
Enigma2ApiController.php(507 строк, 10 XML-actions)XPluginApiController.php(181 строка, device management)SimpleXMLExtendedизвлечён в отдельный класс (был inline в switch/default — баг)
www/enigma2.php и www/xplugin.php → thin proxies (7 строк каждый).
Шаг 11.3 — Playlist/EPG → PlaylistApiController, EpgApiController ✅
Результат:
PlaylistApiController.php(62 строки) — M3U/M3U8 генерацияEpgApiController.php(105 строк) — XMLTV XML генерацияBaseApiController.php(109 строк) — общий базовый класс
www/playlist.php и www/epg.php → thin proxies.
Шаг 11.4 — Internal Server API → InternalApiController ✅
Результат: InternalApiController.php (506 строк, 30+ actions). Auth: password + IP whitelist (server-to-server). Извлечены: serveFile(), probeStream(), killProcessGroup().
www/api.php → thin proxy.
Статистика: 7 controller-файлов = 2313 строк. 6 proxy-файлов = 42 строки.
Шаг 11.5 — Admin/Reseller REST API → единые маршруты ⏳
Статус: ОТЛОЖЕНО. APIWrapper классы встроены в index.php (2065+556 строк, одинаковое имя класса). Требуется: переименование классов, извлечение, разрешение конфликта namespace. Отдельный PR.
Шаг 11.6 — Удаление legacy API файлов ⏳
Статус: ОТЛОЖЕНО. Требуется FC API routing (scope=api). www/*.php thin proxies должны оставаться до маршрутизации API через Front Controller.
Предусловия: 11.5 завершён, nginx rewrites обновлены.
5. Фаза 12: CLI — единый runner и структурированные команды ✅
Цель: Все CLI-скрипты (
includes/cli/,crons/,src/service,src/tools,src/status) запускаются через единую точку входаconsole.php. Каждый скрипт — класс-команда с интерфейсомCommandInterface.
Шаг 12.1 — Console entry point + CommandInterface ✅
Создано:
cli/
├── CommandInterface.php # interface: getName(), getDescription(), execute(array $args): int
├── CommandRegistry.php # Реестр команд (name → class)
├── DaemonTrait.php # Общий boilerplate для демонов
├── Commands/ # 26 команд
└── CronJobs/ # 25 cron-задач
console.php # Единая точка входа (root-level)
Запуск:
# Было:
php /home/xc_vm/includes/cli/startup.php
php /home/xc_vm/crons/servers.php
# Стало:
php /home/xc_vm/console.php startup
php /home/xc_vm/console.php cron:servers
Шаг 12.2 — Миграция includes/cli/ → cli/Commands/ ✅
Результат: 24 файла из includes/cli/ → 26 Command-классов в cli/Commands/. Вся логика перенесена, proxy-файлы были созданы, затем удалены (шаг 12.6).
| Legacy файл | → Command | Тип |
|---|---|---|
startup.php |
Commands/StartupCommand |
daemon |
watchdog.php |
Commands/WatchdogCommand |
daemon |
signals.php |
Commands/SignalsCommand |
daemon |
queue.php |
Commands/QueueCommand |
daemon |
cache_handler.php |
Commands/CacheHandlerCommand |
daemon |
connection_sync.php |
Commands/ConnectionSyncCommand |
daemon |
monitor.php |
Commands/MonitorCommand |
CLI |
scanner.php |
Commands/ScannerCommand |
CLI |
balancer.php |
Commands/BalancerCommand |
CLI |
migrate.php |
Commands/MigrateCommand |
CLI |
tools.php |
Commands/ToolsCommand |
CLI |
update.php |
Commands/UpdateCommand |
CLI |
binaries.php |
Commands/BinariesCommand |
CLI |
archive.php |
Commands/ArchiveCommand |
CLI |
created.php |
Commands/CreatedCommand |
CLI |
delay.php |
Commands/DelayCommand |
CLI |
loopback.php |
Commands/LoopbackCommand |
CLI |
llod.php |
Commands/LlodCommand |
CLI |
ondemand.php |
Commands/OndemandCommand |
CLI |
plex_item.php |
Commands/PlexItemCommand |
CLI |
proxy.php |
Commands/ProxyCommand |
CLI |
record.php |
Commands/RecordCommand |
CLI |
thumbnail.php |
Commands/ThumbnailCommand |
CLI |
watch_item.php |
Commands/WatchItemCommand |
CLI |
| — | Commands/CertbotCommand |
CLI |
| — | Commands/ServiceCommand |
CLI |
Шаг 12.3 — Миграция crons/ → cli/CronJobs/ ✅
Результат: 25 cron-файлов → 25 CronJob-классов в cli/CronJobs/. Proxy-файлы и директория crons/ удалены (шаг 12.8).
| Legacy файл | → CronJob | Заметки |
|---|---|---|
activity.php |
CronJobs/ActivityCronJob |
|
backups.php |
CronJobs/BackupsCronJob |
|
cache.php |
CronJobs/CacheCronJob |
|
cache_engine.php |
CronJobs/CacheEngineCronJob |
|
certbot.php |
CronJobs/CertbotCronJob |
|
cleanup.php |
CronJobs/CleanupCronJob |
|
epg.php |
CronJobs/EpgCronJob |
Крупный: EPG-класс + парсинг XML |
errors.php |
CronJobs/ErrorsCronJob |
|
lines_logs.php |
CronJobs/LinesLogsCronJob |
|
plex.php |
CronJobs/PlexCronJob |
Модуль plex |
providers.php |
CronJobs/ProvidersCronJob |
|
root_mysql.php |
CronJobs/RootMysqlCronJob |
Требует root, LB-excluded |
root_signals.php |
CronJobs/RootSignalsCronJob |
Требует root |
series.php |
CronJobs/SeriesCronJob |
|
servers.php |
CronJobs/ServersCronJob |
|
stats.php |
CronJobs/StatsCronJob |
|
streams.php |
CronJobs/StreamsCronJob |
|
streams_logs.php |
CronJobs/StreamsLogsCronJob |
|
tmdb.php |
CronJobs/TmdbCronJob |
Модуль tmdb |
tmdb_popular.php |
CronJobs/TmdbPopularCronJob |
Модуль tmdb |
tmp.php |
CronJobs/TmpCronJob |
|
update.php |
CronJobs/UpdateCronJob |
|
users.php |
CronJobs/UsersCronJob |
|
vod.php |
CronJobs/VodCronJob |
|
watch.php |
CronJobs/WatchCronJob |
Модуль watch |
Шаг 12.4 — Миграция root-level скриптов ✅
| Legacy | → | Статус |
|---|---|---|
src/service (shell) |
console.php service:{start|stop|restart|reload} |
✅ ServiceCommand |
src/status (PHP) |
console.php status |
✅ StatusCommand |
src/tools (PHP) |
console.php tools:{rescue|access|ports} |
✅ ToolsCommand |
src/update (Python) |
Остаётся Python-скриптом | Без изменений |
Шаг 12.5 — Обновление daemons.sh и crontab ✅
- ✅
bin/daemons.sh→ запуск черезconsole.php {command} - ✅ Crontab генерируется
StartupCommand::installCrontab()/installRootCrontab()→console.php cron:{name} - ✅
src/service→console.php service:{start|stop|restart|reload}
Шаг 12.6 — Удаление legacy CLI файлов ✅
Выполнено:
- ✅
CLI_PATHconstant удалён изcore/Config/Paths.php - ✅ 37 ссылок на
CLI_PATHзаменены наMAIN_HOME . 'console.php {command}'в 14 файлах - ✅
includes/cli/— папка удалена (24 файла) - ✅ Makefile: 3 старые записи
includes/cli/удалены из EXCLUDES - ✅
php -l— все 14 файлов проверены, синтаксис ОК - ✅
crons/— папка удалена (25 proxy-файлов), см. шаг 12.8
Паттерн замены:
# Было:
PHP_BIN . ' ' . CLI_PATH . 'script.php args'
# Стало:
PHP_BIN . ' ' . MAIN_HOME . 'console.php command args'
Особые случаи:
OndemandCommand:md5_file(CLI_PATH . 'ondemand.php')→md5_file(__FILE__)(self-change detection)BalancerCommand: обе ветки$rType == 2→console.php startup(was using different legacy paths)
Шаг 12.8 — Удаление CRON_PATH и crons/ директории ✅
Проблема: 25 proxy-файлов в crons/ и константа CRON_PATH — legacy остатки после миграции cron → console.php cron:{name}. 34+ ссылки на CRON_PATH в кодовой базе.
Выполнено:
- ✅ 35 ссылок на
CRON_PATHзаменены наMAIN_HOME . 'console.php cron:{name}'в 9 файлах - ✅ Константа
CRON_PATHудалена изcore/Config/Paths.php - ✅ Мёртвый autoload-маппинг
EPG → crons/epg.phpудалён - ✅ Класс
EPGвосстановлен вdomain/Epg/EPG.php(был потерян при proxy-конвертации) - ✅
src/crons/— папка удалена (25 proxy-файлов) - ✅ Makefile:
cronsубран изLB_DIRS, 9 записейcrons/*.phpизLB_FILES_TO_REMOVE, 2 строки permissions - ✅ 8 CronJob-файлов (MAIN-only) добавлены в
LB_FILES_TO_REMOVE+file_exists()guards вconsole.php - ✅
MigrationRunner::runFileCleanup()— теперь удаляет опустевшие директории - ✅ Legacy-путь в
post.php(MAIN_HOME . '/php/bin/php ' . MAIN_HOME . '/crons/epg.php') исправлен
Паттерн замены:
# Было:
PHP_BIN . ' ' . CRON_PATH . 'name.php args'
# Стало:
PHP_BIN . ' ' . MAIN_HOME . 'console.php cron:name args'
Затронутые файлы:
cli/CronJobs/CacheEngineCronJob.php(9 замен)cli/Commands/StartupCommand.php(4 замены, вкл.file_exists()→ проверкаcache_complete)cli/Commands/SignalsCommand.php(2 замены)cli/Commands/BalancerCommand.php(1 замена)cli/Commands/CertbotCommand.php(1 замена)public/Controllers/Api/InternalApiController.php(4 замены)public/Controllers/Api/AdminApiController.php(5 замен)public/Views/admin/api.php(8 замен)public/Views/admin/post.php(1 замена)
Шаг 12.7 — LB guard для MAIN-only команд ✅
Проблема: 4 CLI-класса исключены из LB-сборки через LB_FILES_TO_REMOVE, но console.php регистрировал их безусловно → crash require_once на LB.
Решение: file_exists() guards в console.php для условной регистрации.
| Файл | Guard в console.php | Guard в crontab |
|---|---|---|
cli/Commands/MigrateCommand.php |
✅ file_exists() |
— (не в crontab) |
cli/Commands/CacheHandlerCommand.php |
✅ file_exists() |
— (не в crontab) |
cli/Commands/BalancerCommand.php |
✅ file_exists() |
— (не в crontab) |
cli/CronJobs/RootMysqlCronJob.php |
✅ file_exists() |
✅ file_exists() в StartupCommand + StatusCommand |
cli/CronJobs/BackupsCronJob.php |
✅ file_exists() |
— |
cli/CronJobs/CacheEngineCronJob.php |
✅ file_exists() |
— |
cli/CronJobs/EpgCronJob.php |
✅ file_exists() |
— |
cli/CronJobs/UpdateCronJob.php |
✅ file_exists() |
— |
cli/CronJobs/ProvidersCronJob.php |
✅ file_exists() |
— |
cli/CronJobs/SeriesCronJob.php |
✅ file_exists() |
— |
modules/tmdb/TmdbCronJob.php |
✅ auto-discovery | Moved to module dir |
modules/tmdb/TmdbPopularCronJob.php |
✅ auto-discovery | Moved to module dir |
domain/Epg/EPG.php |
— | — (autoload) |
Makefile LB_FILES_TO_REMOVE:
cli/Commands/MigrateCommand.php
cli/Commands/CacheHandlerCommand.php
cli/Commands/BalancerCommand.php
cli/CronJobs/RootMysqlCronJob.php
cli/CronJobs/BackupsCronJob.php
cli/CronJobs/CacheEngineCronJob.php
cli/CronJobs/EpgCronJob.php
cli/CronJobs/UpdateCronJob.php
cli/CronJobs/ProvidersCronJob.php
cli/CronJobs/SeriesCronJob.php
domain/Epg/EPG.php
6. Фаза 13: Streaming entry points ✅ (13.1–13.3)
Цель: Минимизировать дублирование bootstrap в
www/stream/*.php, НЕ увеличивая latency. Hot path остаётся < 50ms p99.
ВАЖНО: Streaming — священная территория. Никакого Router, никакого ServiceContainer, никакого полного autoload в hot path. Только точечные улучшения.
Шаг 13.1 — Единый streaming entry point (micro-router) ✅
Результат: www/stream/index.php — micro-router с ?handler= параметром. Dormant-режим: nginx по-прежнему направляет на конкретные файлы, router активируется опционально.
Overhead < 0.1ms (один array lookup).
Шаг 13.2 — Дедупликация shutdown handlers ✅
Результат: streaming/Lifecycle/ShutdownHandler.php — заменяет дублированные function shutdown() в live/vod/timeshift. Context-параметр для live-специфичной очистки (tmp files, on_demand queue).
auth.php и rtmp.php сохраняют свои shutdown handlers (BruteforceGuard pattern).
Шаг 13.3 — Извлечение streaming auth middleware ✅
Результат: streaming/Auth/StreamAuthMiddleware.php — sendStreamHeaders() + decryptToken(). Извлекает ~35 дублированных строк из каждого streaming-файла.
Изменённые файлы:
www/stream/live.php— ShutdownHandler + StreamAuthMiddlewarewww/stream/vod.php— аналогичноwww/stream/timeshift.php— аналогично
Шаг 13.4 — Ministra JS → modules/ministra/assets/ ⏳
Статус: ОТЛОЖЕНО. www/c — симлинк на ministra/ (создаётся RootSignalsCronJob::enable_ministra). Перемещение JS ломает симлинк, требует nginx alias + изменения signal handler. Низкий benefit, высокий cost.
7. Фаза 14: CSS/JS partials — разбиение footer.php
Цель: Вынести ~800 строк page-specific inline JS из
admin/footer.phpв отдельные файлы. footer.php остаётся < 100 строк (layout-only).
Шаг 14.1 — Аудит inline JS в footer.php
- Список всех
<script>блоков вadmin/footer.phpс привязкой к$_TITLE/ page - Подсчёт строк по блокам
- Группировка: общий JS (все страницы) vs page-specific JS
Шаг 14.2 — Извлечение page-specific JS
Паттерн:
// Было в footer.php:
<?php if ($_TITLE == 'streams'): ?>
<script>
// 200 строк JS для таблицы стримов
</script>
<?php endif; ?>
// Стало:
// public/assets/js/pages/streams.js — отдельный файл
// footer.php: <script src="assets/js/pages/<?= $_TITLE ?>.js"></script>
Действия:
- Для каждого page-блока → создать
public/assets/js/pages/{page}.js - В footer.php: динамический
<script src>по$_TITLE - Общий JS (DataTables init, modals, AJAX helpers) →
public/assets/js/common.js
Шаг 14.3 — Минификация (опционально)
Если нужна production-оптимизация:
make js-minifytarget в Makefile- terser или closure-compiler для
assets/js/pages/*.js - Версионирование:
?v={hash}в<script src>
8. Фаза 15: Удаление includes/admin.php — финальный legacy bootstrap
Цель:
includes/admin.php(последний legacy-файл) удалён. Весь bootstrap черезXC_Bootstrap::boot().
Шаг 15.1 — Аудит зависимостей от includes/admin.php
grep -rn "include.*admin\.php\|require.*admin\.php" src/— все подключения- Для каждого подключения: что именно ожидается (глобальные переменные, функции, сессия)?
- Составить список: что ещё живёт ТОЛЬКО в
includes/admin.phpи не имеет замены
Шаг 15.2 — Перенос оставшихся функций
Ожидаемые остатки:
- глобальные функции-утилиты →
includes/admin_functions.phpили доменные сервисы $languageинициализация →core/I18n/Translator.php- session bootstrap →
core/Auth/SessionManager.php $rPermissionsзагрузка →domain/Auth/AuthorizationService.php
Шаг 15.3 — Переключение bootstrap
BaseAdminController::before(): вместо dual bootstrap → толькоXC_Bootstrap::boot(CONTEXT_ADMIN)BaseResellerController::before(): аналогично- Feature flag:
use_legacy_bootstrap = false - Тестирование всех 134 страниц
Шаг 15.4 — Удаление includes/admin.php
rm src/includes/admin.php- Удалить
require 'admin.php'из всех файлов - Обновить
bootstrap.php— убрать dual bootstrap ветку php -l+ полный smoke test
9. Порядок выполнения и риск-матрица
Порядок выполнения фаз 10–15
Фаза 10 ─── Удаление admin/ legacy entry-points ✅ ВЫПОЛНЕНА
│
▼
Фаза 11 ─── Унификация API (controllers ✅ / deletion ⏳) ✅ 11.1–11.4
│
▼
Фаза 12 ─── CLI единый runner + удаление includes/cli/ ✅ ВЫПОЛНЕНА
│
▼
Фаза 13 ─── Streaming micro-optimizations ✅ 13.1–13.3
│
▼
Фаза 14 ─── CSS/JS partials (footer.php разбиение) ⏳ НЕ НАЧАТА
│
▼
Фаза 15 ─── Удаление includes/admin.php ⏳ НЕ НАЧАТА
Риск-матрица
| Фаза | Риск | Обоснование |
|---|---|---|
| 10 | ✅ Завершена | Все 6 шагов выполнены |
| 11 | ✅ Controllers / ⏳ Deletion | 11.1–11.4 — контроллеры + proxies. 11.5–11.6 — REST API extraction + deletion отложены |
| 12 | ✅ Завершена | console.php, 26 Commands, 25 CronJobs, includes/cli/ удалена, LB guards |
| 13 | ✅ 13.1–13.3 / ⏳ 13.4 | ShutdownHandler + StreamAuthMiddleware. Ministra отложена |
| 14 | 🟢 Низкий | Только frontend-рефакторинг, не ломает backend |
| 15 | 🔴 Высокий | Удаление последнего legacy bootstrap, нет fallback |
Разделение релизов (фазы 0–9)
| Релиз | Содержит | Риск |
|---|---|---|
| v1.8 | Фазы 0–2 (core/ extraction, dedup) | 🟢 Низкий — proxy-методы, обратная совместимость |
| v1.9 | Фазы 3–4 (domain/ + streaming/ extraction) | 🟡 Средний — больше перемещений, proxy покрывает |
| v2.0 | Фазы 5–6 (modules + controllers) | 🟡 Средний — новая маршрутизация, dual bootstrap |
| v2.1 | Фазы 7–8 (cleanup, удаление legacy) | 🔴 Высокий — удаление god-объектов, нет fallback |
Разделение релизов (фазы 10–15)
| Релиз | Содержит | Риск |
|---|---|---|
| v2.2 | Фаза 10 (удаление admin/) | ✅ Завершена |
| v2.3 | Фаза 11 (API controllers) + Фаза 12 (CLI runner) + Фаза 13 (streaming) | ✅ Завершена |
| v2.4 | Фаза 11 (API deletion) + Фаза 14 (CSS/JS partials) | 🟢 + 🟢 |
| v3.0 | Фаза 15 (удаление legacy bootstrap) | 🔴 Финальный milestone |
10. Стратегия миграции по рискам
10.1. Принцип: каждый шаг обратим
Каждое изменение следует паттерну extract → delegate → verify → replace:
1. Extract: создать новый класс
2. Delegate: старый код вызывает новый через proxy
3. Verify: система работает как раньше + новый код работает
4. Replace: обновить вызывающий код на прямые вызовы (отдельный шаг)
Если шаг 3 провалился — откат = удалить новый класс + убрать proxy. Система возвращается в предыдущее состояние.
10.2. Регрессионная стратегия
| Уровень | Что проверяется | Как проверяется | Когда |
|---|---|---|---|
| Syntax | PHP-файлы компилируются | php -l на каждый изменённый файл |
После каждого коммита |
| Smoke | Система запускается | make new && make lb + проверка HTTP 200 |
После каждой фазы |
| Functional | Основные сценарии работают | Ручной checklist (создать поток, запустить, остановить) | После каждой фазы |
| Integration | LB-сборка работает | Деплой на тестовый LB-сервер + стриминг-тест | После фаз 1–4 |
| Backward compat | API не сломан | Проверка ответов API (формат JSON, коды ошибок) | После фазы 6 |
10.3. Dual bootstrap на переходном этапе
Текущее состояние (фазы 1–6): Dual bootstrap работает параллельно.
bootstrap.php (новый) includes/admin.php (старый legacy)
│ │
├── autoload.php ├── require Database.php
├── ServiceContainer ├── CoreUtilities::init()
├── ConfigLoader ├── API/ResellerAPI init
└── новые core/ классы └── 50 define() + global $db
Правила dual bootstrap:
bootstrap.phpзагружается ПЕРВЫМ в каждой точке входаincludes/admin.phpзагружается ПОСЛЕ — для legacy-кода, который ещё не мигрирован- Новые классы (
core/,domain/) инициализируются черезServiceContainer - Legacy-код (
CoreUtilities,admin_api.php) продолжает работать через proxy-методы - После полной миграции (Фаза 15) —
includes/admin.phpудаляется
10.4. API backward compatibility
Правило: Внешний API (player.api, xmltv.php, межсерверный API) не меняет формат ответов до Фазы 11.
Фазы 1–10: Внутренняя рефакторизация, внешний API неизменен
Фаза 11: API v2 (опционально) с новой маршрутизацией
API v1 продолжает работать через compatibility layer
10.5. Rollback plan
Если релиз ломает production:
1. git revert последний merge в main
2. Пересобрать: make new && make lb
3. Задеплоить предыдущую сборку
4. Post-mortem: что сломалось, почему не поймали на smoke test
Для Фазы 15 (удаление legacy) — feature flag:
// config.ini
[migration]
use_legacy_bootstrap = false ; true = откат на admin.php
use_legacy_api = false ; true = откат на admin_api.php switch