# XC_VM — План миграции > Этот документ описывает **порядок миграции**, **детали каждой фазы** и **стратегию управления рисками**. > Архитектурные принципы, структура проекта и описание компонентов — см. [ARCHITECTURE.md](ARCHITECTURE.md). ## Содержание 1. [Принцип миграции](#1-принцип-миграции) 2. [Фазы 0–9: Завершённые](#2-фазы-09-завершённые) 3. [Фаза 10: Удаление legacy admin entry-points ✅](#3-фаза-10-удаление-legacy-admin-entry-points) 4. [Фаза 11: Унификация API ✅ (11.1–11.4)](#4-фаза-11-унификация-api) 5. [Фаза 12: CLI — единый runner ✅](#5-фаза-12-cli--единый-runner) 6. [Фаза 13: Streaming entry points ✅ (13.1–13.3)](#6-фаза-13-streaming-entry-points) 7. [Фаза 14: CSS/JS partials](#7-фаза-14-cssjs-partials) 8. [Фаза 15: Удаление includes/admin.php](#8-фаза-15-удаление-includesadminphp) 9. [Порядок выполнения и риск-матрица](#9-порядок-выполнения-и-риск-матрица) 10. [Стратегия миграции по рискам](#10-стратегия-миграции-по-рискам) --- ## 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 свойств → `BlocklistService` lazy 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):** 1. `LB_FILES` → `LB_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 ``` **Масштаб:** 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.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) ``` **Запуск:** ```bash # Было: 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/service` → `console.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 == 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` в кодовой базе. **Выполнено:** 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()` | — | | `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`:** ```makefile 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 + 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. Список всех ` // Стало: // public/assets/js/pages/streams.js — отдельный файл // footer.php: ``` **Действия:** 1. Для каждого page-блока → создать `public/assets/js/pages/{page}.js` 2. В footer.php: динамический `