Files
main/MIGRATION.md
Divarion-D 858ad4508c build(makefile): update LB build for module system
- 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/
2026-03-16 22:33:32 +03:00

780 lines
42 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# XC_VM — План миграции
> Этот документ описывает **порядок миграции**, **детали каждой фазы** и **стратегию управления рисками**.
> Архитектурные принципы, структура проекта и описание компонентов — см. [ARCHITECTURE.md](ARCHITECTURE.md).
## Содержание
1. [Принцип миграции](#1-принцип-миграции)
2. [Фазы 09: Завершённые](#2-фазы-09-завершённые)
3. [Фаза 10: Удаление legacy admin entry-points ✅](#3-фаза-10-удаление-legacy-admin-entry-points)
4. [Фаза 11: Унификация API ✅ (11.111.4)](#4-фаза-11-унификация-api)
5. [Фаза 12: CLI — единый runner ✅](#5-фаза-12-cli--единый-runner)
6. [Фаза 13: Streaming entry points ✅ (13.113.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. Фазы 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.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.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** `$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.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_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
<?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)
```
**Запуск:**
```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.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.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. Список всех `<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:**
```php
// config.ini
[migration]
use_legacy_bootstrap = false ; true = откат на admin.php
use_legacy_api = false ; true = откат на admin_api.php switch
```