Files
XC_VM/docs/en/development/autoloader.md
T
Divarion-D f8a37947b1 feat(tmdb)!: fold the tmdb module into core, replace stale standard-set copies
The panel is deeply coupled to TMDb (VOD import, player metadata, admin
search, two crons), so shipping it as an uninstallable module only added
failure modes: after the move to hash-suffixed dirs (tmdb_f4e6e) every
hardcoded `Modules/tmdb/lib/...` require broke, and 2.3.3 crons died with
"Failed opening required TmdbClient.php".

tmdb -> core:
- Vendored \TMDB client -> src/Infrastructure/Tmdb/lib/; the only loader
  is TmdbApiService::requireLibrary() (now public, also loads Release.php).
- TmdbApiService -> XcVm\Infrastructure\Tmdb — composer-autoloaded in every
  bootstrap context, no module boot required (player scope never booted
  modules, so module-namespace classes were unreachable there).
- TmdbCron / TmdbPopularCron -> XcVm\Domain\Vod; cron jobs -> Cli/CronJobs
  (picked up by the console.php scan; command names cron:tmdb and
  cron:tmdb_popular are unchanged).
- TmdbController -> Public/Controllers/Admin; tmdb_search / tmdb api
  actions registered in routes/admin.php (same dispatchApi fallback).
- Domain/Vod services and player_functions.php load the lib through
  TMDbService::requireLibrary() instead of hardcoded module paths.
- tmdb removed from config/bundled_modules.php. ModuleLoader gains
  CORE_PROVIDED_MODULES: released watch/plex archives still declare
  "dependencies": ["tmdb"] — such deps are stripped during manifest
  normalization and in ModuleManager::listModules().
- syncBundledModules() purges stale on-disk tmdb module dirs and their
  config/modules.php state on upgraded panels, so the old copy cannot boot
  alongside the core implementation and collide on command names.

Standard-set provisioning fix (root cause of the "Undefined variable $db"
errors from watch/plex settings views on 2.3.3):
- Production still ran watch_e6c86/plex_20cd9-less legacy copies migrated
  from 2.3.2 with generated hash_ids; provisionStandardSet() treated any
  same-name directory as "already on disk" and never fetched the pinned
  1.0.2/1.0.1 releases that contain the fix. A same-name directory whose
  identity does not match the pinned hash_id is now considered stale: it
  is deleted and the pinned release is installed in its place.
- installModuleFromSource(): when the module is already recorded as
  installed (files re-provisioned over a stale copy), run updateModule()
  (incremental from->to migrations) instead of re-running the initial
  install.
2026-07-05 20:17:31 +03:00

113 lines
4.0 KiB
Markdown

# Autoloading (PSR-4)
XC_VM autoloads classes with a standard **Composer PSR-4** autoloader; the namespace encodes the file path, so resolution is a direct `file_exists` with no scan and no cache.
---
## Overview
Every first-party class lives under the `XcVm\` root namespace, mapped to `src/`:
```text
XcVm\Core\Auth\Authenticator -> src/Core/Auth/Authenticator.php
XcVm\Domain\Stream\StreamService -> src/Domain/Stream/StreamService.php
XcVm\Public\Controllers\Admin\UserController -> src/Public/Controllers/Admin/UserController.php
```
The mapping is declared in `src/composer.json`:
```json
"autoload": {
"psr-4": {
"XcVm\\": "./",
"M3uParser\\": "Core/Parsing/M3uParser/src/",
"Chrisyue\\PhpM3u8\\": "Core/Parsing/PhpM3u8/src/"
}
}
```
`src/vendor/` (the Composer autoloader + production dependencies) is committed and
shipped — the deploy path has no Composer and never runs `composer install`. There
is **no class-map cache** (no `optimize-autoloader`): a class miss is a plain path
lookup, not a directory rescan.
## Adding a new class
Create the file at the path its namespace maps to — that is all; Composer resolves it on demand:
```php
// src/Domain/Billing/InvoiceService.php
namespace XcVm\Domain\Billing;
class InvoiceService {
public static function generate(int $userId): string { /* ... */ }
}
```
Reference it from other namespaced code with a `use` import, or by its FQCN:
```php
use XcVm\Domain\Billing\InvoiceService;
```
No cache to clear, no registry to edit. A brand-new sub-namespace (e.g.
`XcVm\Domain\Billing`) works immediately because it maps straight onto the
directory.
## Naming rules
| Rule | Example |
| --- | --- |
| File name **must** match the class name | `InvoiceService.php` → `class InvoiceService` |
| One class per file | PSR-4 resolves one class per path; split multi-class files |
| Namespace **must** match the directory path (case-sensitive) | `src/Domain/Billing/` → `namespace XcVm\Domain\Billing;` |
| PascalCase classes and directories | `StreamService`, `DatabaseHandler`, `Core/Auth/` |
| Project convention: no `declare(strict_types=1)` | — |
Because the namespace carries the location, duplicate short names in different
namespaces no longer collide — `XcVm\Public\Controllers\Admin\PlexController` and
`XcVm\Module\Plex\PlexController` are distinct.
## Procedural and third-party files
Some files are intentionally **not** namespaced and are loaded by explicit
`require`, not the autoloader:
- procedural entry points, views and bootstrap glue (e.g. `Public/index.php`,
`Public/Views/**`, `Infrastructure/Bootstrap/*.php`);
- global constants and functions (`Core/Config/*`, error handler);
- the ioncube `XC_VM` class and bundled `Infrastructure/Tmdb/lib/*`.
The vendored `M3uParser` and `Chrisyue\PhpM3u8` packages have their own PSR-4
prefixes (above) and autoload normally.
## Modules
Module classes use the `XcVm\Module\<Name>\…` namespace but are **not** registered
in `composer.json` (module/marketplace slug directories — `plex`, `watch-d2bho` —
do not fit a single PSR-4 rule). They are resolved by `ModuleLoader`'s own PSR-4
resolver: it strips the module's base namespace and maps the remainder onto a
sub-path under the module directory. See [Module System](modules.md).
## Dev tooling
The committed `vendor/` is production-only. PHPStan and PHP-CS-Fixer are
`require-dev` packages — install them locally with:
```bash
make dev-tools # = cd src && composer install
```
They are never committed (a CI gate enforces a prod-only committed vendor). See
[Development Workflow](../guides/dev-workflow.md).
## Related files
| File | Role |
| --- | --- |
| `src/composer.json` | PSR-4 prefix map + dependencies |
| `src/composer.lock` | committed lock for reproducible `composer install` |
| `src/vendor/` | committed Composer autoloader + production deps |
| `src/bootstrap.php` | defines `MAIN_HOME`, requires `vendor/autoload.php` |
| `src/Core/Module/ModuleLoader.php` | PSR-4 resolver for module classes |