Files
main/MIGRATION.md
Divarion-D 774d2318f3 feat: Migrate legacy CLI scripts to console.php and extract streaming middleware
CLI consolidation:
- Delete 13 legacy CLI scripts from includes/cli/ (ondemand, proxy, queue, record, scanner, signals, startup, thumbnail, tools, update, watchdog, plex_item, watch_item)
- Convert status and tools entry points to thin proxies that delegate to console.php
- Update all shell_exec() calls across admin controllers, views, and API layer to use console.php command syntax instead of direct CLI file paths
- Update src/service to launch daemons via console.php (signals, watchdog, queue, cache_handler, startup)
- Update Python src/update script to call console.php update instead of includes/cli/update.php
- Update test_installer to use console.php startup

Streaming deduplication:
- Extract StreamAuthMiddleware — common response headers and token decryption shared by live/vod/timeshift
- Extract ShutdownHandler — unified shutdown logic replacing 3 duplicate function shutdown() blocks
- Refactor live.php, vod.php, timeshift.php to use new middleware classes
- Add streaming micro-router to www/stream/index.php as fallback entry point

Routing fixes:
- Fix admin index.php redirect to use relative path (supports access code prefixes)
- Add access code root redirect in public/index.php to prevent broken CSS/JS asset resolution
- Fix init.php for CLI compatibility: guard $_SERVER access, define PHP_ERRORS safely

Migrations:
- 001_update_crontab_filenames.sql — strip .php suffix from crontab filenames
- Fix cache.php view query to match new filename format (cache_engine instead of cache_engine.php)
2026-03-14 23:57:33 +03:00

42 KiB
Raw Blame History

XC_VM — План миграции

Этот документ описывает порядок миграции, детали каждой фазы и стратегию управления рисками. Архитектурные принципы, структура проекта и описание компонентов — см. ARCHITECTURE.md.

Содержание

  1. Принцип миграции
  2. Фазы 09: Завершённые
  3. Фаза 10: Удаление legacy admin entry-points
  4. Фаза 11: Унификация API (11.111.4)
  5. Фаза 12: CLI — единый runner
  6. Фаза 13: Streaming entry points (13.113.3)
  7. Фаза 14: CSS/JS partials
  8. Фаза 15: Удаление includes/admin.php
  9. Порядок выполнения и риск-матрица
  10. Стратегия миграции по рискам

1. Принцип миграции

Извлечение → делегирование → замена

Каждый шаг миграции следует одному паттерну:

1. Создать новый класс в целевой директории
2. Перенести в него методы из god-объекта
3. В старом файле оставить proxy-метод:
     public static function oldMethod(...$args) {
         return NewClass::method(...$args);
     }
4. Зарегистрировать класс в autoloader
5. Проверить: система работает как раньше
6. (позже) Обновить вызывающий код → удалить proxy

Так каждый шаг безопасен и обратимо совместим.


2. Фазы 09: Завершённые

Фаза 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.phpXC_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.38.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.48.6 — Свойства (blocklist, FFmpeg, light)

  • 8.4 7 blocklist/allowlist свойств → BlocklistService lazy getters (FileCache)
  • 8.5 3 FFmpeg свойства → value-object FfmpegPaths::resolve()
  • 8.6 3 свойства ($rCategories, $rBouquets, $rSegmentSettings) → сервисные геттеры

Шаги 8.78.10 — Инфраструктурные свойства → singletons

  • 8.7 $rCachedFileCache, $rConfigConfigReader, $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.48.6 19 2 8
После 8.78.8 9 1 2
После 8.98.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 78) довести проект до состояния «зелёной» сборки: корректный Makefile, стандартизированные PHP-заголовки, унифицированный layout, экспорт глобалов.

Шаг 9.1 — Makefile: LB-сборка для новой архитектуры

Проблема: LB_FILES не включал core/, domain/, streaming/, infrastructure/, resources/, autoload.php, bootstrap.php — LB-сборка не могла работать с мигрированным кодом.

Решение (5 правок в Makefile):

  1. LB_FILESLB_DIRS (14 dirs) + LB_ROOT_FILES (5 root files)
  2. lb_copy_files: второй цикл для root-файлов через git ls-files --error-unmatch
  3. lb_update_copy_files: каскадная проверка (dirs → root files) для delta-обновлений
  4. LB_DIRS_TO_REMOVE: +6 admin-only исключений (includes/bootstrap, domain/User, domain/Device, domain/Auth, resources/langs, resources/libs)
  5. 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.php
  • public/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

