2026-06-26 15:56:15 +03:00
# Autoloading (PSR-4)
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
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.
2026-03-15 12:26:50 +03:00
---
2026-06-26 15:56:15 +03:00
## Overview
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
Every first-party class lives under the `XcVm\` root namespace, mapped to `src/` :
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
```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
2026-03-15 12:26:50 +03:00
```
2026-06-26 15:56:15 +03:00
The mapping is declared in `src/composer.json` :
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
```json
"autoload" : {
"psr-4" : {
2026-08-26 22:42:43 +03:00
"XcVm\\" : "./"
2026-06-26 15:56:15 +03:00
}
}
2026-03-15 12:26:50 +03:00
```
2026-06-26 15:56:15 +03:00
`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.
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
## Adding a new class
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
Create the file at the path its namespace maps to — that is all; Composer resolves it on demand:
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
```php
// src/Domain/Billing/InvoiceService.php
namespace XcVm\Domain\Billing ;
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
class InvoiceService {
public static function generate ( int $userId ) : string { /* ... */ }
}
2026-03-15 12:26:50 +03:00
```
2026-06-26 15:56:15 +03:00
Reference it from other namespaced code with a `use` import, or by its FQCN:
2026-03-15 12:26:50 +03:00
```php
2026-06-26 15:56:15 +03:00
use XcVm\Domain\Billing\InvoiceService ;
2026-03-15 12:26:50 +03:00
```
2026-06-26 15:56:15 +03:00
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.
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
## Naming rules
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
| 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)` | — |
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
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.
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
## Procedural and third-party files
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
Some files are intentionally **not** namespaced and are loaded by explicit
`require` , not the autoloader:
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
- 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);
2026-07-05 20:17:31 +03:00
- the ioncube `XC_VM` class and bundled `Infrastructure/Tmdb/lib/*` .
2026-03-15 12:26:50 +03:00
2026-08-26 22:42:43 +03:00
Third-party libraries (e.g. `gemorroj/m3u-parser` , `chrisyue/php-m3u8` ,
`mobiledetect/mobiledetectlib` , `geoip2/geoip2` ) are ordinary Composer `require`
dependencies declared in `src/composer.json` ; they live under `src/vendor/` and
autoload through the Composer vendor autoloader — they are **not** listed in the
`psr-4` block above.
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
## Modules
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
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
2026-08-26 22:42:43 +03:00
sub-path under the module directory. See [Module Authoring ](module-authoring.md ).
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
## Dev tooling
2026-03-15 12:26:50 +03:00
2026-08-21 17:55:24 +03:00
The committed `vendor/` is production-only. PHPStan and phpcs are
2026-06-26 15:56:15 +03:00
`require-dev` packages — install them locally with:
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
```bash
make dev-tools # = cd src && composer install
```
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
They are never committed (a CI gate enforces a prod-only committed vendor). See
[Development Workflow ](../guides/dev-workflow.md ).
2026-03-15 12:26:50 +03:00
2026-06-26 15:56:15 +03:00
## 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 |