diff --git a/docs/en/_sidebar.md b/docs/en/_sidebar.md index ac98a09..4deaeaf 100644 --- a/docs/en/_sidebar.md +++ b/docs/en/_sidebar.md @@ -19,6 +19,7 @@ - 🧰 **Development** - [🖥 CLI Tools & DB Migrations](en-us/development/cli-tools.md) + - [⚙️ Autoloader & Class Registration](en-us/development/autoloader.md) - 📦 **Additional** - [🎬 Watchfolder](en-us/info/watch_folder.md) diff --git a/docs/en/development/autoloader.md b/docs/en/development/autoloader.md new file mode 100644 index 0000000..43e9dc5 --- /dev/null +++ b/docs/en/development/autoloader.md @@ -0,0 +1,148 @@ +# Autoloader — Class Registration + +XC_VM uses a custom autoloader (`src/autoload.php`) instead of Composer. +Classes are discovered **automatically** — no manual registration required. + +--- + +## How It Works + +``` +Request → autoload.php included + │ + ├── Cache file exists? + │ YES → load igbinary cache → O(1) lookups + │ NO → warmCache(): scan all directories via token_get_all() + │ → build ClassName → filePath map + │ → persist to tmp/cache/autoload_map + │ + └── Class requested by PHP + │ + ├── 1. Explicit classMap (addClass()) + ├── 2. Resolved cache (from file or previous lookup) + └── 3. Live directory scan (fallback, cached for next time) +``` + +## Adding a New Class + +**Just create the file.** That's it. + +Place your PHP file in any registered directory (or its subdirectory): + +| Directory | Purpose | +|-----------|---------| +| `src/core/` | Framework core (Database, Cache, Auth, Http, Process, etc.) | +| `src/domain/` | Business logic (Services, Repositories) | +| `src/infrastructure/` | External adapters (DatabaseFactory, CacheReader, Redis) | +| `src/streaming/` | Streaming subsystem (Auth, Delivery, Codec, Health) | +| `src/modules/` | Optional modules (Plex, Watch, TMDB, Ministra, etc.) | +| `src/public/` | Controllers and Views | +| `src/includes/` | Legacy code | +| `src/includes/libs/` | Third-party libraries | + +### Example + +```php +// src/domain/Billing/InvoiceService.php +class InvoiceService { + public static function generate($userId) { ... } +} +``` + +After creating the file, **delete the cache** so the autoloader rediscovers classes: + +```bash +rm -f /home/xc_vm/tmp/cache/autoload_map +``` + +On the next request, `warmCache()` runs automatically, finds `InvoiceService`, and caches it. + +## Cache Invalidation + +The cache file `tmp/cache/autoload_map` is a binary file (igbinary format). +It must be deleted whenever: + +- A new class file is added +- A class file is renamed or moved +- A class file is deleted + +```bash +# Delete manually +rm -f /home/xc_vm/tmp/cache/autoload_map + +# Or via PHP +XC_Autoloader::clearCache(); +``` + +> **Note:** If a class is requested that isn't in the cache, the autoloader falls back to a live directory scan and caches the result automatically. So the cache only needs to be cleared when files are moved/renamed/deleted — new classes will eventually be found via fallback. + +## Manual Registration (Rare) + +For special cases where a file contains multiple classes or the filename doesn't match the class name: + +```php +XC_Autoloader::addClass('DropboxClient', '/home/xc_vm/includes/libs/Dropbox.php'); +XC_Autoloader::addClass('DropboxException', '/home/xc_vm/includes/libs/Dropbox.php'); +``` + +This is mainly needed for legacy library files that bundle multiple classes in one file (e.g., `iptables.php`, `m3u.php`). + +## Adding a New Source Directory + +Edit `registerDirectories()` in `src/autoload.php`: + +```php +private static function registerDirectories() { + $base = self::$basePath; + + self::addDirectory($base . 'includes'); + self::addDirectory($base . 'includes/libs'); + self::addDirectory($base . 'core'); + self::addDirectory($base . 'domain'); + self::addDirectory($base . 'infrastructure'); + self::addDirectory($base . 'streaming'); + self::addDirectory($base . 'modules'); + self::addDirectory($base . 'public'); + + // Add your new directory here: + self::addDirectory($base . 'my_new_dir'); +} +``` + +Then delete the cache. + +## Naming Rules + +| Rule | Example | +|------|---------| +| File name **must** match class name | `InvoiceService.php` → `class InvoiceService` | +| One class per file (recommended) | Multi-class files need `addClass()` | +| PascalCase | `StreamService`, `DatabaseHandler` | +| No namespaces | `class StreamService { }` — no `namespace` keyword | +| No `declare(strict_types=1)` | Project convention | + +## Duplicate Class Names + +If two files define the same class name, **first-found wins** based on directory scan order. This is fragile — avoid duplicate names. Use prefixed names instead: + +``` +✗ public/Controllers/Admin/PlexController.php ← conflict +✗ modules/plex/PlexController.php ← conflict + +✓ public/Controllers/Admin/AdminPlexController.php ← unique +✓ modules/plex/PlexController.php ← unique +``` + +## Debugging + +```php +// See all registered directories +print_r(XC_Autoloader::getDirectories()); + +// See explicit class map +print_r(XC_Autoloader::getClassMap()); + +// Force full rescan +XC_Autoloader::clearCache(); +XC_Autoloader::warmCache(); +``` diff --git a/docs/ru/_sidebar.md b/docs/ru/_sidebar.md index 95d6635..69f1880 100644 --- a/docs/ru/_sidebar.md +++ b/docs/ru/_sidebar.md @@ -19,6 +19,7 @@ - 🧰 **Разработка** - [🖥 CLI-инструменты и миграции БД](ru-ru/development/cli-tools.md) + - [⚙️ Автозагрузчик и регистрация классов](ru-ru/development/autoloader.md) - 📦 **Дополнительно** - [🎬 Watchfolder](ru-ru/info/watch_folder.md) diff --git a/docs/ru/development/autoloader.md b/docs/ru/development/autoloader.md new file mode 100644 index 0000000..a60302c --- /dev/null +++ b/docs/ru/development/autoloader.md @@ -0,0 +1,148 @@ +# Автозагрузчик — Регистрация классов + +XC_VM использует собственный автозагрузчик (`src/autoload.php`) вместо Composer. +Классы обнаруживаются **автоматически** — ручная регистрация не нужна. + +--- + +## Как это работает + +``` +Запрос → подключается autoload.php + │ + ├── Файл кэша существует? + │ ДА → загрузить igbinary кэш → O(1) поиск + │ НЕТ → warmCache(): сканировать все директории через token_get_all() + │ → построить карту ClassName → filePath + │ → сохранить в tmp/cache/autoload_map + │ + └── PHP запрашивает класс + │ + ├── 1. Явная карта (addClass()) + ├── 2. Кэш (файловый или из предыдущего поиска) + └── 3. Живой поиск по директориям (fallback, кэшируется) +``` + +## Добавление нового класса + +**Просто создайте файл.** Это всё. + +Поместите PHP-файл в любую зарегистрированную директорию (или её поддиректорию): + +| Директория | Назначение | +|-----------|---------| +| `src/core/` | Ядро фреймворка (Database, Cache, Auth, Http, Process и т.д.) | +| `src/domain/` | Бизнес-логика (Services, Repositories) | +| `src/infrastructure/` | Внешние адаптеры (DatabaseFactory, CacheReader, Redis) | +| `src/streaming/` | Стриминг-подсистема (Auth, Delivery, Codec, Health) | +| `src/modules/` | Опциональные модули (Plex, Watch, TMDB, Ministra и т.д.) | +| `src/public/` | Контроллеры и Views | +| `src/includes/` | Legacy-код | +| `src/includes/libs/` | Сторонние библиотеки | + +### Пример + +```php +// src/domain/Billing/InvoiceService.php +class InvoiceService { + public static function generate($userId) { ... } +} +``` + +После создания файла **удалите кэш**, чтобы автозагрузчик обнаружил новые классы: + +```bash +rm -f /home/xc_vm/tmp/cache/autoload_map +``` + +При следующем запросе `warmCache()` запустится автоматически, найдёт `InvoiceService` и закэширует. + +## Инвалидация кэша + +Файл кэша `tmp/cache/autoload_map` — бинарный файл (формат igbinary). +Его нужно удалять когда: + +- Добавлен новый файл с классом +- Файл с классом переименован или перемещён +- Файл с классом удалён + +```bash +# Удалить вручную +rm -f /home/xc_vm/tmp/cache/autoload_map + +# Или через PHP +XC_Autoloader::clearCache(); +``` + +> **Примечание:** Если запрошен класс, которого нет в кэше, автозагрузчик делает живой поиск по директориям и кэширует результат. Поэтому кэш нужно сбрасывать только при перемещении/переименовании/удалении — новые классы будут найдены через fallback. + +## Ручная регистрация (редко) + +Для особых случаев когда файл содержит несколько классов или имя файла не совпадает с именем класса: + +```php +XC_Autoloader::addClass('DropboxClient', '/home/xc_vm/includes/libs/Dropbox.php'); +XC_Autoloader::addClass('DropboxException', '/home/xc_vm/includes/libs/Dropbox.php'); +``` + +Это нужно в основном для legacy-библиотек с несколькими классами в одном файле (например, `iptables.php`, `m3u.php`). + +## Добавление новой директории + +Отредактируйте `registerDirectories()` в `src/autoload.php`: + +```php +private static function registerDirectories() { + $base = self::$basePath; + + self::addDirectory($base . 'includes'); + self::addDirectory($base . 'includes/libs'); + self::addDirectory($base . 'core'); + self::addDirectory($base . 'domain'); + self::addDirectory($base . 'infrastructure'); + self::addDirectory($base . 'streaming'); + self::addDirectory($base . 'modules'); + self::addDirectory($base . 'public'); + + // Добавьте новую директорию: + self::addDirectory($base . 'my_new_dir'); +} +``` + +Затем удалите кэш. + +## Правила именования + +| Правило | Пример | +|---------|--------| +| Имя файла **должно** совпадать с именем класса | `InvoiceService.php` → `class InvoiceService` | +| Один класс на файл (рекомендуется) | Файлы с несколькими классами требуют `addClass()` | +| PascalCase | `StreamService`, `DatabaseHandler` | +| Без namespace | `class StreamService { }` — без ключевого слова `namespace` | +| Без `declare(strict_types=1)` | Конвенция проекта | + +## Дублирование имён классов + +Если два файла определяют одинаковое имя класса, **побеждает первый найденный** по порядку сканирования директорий. Это хрупко — избегайте дубликатов. Используйте префиксы: + +``` +✗ public/Controllers/Admin/PlexController.php ← конфликт +✗ modules/plex/PlexController.php ← конфликт + +✓ public/Controllers/Admin/AdminPlexController.php ← уникально +✓ modules/plex/PlexController.php ← уникально +``` + +## Отладка + +```php +// Все зарегистрированные директории +print_r(XC_Autoloader::getDirectories()); + +// Явная карта классов +print_r(XC_Autoloader::getClassMap()); + +// Принудительный полный пересканирование +XC_Autoloader::clearCache(); +XC_Autoloader::warmCache(); +```