Что сделано:

  1. Созданы контроллеры для 7 ещё не маршрутизированных страниц:
    • LogoutController — destroySession + redirect
    • PlayerEmbedController — проксирует admin/player.php
    • PostController — устанавливает $GLOBALS['__forcePostMode'], проксирует admin/post.php
    • LoginController — проксирует admin/login.php (собственный HTML-документ)
    • SetupController — методы index() (setup.php) и database() (database.php)
  2. Добавлены 7 маршрутов в public/routes/admin.php (logout, player, post, login, setup, database, index)
  3. Унифицирована секция 4a FC: все scope (admin/reseller/player) используют Router для noBootstrapPages
  4. Секция 7 (legacy fallback) обёрнута feature flag $rSettings['use_legacy_fallback'] (default: true)
  5. Модифицирован 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/

Что сделано:

  1. 127 PHP-файлов перемещены из admin/public/Views/admin/
  2. 423 статических файла (CSS, fonts, images, JS, libs) перемещены из admin/assets/public/assets/admin/
  3. Директория src/admin/ полностью удалена
  4. Обновлены все ссылки:
    • 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: $rAlias admin → 'public/Views/admin'
    • Makefile: permissions $(TEMP_DIR)/public/assets/admin
  5. nginx template (bin/nginx/conf/codes/template) уже корректный: alias /home/xc_vm/public/assets/#TYPE#/
  6. Syntax check: 126/127 pass (review.php — pre-existing bug, не регрессия)

