mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-03 20:02:29 +02:00
Verify every dev-doc claim against src/ and fix factual drift: wrong method signatures/return types, wrong enum casing (BootContext cases are PascalCase), stale paths (M3u parsers are Composer deps under vendor/, MobileDetect is mobiledetect/mobiledetectlib v4.9.0 \Detection\MobileDetect, NotFoundException lives in XcVm\Core\Container\Psr), a fictional `stream:check` command/class, reversed migration-failure semantics ([FAIL] = not recorded, retried), inverted isStreamRunning/isStreamAlive descriptions, findProcessPIDs ANY-not-ALL, acquireCronLock has no shutdown callback, and nonexistent make targets. Split oversized pages and fix nav + cross-links: - modules.md -> module-authoring / module-lifecycle / module-extension-points - cli-tools.md -> cli-tools + database-migrations - streaming-subsystem.md -> + streaming-diagnostics - geoip-and-device-detection.md -> geoip-isp-and-geo-routing + device-detection-and-stb-locking Add development/core-wiring.md: how the core assembles itself at boot (container population, ServiceContainer reference, bootAll orchestration, CLI command auto-discovery, end-to-end Admin/CLI boot walkthroughs). Only docs/en + mkdocs.yml touched; docs/ru is regenerated before release.
142 lines
4.7 KiB
Markdown
142 lines
4.7 KiB
Markdown
# Module Lifecycle
|
|
|
|
How XC_VM discovers, loads, enables/disables, installs and distributes modules at runtime. To author a module see [Module Authoring](module-authoring.md); for its extension hooks see [Module Extension Points](module-extension-points.md).
|
|
|
|
## Enable / disable modules
|
|
|
|
|
|
All discovered modules load by default. Use `src/config/modules.php` to override state:
|
|
|
|
```php
|
|
return [
|
|
'my-module' => ['state' => 'disabled'], // preferred
|
|
// or legacy boolean (still accepted):
|
|
'my-module' => ['enabled' => false],
|
|
];
|
|
```
|
|
|
|
Available `state` values (backed by `ModuleState` enum):
|
|
|
|
| Value | Meaning |
|
|
| ----- | ------- |
|
|
| `enabled` | Module loads and boots (default) |
|
|
| `disabled` | Module is discovered but skipped |
|
|
| `installing` | Transient state set by `ModuleManager` during install |
|
|
| `failed` | Install failed; module skipped (not loaded) |
|
|
|
|
> **Panel diagnostics.** The **Modules** page shows a yellow **⚠ Dependency issue** badge next to a module's status when a required dependency is missing or not enabled (e.g. `plex` reads `Enabled` but `watch` is `failed`). The badge tooltip lists the concrete problems. This `dependency_warnings` field is computed by `ModuleManager::listModules()`.
|
|
|
|
To override the class resolved for a module:
|
|
|
|
```php
|
|
return [
|
|
'my-module' => ['class' => 'XcVm\\Module\\MyModuleV2\\MyModuleV2Module'],
|
|
];
|
|
```
|
|
|
|
`config/modules.php` contains only overrides. An empty or missing file means all discovered
|
|
modules load.
|
|
|
|
---
|
|
|
|
## How loading works
|
|
|
|
|
|
`ModuleLoader` follows these steps on every request:
|
|
|
|
1. Scans `src/Modules/*/module.json`
|
|
2. Applies overrides from `config/modules.php`
|
|
3. Filters by environment (`main` / `lb` / `any`)
|
|
4. Resolves the load order:
|
|
- `pruneUnsatisfiableModules()` drops modules whose required dependencies are unavailable (cascading, with a logged warning) so the load never aborts
|
|
- Topological sort (DFS) over the dependency graph
|
|
- Within the same dependency group, sorts by `priority` descending, then alphabetically
|
|
- Throws `ModuleCycleException` on cycles (a subclass of `\RuntimeException`; cyclic dependencies remain fatal)
|
|
- Missing optional dependencies are silently skipped
|
|
5. Resolves class name: `my-module` → FQN `XcVm\Module\MyModule\MyModuleModule`
|
|
(kebab-case → PascalCase; can be overridden via `class` key in config)
|
|
6. Registers the module's PSR-4 autoloader (maps `XcVm\Module\<Name>` onto the module directory)
|
|
7. Instantiates the module class
|
|
|
|
In web context:
|
|
|
|
- `bootAll($container, $router)` → calls `boot()`, `registerRoutes()`, `registerNavbar()`,
|
|
and subscribes to events for every loaded module
|
|
|
|
In CLI context:
|
|
|
|
- `registerAllCommands($registry)` → calls `registerCommands()` on every loaded module
|
|
|
|
---
|
|
|
|
## Marketplace: install via C extension
|
|
|
|
|
|
Modules from the platform are installed via `ModuleManager::downloadFromPlatform()`:
|
|
|
|
```php
|
|
$manager->downloadFromPlatform(slug: 'my-module', version: '1.2.0', apiKey: $key);
|
|
```
|
|
|
|
Under the hood:
|
|
|
|
1. `XC_VM::module_install($slug, $version, $apiKey)` — C extension downloads, decrypts, unpacks
|
|
2. `installModule($slug)` — runs `install()` on the module
|
|
3. `EventDispatcher::dispatch(new PackageInstalledEvent(...))` — dispatches the event
|
|
4. `hotReload($slug, $path)` — loads and boots the module in the current request **without PHP-FPM restart**
|
|
|
|
---
|
|
|
|
## Isolated subsystems
|
|
|
|
|
|
A module can be a fully isolated subsystem with its own entry point and bootstrap
|
|
(like Ministra). This is a **convention**, not a marker interface — it stays an
|
|
ordinary `ModuleInterface`/`BaseModule` module:
|
|
|
|
```php
|
|
class MyModule extends BaseModule {
|
|
|
|
public function getName(): string {
|
|
return 'my-module';
|
|
}
|
|
|
|
public function getVersion(): string {
|
|
return '1.0.0';
|
|
}
|
|
}
|
|
```
|
|
|
|
Isolation means the subsystem runs through its own public entry point (e.g.
|
|
`my-module/portal.php`, a path relative to `src/` that handles its own bootstrap)
|
|
with a separate bootstrap path. It shares infrastructure (db, cache, config) but
|
|
does **not** participate in the main `Router`, `ModuleLoader::bootAll()`, or
|
|
`NavbarRegistry`. The `boot()` and `registerRoutes()` implementations are typically
|
|
left as inherited no-ops.
|
|
|
|
---
|
|
|
|
## Composer package discovery
|
|
|
|
|
|
Modules can be distributed as Composer packages with `"type": "xcvm-module"`:
|
|
|
|
```json
|
|
{
|
|
"name": "vendor/my-xcvm-module",
|
|
"type": "xcvm-module",
|
|
"extra": {
|
|
"xcvm": {
|
|
"module-path": "src"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
`ModuleLoader` automatically scans `vendor/composer/installed.json` (Composer 1 and 2
|
|
formats) and discovers any installed `xcvm-module` packages alongside the built-in
|
|
`src/Modules/` directory. Packages are deduplicated — a module in both `modules/` and
|
|
`vendor/` is loaded only once.
|
|
|
|
---
|