# Жизненный цикл модуля Как XC_VM обнаруживает, загружает, включает/отключает, устанавливает и распространяет модули во время выполнения. Чтобы создать модуль, смотрите [Разработка модуля](module-authoring.md); для его расширений смотрите [Точки расширения модуля](module-extension-points.md). ## Включение / выключение модулей Все обнаруженные модули загружаются по умолчанию. Используйте `src/config/modules.php` для переопределения состояния: ```php return [ 'my-module' => ['state' => 'disabled'], // preferred // or legacy boolean (still accepted): 'my-module' => ['enabled' => false], ]; ``` Доступные значения `state` (подкрепленные перечислением `ModuleState`): |Ценность|Значение| | ----- | ------- | | `enabled` |Загрузка модуля (по умолчанию)| | `disabled` |Модуль обнаружен, но пропущен| | `installing` |Переходное состояние, заданное значением `ModuleManager` во время установки| | `failed` |Ошибка установки; модуль пропущен (не загружен)| > **Панельная диагностика.** На странице **Модули** отображается желтый значок **⚠ Проблема зависимости** рядом со статусом модуля, если требуемая зависимость отсутствует или не включена (например, в `plex` указано `Enabled`, а в `watch` - `failed`). Во всплывающей подсказке к значку перечислены конкретные проблемы. Это поле `dependency_warnings` вычисляется с помощью `ModuleManager::listModules()`. Чтобы переопределить класс, разрешенный для модуля: ```php return [ 'my-module' => ['class' => 'XcVm\\Module\\MyModuleV2\\MyModuleV2Module'], ]; ``` `config/modules.php` содержит только переопределения. Пустой или отсутствующий файл означает, что все обнаруженные загружаются модули. --- ## Как работает загрузка `ModuleLoader` выполняет эти действия при каждом запросе: 1. Сканирование `src/Modules/*/module.json` 2. Применяет переопределения из `config/modules.php` 3. Фильтры по окружающей среде (`main` / `lb` / `any`) 4. Определяет порядок загрузки: - `pruneUnsatisfiableModules()` удаляет модули, требуемые зависимости которых недоступны (каскадно, с зарегистрированным предупреждением), чтобы загрузка никогда не прерывалась - Топологическая сортировка (DFS) по графу зависимостей - В пределах одной и той же группы зависимостей выполните сортировку по убыванию `priority`, затем по алфавиту - Выдает `ModuleCycleException` для циклов (подкласс `\RuntimeException`; циклические зависимости остаются фатальными) - Отсутствующие необязательные зависимости автоматически пропускаются 5. Resolves class name: `my-module` → FQN `XcVm\Module\MyModule\MyModuleModule` (kebab-case → PascalCase; может быть переопределен с помощью клавиши `class` в конфигурации) 6. Регистрирует автозагрузчик модуля PSR-4 (сопоставляет `XcVm\Module\` с каталогом модуля) 7. Создает экземпляр класса module В веб-контексте: - `bootAll($container, $router)` → вызовы `boot()`, `registerRoutes()`, `registerNavbar()`, и подписывается на события для каждого загруженного модуля В контексте командной строки: - `registerAllCommands($registry)` → вызывает `registerCommands()` для каждого загруженного модуля --- ## Marketplace: установка через расширение C Модули с платформы устанавливаются через `ModuleManager::downloadFromPlatform()`: ```php $manager->downloadFromPlatform(slug: 'my-module', version: '1.2.0', apiKey: $key); ``` Под капотом: 1. `XC_VM::module_install($slug, $version, $apiKey)` — Расширение C загружает, расшифровывает и распаковывает файлы 2. `installModule($slug)` — запускает `install()` в модуле 3. `EventDispatcher::dispatch(new PackageInstalledEvent(...))` — отправляет событие 4. `hotReload($slug, $path)` — загружает модуль в текущем запросе **без перезапуска PHP-FPM** --- ## Изолированные подсистемы Модуль может быть полностью изолированной подсистемой со своей собственной точкой входа и начальной загрузкой (например, Ministra). Это **соглашение**, а не маркерный интерфейс — он остается обычный модуль `ModuleInterface`/`BaseModule`: ```php class MyModule extends BaseModule { public function getName(): string { return 'my-module'; } public function getVersion(): string { return '1.0.0'; } } ``` Изоляция означает, что подсистема работает через свою собственную общедоступную точку входа (например, `my-module/portal.php`, путь относительно `src/`, который обрабатывает свой собственный bootstrap) с отдельным путем начальной загрузки. Он использует общую инфраструктуру (базу данных, кэш, конфигурацию), но участвует ли **нет** в основных `Router`, `ModuleLoader::bootAll()` или `NavbarRegistry`. Реализации `boot()` и `registerRoutes()` обычно являются оставлено как унаследованное бездействие. --- ## Composer обнаружение пакета Модули могут распространяться в виде Composer пакетов с `"type": "xcvm-module"`: ```json { "name": "vendor/my-xcvm-module", "type": "xcvm-module", "extra": { "xcvm": { "module-path": "src" } } } ``` `ModuleLoader` автоматически сканирует `vendor/composer/installed.json` (Composer 1 и 2 форматирует) и обнаруживает все установленные пакеты `xcvm-module` вместе со встроенным каталог `src/Modules/`. Пакеты дедуплицируются — модуль как в `modules/`, так и в `vendor/` загружается только один раз. ---