4. Фаза 11: Унификация API — внешние и внутренние (11.111.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

  1. bin/daemons.sh → запуск через console.php {command}
  2. Crontab генерируется StartupCommand::installCrontab() / installRootCrontab()console.php cron:{name}
  3. src/serviceconsole.php service:{start|stop|restart|reload}

Шаг 12.6 — Удаление legacy CLI файлов

Выполнено:

  1. CLI_PATH constant удалён из core/Config/Paths.php
  2. 37 ссылок на CLI_PATH заменены на MAIN_HOME . 'console.php {command}' в 14 файлах
  3. includes/cli/папка удалена (24 файла)
  4. Makefile: 3 старые записи includes/cli/ удалены из EXCLUDES
  5. php -l — все 14 файлов проверены, синтаксис ОК
  6. 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 == 2console.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 в кодовой базе.

Выполнено:

  1. 35 ссылок на CRON_PATH заменены на MAIN_HOME . 'console.php cron:{name}' в 9 файлах
  2. Константа CRON_PATH удалена из core/Config/Paths.php
  3. Мёртвый autoload-маппинг EPG → crons/epg.php удалён
  4. Класс EPG восстановлен в domain/Epg/EPG.php (был потерян при proxy-конвертации)
  5. src/crons/папка удалена (25 proxy-файлов)
  6. Makefile: crons убран из LB_DIRS, 9 записей crons/*.php из LB_FILES_TO_REMOVE, 2 строки permissions
  7. 8 CronJob-файлов (MAIN-only) добавлены в LB_FILES_TO_REMOVE + file_exists() guards в console.php
  8. MigrationRunner::runFileCleanup() — теперь удаляет опустевшие директории
  9. 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()
cli/CronJobs/TmdbCronJob.php file_exists()
cli/CronJobs/TmdbPopularCronJob.php file_exists()
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
cli/CronJobs/TmdbCronJob.php
cli/CronJobs/TmdbPopularCronJob.php
domain/Epg/EPG.php

6. Фаза 13: Streaming entry points (13.113.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.phpsendStreamHeaders() + decryptToken(). Извлекает ~35 дублированных строк из каждого streaming-файла.

Изменённые файлы:

  • www/stream/live.php — ShutdownHandler + StreamAuthMiddleware
  • www/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

  1. Список всех <script> блоков в admin/footer.php с привязкой к $_TITLE / page
  2. Подсчёт строк по блокам
  3. Группировка: общий 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>

Действия:

  1. Для каждого page-блока → создать public/assets/js/pages/{page}.js
  2. В footer.php: динамический <script src> по $_TITLE
  3. Общий JS (DataTables init, modals, AJAX helpers) → public/assets/js/common.js

Шаг 14.3 — Минификация (опционально)

Если нужна production-оптимизация:

  1. make js-minify target в Makefile
  2. terser или closure-compiler для assets/js/pages/*.js
  3. Версионирование: ?v={hash} в <script src>

8. Фаза 15: Удаление includes/admin.php — финальный legacy bootstrap

Цель: includes/admin.php (последний legacy-файл) удалён. Весь bootstrap через XC_Bootstrap::boot().

Шаг 15.1 — Аудит зависимостей от includes/admin.php

  1. grep -rn "include.*admin\.php\|require.*admin\.php" src/ — все подключения
  2. Для каждого подключения: что именно ожидается (глобальные переменные, функции, сессия)?
  3. Составить список: что ещё живёт ТОЛЬКО в 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

  1. BaseAdminController::before(): вместо dual bootstrap → только XC_Bootstrap::boot(CONTEXT_ADMIN)
  2. BaseResellerController::before(): аналогично
  3. Feature flag: use_legacy_bootstrap = false
  4. Тестирование всех 134 страниц

Шаг 15.4 — Удаление includes/admin.php

  1. rm src/includes/admin.php
  2. Удалить require 'admin.php' из всех файлов
  3. Обновить bootstrap.php — убрать dual bootstrap ветку
  4. php -l + полный smoke test

9. Порядок выполнения и риск-матрица

Порядок выполнения фаз 1015

Фаза 10 ─── Удаление admin/ legacy entry-points        ✅ ВЫПОЛНЕНА
    │
    ▼
Фаза 11 ─── Унификация API (controllers ✅ / deletion ⏳)  ✅ 11.111.4
    │
    ▼
Фаза 12 ─── CLI единый runner + удаление includes/cli/   ✅ ВЫПОЛНЕНА
    │
    ▼
Фаза 13 ─── Streaming micro-optimizations                ✅ 13.113.3
    │
    ▼
Фаза 14 ─── CSS/JS partials (footer.php разбиение)       ⏳ НЕ НАЧАТА
    │
    ▼
Фаза 15 ─── Удаление includes/admin.php                  ⏳ НЕ НАЧАТА

Риск-матрица

Фаза Риск Обоснование
10 Завершена Все 6 шагов выполнены
11 Controllers / Deletion 11.111.4 — контроллеры + proxies. 11.511.6 — REST API extraction + deletion отложены
12 Завершена console.php, 26 Commands, 25 CronJobs, includes/cli/ удалена, LB guards
13 13.113.3 / 13.4 ShutdownHandler + StreamAuthMiddleware. Ministra отложена
14 🟢 Низкий Только frontend-рефакторинг, не ломает backend
15 🔴 Высокий Удаление последнего legacy bootstrap, нет fallback

Разделение релизов (фазы 09)

Релиз Содержит Риск
v1.8 Фазы 02 (core/ extraction, dedup) 🟢 Низкий — proxy-методы, обратная совместимость
v1.9 Фазы 34 (domain/ + streaming/ extraction) 🟡 Средний — больше перемещений, proxy покрывает
v2.0 Фазы 56 (modules + controllers) 🟡 Средний — новая маршрутизация, dual bootstrap
v2.1 Фазы 78 (cleanup, удаление legacy) 🔴 Высокий — удаление god-объектов, нет fallback

Разделение релизов (фазы 1015)

Релиз Содержит Риск
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-сервер + стриминг-тест После фаз 14
Backward compat API не сломан Проверка ответов API (формат JSON, коды ошибок) После фазы 6

10.3. Dual bootstrap на переходном этапе

Текущее состояние (фазы 16): 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:

  1. bootstrap.php загружается ПЕРВЫМ в каждой точке входа
  2. includes/admin.php загружается ПОСЛЕ — для legacy-кода, который ещё не мигрирован
  3. Новые классы (core/, domain/) инициализируются через ServiceContainer
  4. Legacy-код (CoreUtilities, admin_api.php) продолжает работать через proxy-методы
  5. После полной миграции (Фаза 15) — includes/admin.php удаляется

10.4. API backward compatibility

Правило: Внешний API (player.api, xmltv.php, межсерверный API) не меняет формат ответов до Фазы 11.

Фазы 110: Внутренняя рефакторизация, внешний 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