Commit Graph
26 Commits
Author SHA1 Message Date
Divarion_D fda7f41258 refactor(ministra): move Stalker portal from module into core (src/Ministra)
Ministra stops being a module — the whole Stalker portal (portal.php,
MinistraBootstrap, PortalHandler/PortalHelpers and the STB front-end) now
lives in src/Ministra/ under the XcVm\Ministra namespace, served at
/home/xc_vm/Ministra via the nginx alias.

- src/ministra/* and Modules/ministra_85a7d/{PortalHandler,PortalHelpers}
  → src/Ministra/; MinistraModule.php + module.json removed. Ministra was
  the only committed module, so src/Modules/ keeps a .gitkeep.
- portal.php resolves PortalHandler as a sibling and derives MAIN_HOME from
  its new location (glob crutch gone).
- nginx alias + AuthRepository $rAlias switched to /home/xc_vm/Ministra
  (PascalCase); ministra entry dropped from bundled_modules.php.
- Makefile: Modules/ removed from LB_DIRS — all modules are MAIN-only, so
  the ~50 MB of portal assets no longer ship to LB nodes.
- ArchitectureTest: zero committed modules is now a valid state.
- PHPStan: analyse src/Ministra, exclude the procedural portal.php entry,
  repath the ministra baseline entries.
- Docs (architecture, ministra-browser-emulation, extraction plan) updated
  to the new layout; the "extract to a separate repo" plan is cancelled.

Verified: php -l, make gates, make phpstan (No errors), full unit suite
(432 tests). On-server smoke: handshake + get_profile work end-to-end with
a registered MAC after deploy.
2026-08-11 21:41:11 +03:00
Divarion_D 9c7231c6ca refactor(core): remove unused BoundaryInterface marker
BoundaryInterface was a marker interface with no runtime consumer —
nothing read getEntryPoint()/isIsolated() and, being static-less
metadata, it enforced nothing. Its only implementor was MinistraModule.

Removes the interface, drops `implements BoundaryInterface` plus the two
orphaned methods from MinistraModule (getName/getVersion stay — they come
from BaseModule/ModuleInterface), and deletes the now-empty Core/Boundary/.

Test contract (InterfaceContractTest) loses the three BoundaryInterface
assertions; docs (en/ru architecture + modules, .github instructions) now
describe isolated subsystems like Ministra as a convention — own entry
point + bootstrap — rather than a marker interface.

Verified: php -l, make gates, make phpstan (No errors);
InterfaceContractTest + ArchitectureTest green (33 tests, 96 assertions).
2026-08-11 19:22:09 +03:00
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
Divarion-D 8e67803e4b feat(modules)!: extract watch & plex into standalone git repositories
watch and plex now live in their own repos and are fetched from GitHub releases
instead of shipping in the panel archive:
- bundled_modules.php: their standard-set entries switch source bundled -> git
  (Vateron-Media/Module_Watch @ 1.0.2, Vateron-Media/Module_Plex @ 1.0.1).
- Remove src/Modules/watch_2541a and plex_20cd9 from the panel tree.

On install/status, provisionStandardSet() downloads each module's release
(module.tar.gz + hashes.md5), verifies the md5 and the pinned hash_id, and
installs it — in registry order, so tmdb (bundled) and watch land before plex.
deleted_files.txt captures the removed paths at release time.

Note: core/domain code still hard-references these module classes (e.g.
Domain\Vod\MovieService calls WatchService::getWatchCategories), so the standard
set must be provisioned for VOD import etc. to work — a pre-existing coupling,
now also a fetch-at-install dependency.
2026-07-03 21:41:23 +03:00
Divarion-D ff435f2c01 refactor(modules)!: move bundled modules to {name}_{hash5} directories
Rename watch->watch_2541a, plex->plex_20cd9, tmdb->tmdb_f4e6e,
ministra->ministra_85a7d, where the suffix is the first 5 chars of each
module's hash_id. The logical name (module.json "name", never containing "_")
stays canonical, so config keys, namespaces and dependencies are unchanged —
only the on-disk directory carries the hash. This lets same-named modules
coexist on disk. deleted_files.txt captures the old paths at release time
(make generate_deleted_files, --no-renames).
2026-07-03 20:11:07 +03:00
Divarion-D 58656fa396 refactor(modules): master/drop/migrations schema layout + harden install
Mirror core's DB layout at the module level: one master database.sql (full
current schema), one database_drop.sql (teardown), and forward-only
migrations/<semver>.sql deltas. ModuleMigrator gains install()/uninstall();
up() reads migrations/*.sql; down() removed.

- watch: database.sql (delete_missing folded into watch_folders),
  database_drop.sql, migrations/1.0.1.sql; drop 1.0.0.up/down.sql;
  getVersion() -> 1.0.1 (matches module.json).
- Move core migration 004_add_watch_delete_missing.sql into the watch module.
- installModule no longer wraps the DDL migration in a transaction: MySQL/MariaDB
  implicitly commit DDL, so the wrapping rollback() only masked the real SQL error.
- syncBundledModules retries a previously-failed install (skips only admin-disabled)
  so `console.php status` self-heals.
2026-07-02 21:28:33 +03:00
Divarion-D 5df0f1f826 feat(modules): own DB schema via migrations + uninstall + event decoupling
Make the bundled watch/plex modules own their database schema and lifecycle
  instead of being hard-wired into the base schema, and decouple core code from
  module-owned tables.

  File-based module migrations:
  - ModuleMigrator runs Modules/<name>/migrations/<semver>.up.sql / .down.sql,
    ordered by semver; installed_version (config/modules.php) is the watermark.
  - Wired into ModuleManager: install → up(null→target), update → up(from→target),
    uninstall → down(installed). Install is transactional (a failing migration
    rolls back and marks the module Failed).
  - watch ships 1.0.0.up/down.sql (creates/drops watch_categories, watch_folders,
    watch_logs, watch_refresh + seeds the default TMDb genre categories with
    INSERT IGNORE). Removed those tables from bin/install/database.sql — the
    module is now the single source of that DDL.

  Module lifecycle:
  - syncBundledModules() auto-installs never-installed bundled modules in
    dependency order; called from StatusCommand after MigrationRunner::run so a
    fresh install/update provisions their tables. Idempotent.
  - uninstallModule() refuses to remove a module that installed dependents still
    require.
  - plex declares "dependencies": ["watch"], owns no tables, and on uninstall
    deletes only its own rows (watch_folders type='plex', categories 3/4).

  Decouple core from watch tables via events (core would otherwise fatal once the
  tables are droppable):
  - New StreamsDeletedEvent / BouquetDeletedEvent, dispatched from
    StreamRepository::deleteStream/deleteStreams, ToolsCommand::deleteStreams and
    BouquetService::deleteById in place of direct DELETE/UPDATE on watch tables.
  - WatchModule subscribes via #[ListensTo] and delegates to WatchService, which
    now holds the watch_refresh/watch_logs/watch_folders cleanup. Fixes a latent
    bug where bouquet deletion wiped fb_bouquets (column was read but not SELECTed).
  - Moved TMDbService::updateCategories() (writes watch_categories) into
    WatchService; it now builds the client via the PSR-4
    TmdbApiService::createClient() instead of a manual require of the legacy lib
    (createClient is now self-contained).
2026-06-28 21:51:36 +03:00
Divarion-D a9cf7a5266 refactor(database): centralize domain DB access in DatabaseAware trait
Replace the fragile per-class setDb()/db() pattern (which threw when a
  bootstrap path forgot to wire $db) with a shared trait that lazily resolves
  from DatabaseFactory. Fixes the streaming UserRepository fatal and the same
  latent issue in module crons. 42 classes converted.
2026-06-28 13:48:35 +03:00
Divarion-D 666da2b059 fix: remove backslashes from TMDB references in comments and descriptions 2026-06-26 20:17:23 +03:00
Divarion-D c2343885dc chore(phpstan): exclude vendored libs + fix safe return.void/deadCode errors
- Exclude bundled third-party code from analysis (Modules/tmdb/lib/*,
    Core/Util/MobileDetect.php) — like the vendored Composer packages, these
    are not our code (php-tmdb client, Mobile-Detect). Removes 67 baselined
    errors incl. the only class.notFound and phpDoc.parseError (both vendored).
  - return.void (10): drop the discarded return value (return true/false/null →
    return;) in void methods — NetworkUtils, LineService, StreamProcess, TMDbService.
  - deadCode.unreachable (2): remove the dead `return;` after AdminHelpers::goHome()
    (a never-returning call) in Plex/WatchController.
  - Regenerate phpstan-baseline.neon: 440 -> 361 errors (291 -> 241 blocks).

  Verified: make phpstan green, PHPUnit 298/298.
2026-06-26 19:26:18 +03:00
Divarion-D 70024ad666 fix(modules): drop stray backslash in tmdb/lib/Release.php require paths
The PSR-4 migration's leading-backslash qualification leaked into string
  require paths, turning 'Modules/tmdb/lib/Release.php' into
  'Modules/tmdb/lib/\Release.php' → "Failed to open stream: No such file or
  directory" at runtime (TmdbCronJob, TmdbCron, watch/WatchItemCommand). Remove
  the stray backslash in all three. PHPStan can't catch this (constant + string
  literal concatenation in require), so it surfaced only at runtime.
2026-06-26 19:15:23 +03:00
Divarion-D ef1835d8fb fix: resolve string class references missed by the PSR-4 migration
PHP string class names are always fully-qualified (they ignore the current
namespace and use-imports), so several short-name string references silently
broke when their classes were namespaced — failing only at runtime.

- StreamUtils::sortArguments(): the uasort() callback array('StreamUtils',
  'customOrder') referenced the now-namespaced class by its old global short
  name -> "class StreamUtils not found". Use array(self::class, 'customOrder').
- Router::checkPermission(): `class_exists('Authorization')` was always false
  (Authorization is now XcVm\Core\Auth\Authorization), so the ['type','key']
  permission branch never called check() and fell through to `return true` —
  granting access unconditionally. Call Authorization::check() directly.
- TmdbController::hasMediaPermission(): `class_exists('Authorization')` always
  false -> method always returned false (media permissions always denied). Drop
  the dead guard; the call already imports the FQCN.
- AdminLogoutController: `class_exists('SessionManager')` always false -> the
  admin session was never cleared on logout. Call SessionManager::clearContext()
  directly.
- BruteforceGuard::getDB(): `class_exists('DatabaseFactory', false)` always
  false -> qualify to \XcVm\Infrastructure\Database\DatabaseFactory::class.

Verified: php -l clean; StreamUtils callback is callable; make gates pass;
PHPUnit green (incl. StreamUtilsTest).
2026-06-26 17:43:31 +03:00
Divarion-D 9eec63937e style: import/namespace hygiene across src (PHP-CS-Fixer)
Apply 'make cs-fix' — 493 files. Mechanical, import-block only:
- sort use statements alphabetically (class/function/const grouped);
- drop imports left unused by the PSR-4 migration (e.g. classes referenced by
  leading-backslash FQCN whose redundant 'use' the automated insertion had added);
- one blank line after namespace and after the import block; collapse stray
  blank lines around use.

No logic changes. Verified: php -l clean; PHPStan no errors; PHPUnit 295/295; and a
temporary PHPStan pass over src/Public/Controllers confirms no still-referenced
import was removed (0 unresolved classes). 'use' after inline HTML in view
templates is valid and aliases correctly (verified) — those imports are sorted too.
2026-06-25 20:24:36 +03:00
Divarion-D 43e2f275b1 chore(psr4): phase 3 — namespace module sub-classes
Namespace the modules' own classes into XcVm\Module\<Pascal> (Plex, Watch, Tmdb,
Ministra) — PlexController/PlexService/PlexCron/..., WatchController/WatchService/...,
TmdbController/TmdbCron/TmdbApiService/..., PortalHandler/PortalHelpers (~23 classes).
They now resolve through the Phase-2 ModuleLoader PSR-4 resolver.

- The bundled third-party tmdb/lib/* (TMDB client, Entities, roles, config) stays
  GLOBAL, like the other vendored libs; namespaced Tmdb classes reference it via
  \TMDB etc.
- Rewrite the *Module classes' (and any sibling) 'use ShortName;' imports → FQCN;
  add use to referrers; convert leading-backslash refs. Sub-classes keep the
  Core/Domain/Cli use imports added during those layers.
- Qualify built-ins (\DateTime, \Exception, \PDO, ...) and the global lib classes.
- phpstan-baseline.neon regenerated.

Verified: php -l clean; PHPStan no errors; PHPUnit 295/295; all 4 modules load and
their sub-classes (e.g. XcVm\Module\Plex\PlexService) resolve via the module
PSR-4 autoloader; re-sweep clean.
2026-06-25 19:12:48 +03:00
Divarion-D 3377236d77 chore(psr4): phase 3 (Cli) — namespace Cli + switch console.php to FQCN discovery
Namespace all 55 Cli classes into XcVm\Cli (CommandInterface, CommandRegistry,
CronTrait, DaemonTrait), XcVm\Cli\Commands (28) and XcVm\Cli\CronJobs (23);
migration_logic.php stays procedural/global.

- console.php: switch command discovery from basename==classname + manual require
  to FQCN resolution — each scan dir maps to its PSR-4 namespace, classes load via
  Composer, implementsInterface(CommandInterface::class). Drop the manual
  CommandInterface/CommandRegistry requires. This is the atomic switch the plan
  required to land with the Cli namespacing.
- Commands/CronJobs get 'use XcVm\Cli\CommandInterface;' (implements) and the
  trait imports 'use XcVm\Cli\CronTrait;'/'DaemonTrait;'; rewrite the modules'
  'use CommandRegistry;' → FQCN; qualify built-ins/global (\ReflectionClass,
  \PDO, \Exception, \RuntimeException, \XC_Autoloader, \XC_VM).
- Fixtures: 'use CommandRegistry;' → FQCN; InterfaceContractTest registerCommands
  param-type → FQCN. phpstan-baseline.neon regenerated.

Verified: php -l clean; PHPStan no errors; PHPUnit 295/295; FQCN discovery resolves
51 classes (49 implement CommandInterface); no mangled FQCNs; re-sweep clean.
2026-06-25 19:03:39 +03:00
Divarion-D 62d2d1d942 chore(psr4): phase 3 — namespace Streaming layer
Namespace all 17 Streaming classes into XcVm\Streaming\<Subdir> (Auth, Balancer,
Codec, Delivery, Health, Lifecycle, Protection + root AsyncFileOperations,
StreamingBootstrap, TS).

- Add namespace to every class; add use to referrers across src/ and tests/;
  convert leading-backslash refs from earlier commits (\FfmpegPaths,
  \FFprobeRunner, \ProcessChecker, \StreamingBootstrap) to FQCNs.
- Qualify built-ins/ioncube inside Streaming (\DateTime, \DateTimeZone,
  \Exception, \XC_VM).
- FfmpegPathsTest: new ReflectionClass('FfmpegPaths') string → ::class FQCN.
- phpstan-baseline.neon regenerated (291).

Verified: php -l clean; PHPStan no errors; PHPUnit 295/295; no mangled FQCNs;
leading-backslash re-sweep clean.
2026-06-25 18:55:35 +03:00
Divarion-D aaa8e2a23a chore(psr4): phase 3 — namespace Infrastructure layer
Namespace the Infrastructure classes and PascalCase its subdirectories for PSR-4
consistency with Core/Domain:
- git mv bootstrap/→Bootstrap/, cache/→Cache/, database/→Database/, redis/→Redis/;
  update the require paths to the procedural Bootstrap/*.php glue files.
- Namespace the 7 classes: StreamingRequestBootstrap, WebApiBootstrap →
  XcVm\Infrastructure\Bootstrap; CacheReader → \Cache; DatabaseFactory →
  \Database; RedisManager → \Redis; ResellerApiDispatcher, ResellerTableRenderer
  → XcVm\Infrastructure. The procedural Bootstrap glue files stay global.
- Qualify built-ins/ioncube (\Redis, \RedisException, \DateTime, \Exception,
  \XC_VM) and still-global \StreamingBootstrap; add use to referrers; convert
  the leading-backslash \CacheReader/\DatabaseFactory/\RedisManager refs from
  earlier commits to FQCNs.
- deleted_files.txt: old lowercase subdir paths for client cleanup.
- phpstan-baseline.neon regenerated.

Verified: php -l clean; PHPStan no errors; PHPUnit 295/295; re-sweep clean.
2026-06-25 18:45:19 +03:00
Divarion-D 42d9ca8ea0 chore(psr4): phase 3 — namespace Domain layer
Namespace all of Domain into XcVm\Domain\<Subdir> (33 classes across Bouquet,
Device, Epg, Line, Security, Server, Stream, User, Vod).

- Add namespace to every Domain class; add use to referrers across src/ and
  tests/; convert the leading-backslash refs qualified in earlier Core commits
  (\UserRepository, \ServerRepository, \BouquetService, \CategoryService,
  \ConnectionTracker, \BlocklistService, ...) to their FQCNs.
- Qualify still-global / built-in deps inside Domain with leading backslash
  (\RedisManager, \TMDB, \WatchService, \FFprobeRunner, \ProcessChecker,
  \Exception, \DateTime, \PDO, ...) — Infrastructure/Streaming/module classes
  migrate later.
- BruteforceGuard: string guards class_exists('ServerRepository'/'BlocklistService')
  → ::class FQCN; drop the now-always-true method_exists check.
- phpstan-baseline.neon regenerated (292→291).

Verified: php -l clean; PHPStan no errors; PHPUnit 295/295; leading-backslash
re-sweep across Domain classes is clean.
2026-06-25 18:39:28 +03:00
Divarion-D da291e20f8 chore(psr4): phase 3 (Core) — namespace remaining Core subdirs
Namespace the rest of Core, completing the Core layer:
- Backup, Boundary, Cache (CacheInterface/FileCache/RedisCache), Diagnostics,
  GeoIP (GeoIPService/MaxMindUpdater), Init (LegacyInitializer), Localization
  (Translator), Process (Thread/Multithread/ProcessManager), Updates
  (GitHubReleases), Util (NetworkUtils, AdminHelpers, Encryption, GeoIP,
  ImageUtils/ImageResizeService, Mobile_Detect, StreamUtils, SystemInfo,
  TimeUtils), Validation (InputValidator) → XcVm\Core\<Subdir>.
- Rename GithubReleases.php → GitHubReleases.php so the file matches its class
  name (PSR-4 is case-sensitive); fix the WebApiBootstrap require accordingly.
- Qualify built-ins (\Exception, \DateTime, \DateTimeZone, \Redis,
  \RuntimeException, \BadMethodCallException, ...) and still-global deps
  (\ServerRepository, \CacheReader, \DatabaseFactory, \BouquetService,
  \CategoryService, \FfmpegPaths, \StreamSorter, \XC_VM). LegacyInitializer's
  Domain calls stay \-qualified (the known Core→Domain exception).
- Add use to referrers; fix leading-backslash refs from earlier commits; fix
  string class refs class_exists('Translator'/'NetworkUtils'/...) → ::class.
  Remove the duplicate global 'use BoundaryInterface;' in MinistraModule.
- phpstan-baseline.neon regenerated.

Core is now fully namespaced (procedural constant/error/RequestGuard files stay
global by design; M3uParser/PhpM3u8 remain vendored). Verified: php -l clean;
PHPStan no errors; PHPUnit 295/295; leading-backslash re-sweep clean.
2026-06-24 22:35:33 +03:00
Divarion-D e8b7ab8b2e chore(psr4): phase 3 (Core) — namespace Core/Container
Move Core/Container into XcVm\Core\Container (ServiceContainer) and
Core/Container/Psr into XcVm\Core\Container\Psr (ContainerInterface,
ContainerExceptionInterface, NotFoundExceptionInterface, NotFoundException).

- Namespace ServiceContainer + the 4 PSR-11 interfaces/classes.
- ServiceContainer: import the PSR siblings (use ...\Psr\ContainerInterface,
  NotFoundException); migrated deps (DatabaseHandler/ModuleInterface/Request/
  SettingsManager) keep their use; still-global exceptions/decorators qualified
  with leading backslash (\ContainerException, \XcVmException, \Exception, ...).
- Rewrite 'use ServiceContainer;' → FQCN (incl. module fixtures); add use to
  referrers across src/ and tests/; fix leading-backslash \ServiceContainer refs.
- Core/Exception/Container/ContainerException: import the moved PSR
  ContainerExceptionInterface.
- Router: class_exists('ServiceContainer') → ::class. InterfaceContractTest boot
  param-type → FQCN. Repaired use-inserter mis-placement in the two namespace()
  fixture tests.
- phpstan-baseline.neon regenerated (292→292).

Verified: php -l clean; PHPStan no errors; PHPUnit 295/295; ServiceContainer
resolves and implements XcVm\Core\Container\Psr\ContainerInterface.
2026-06-24 22:13:35 +03:00
Divarion-D 881991e52b chore(psr4): phase 3 (Core hubs) — namespace Core/Module
Move Core/Module into XcVm\Core\Module (BaseModule, ModuleInterface,
MigratableInterface, ModuleLoader, ModuleManager, NavbarRegistry, NavbarItem,
CoreNavbarProvider) and Core/Module/Contract into XcVm\Core\Module\Contract
(the 6 provider interfaces). Completes the Core hub layer.

- Namespace 14 files (8 root + 6 Contract). Root files import the contracts via
  'use XcVm\Core\Module\Contract\...'; NavbarProviderInterface imports the
  root NavbarRegistry. Qualify still-global deps with leading backslash
  (\ServiceContainer, \CommandRegistry, \ModuleState, \ServerEnvironment,
  \EventDispatcher, \ListensTo, the Module exceptions, \ZipArchive,
  \InvalidArgumentException, \RuntimeException) — Container/Events/Enum/Exception
  migrate later. Migrated deps (Router, StreamPipeline, ...) keep their use.
- Rewrite the modules' 'use BaseModule/NavbarRegistry/NavbarItem;' → FQCN; add
  'use XcVm\Core\Module\...;' to other referrers across src/ and tests/.
- Fix pre-existing leading-backslash refs to the FQCN.
- Tests: qualify the module fixtures' generated 'use ModuleInterface;' /
  'use BaseModule;' / 'use NavbarRegistry;' and the registerRoutes type hint to
  FQCN; add real top-level use imports to the fixture-builder tests; update
  InterfaceContractTest registerNavbar param-type to the FQCN. Repaired
  use-inserter mis-placements in the two tests that declare a namespace() method.
- Public/index.php: class_exists('ModuleLoader') → class_exists(ModuleLoader::class).
- phpstan-baseline.neon regenerated (292→292; no new/unknown-class errors).

Verified: php -l clean; PHPStan no errors; PHPUnit 295/295; the 4 real modules
load and resolve as XcVm\Core\Module\ModuleInterface; leading-backslash
re-sweep across all migrated classes is clean.
2026-06-24 22:05:10 +03:00
Divarion-D 160adc1439 chore(psr4): phase 3 (Core hubs) — namespace Core/Auth
Move Core/Auth into XcVm\Core\Auth (Authenticator, Authorization, AuthRepository,
AuthService, BruteforceGuard, PageAuthorization, SessionManager).

- Namespace the 7 classes; same-namespace siblings unqualified; migrated deps
  (Logger, ApiClient, QueryHelper, SettingsManager, RequestManager) keep their
  existing use imports. Qualify still-global deps with leading backslash
  (\UserRepository, \NetworkUtils, \ServerRepository, \BlocklistService,
  \DatabaseFactory, \RedisManager, \AdminHelpers, \Encryption, \CodeService,
  \CodeRepository) — these migrate later with Domain/Util.
- Add 'use XcVm\Core\Auth\...;' to referencing files (Authorization 69,
  AuthRepository 24, BruteforceGuard 12, SessionManager/PageAuthorization 9,
  Authenticator 5, AuthService 3) across src/ and tests/.
- Fix pre-existing leading-backslash refs (e.g. Router's \Authorization, now
  \XcVm\Core\Auth\Authorization). Preserve class_exists('NetworkUtils',false)
  defensive string guards as-is (still-global classes).
- phpstan-baseline.neon regenerated (292→292; AuthRepository::getHMACById message
  re-anchored with namespace, no new/unknown-class errors).

Verified: php -l clean; PHPStan no errors; PHPUnit 295/295; Auth classes resolve
via Composer; leading-backslash re-sweep clean.
2026-06-24 21:42:30 +03:00
Divarion-D 14202afd64 chore(psr4): phase 3 (Core hubs) — namespace Core/Http
Move Core/Http into XcVm\Core\Http (ApiClient, CurlClient, RequestManager,
Request, Response, Router) and Core/Http/Pipeline into XcVm\Core\Http\Pipeline
(StreamContext, StreamMiddlewareInterface, StreamPipeline). RequestGuard.php is
procedural (global functions) and stays global.

- Namespace the 9 classes; qualify still-global deps with leading backslash
  (\ServerRepository in ApiClient; \Authorization, \ServiceContainer,
  \AdminHelpers in Router) and built-in \Throwable; same-namespace siblings
  unqualified.
- Add 'use XcVm\Core\Http\...;' to referencing files — RequestManager 128,
  Router 19, ApiClient 15, Request 11, Response 6, CurlClient 5, plus Pipeline —
  across src/ and tests/.
- Rewrite the modules' 'use Router;' → 'use XcVm\Core\Http\Router;' (plex,
  watch, tmdb).
- Fix pre-existing leading-backslash global refs (\Router etc.) to the FQCN.
- Tests: fully-qualify the Router type hint in generated module fixtures
  (registerRoutes(\XcVm\Core\Http\Router ...)) and update the
  InterfaceContractTest param-type expectation to the FQCN. Also repaired two
  fixtures where the use-inserter mis-placed a 'use' (files declare a method
  literally named namespace(), which the tokenizer reports as T_NAMESPACE).
- phpstan-baseline.neon regenerated (292→292; class names in frozen messages
  gained the namespace, no new/unknown-class errors).

Verified: php -l clean; PHPStan no errors; PHPUnit 295/295; Http classes resolve
via Composer; leading-backslash re-sweep across migrated classes is clean.
2026-06-24 21:32:10 +03:00
Divarion-D 42db4be509 chore(psr4): phase 3 (Core hubs) — namespace Core/Database
Move Core/Database into XcVm\Core\Database (DatabaseHandler, Database,
MigrationRunner, QueryHelper). First of the Core hub sub-layers.

- Namespace the 4 classes; DatabaseHandler extends Database (same namespace);
  built-ins/ioncube qualified (\PDO, \PDOException, \Exception, \Throwable,
  \XC_VM); Database keeps its 'use XcVm\Core\Logging\FileLogger;'.
- Add 'use XcVm\Core\Database\...;' to referencing files (DatabaseHandler 51,
  Database 64, QueryHelper 29, MigrationRunner 3) + 2 test files (PHPStan does not
  analyse tests/, so PHPUnit is the gate there).
- Rewrite pre-existing leading-backslash global refs (\DatabaseHandler etc., e.g.
  in @param docblocks of ResellerApiDispatcher/ResellerTableRenderer) to the full
  FQCN \XcVm\Core\Database\... — a 'use' import does not cover a leading-\
  reference. Done with a lookbehind so FQCN continuations and use-lines are intact.
- phpstan-baseline.neon regenerated (292→292; pre-existing Database/migration_logic
  findings re-anchored after class names in messages gained the namespace).

Verified: php -l clean; PHPStan no errors; PHPUnit 295/295.
2026-06-24 21:15:06 +03:00
Divarion-D 8ac1908554 chore(psr4): phase 3 (Core leaf) — namespace SettingsManager + SettingsRepository
Move the last two Core/Config classes into XcVm\Core\Config. SettingsManager
has the largest fan-out of the whole migration (referenced by ~224 files).

- Namespace SettingsManager (self-contained singleton, no class deps) and
  SettingsRepository (\FileCache:: qualified).
- Add 'use XcVm\Core\Config\SettingsManager;' to 224 referencing files and
  'use ...\SettingsRepository;' to 12 — call sites (SettingsManager::get(), etc.)
  unchanged. Done with a token-based inserter (after namespace/declare/<?php,
  idempotent, same-namespace files skipped).
- ToolsCommand::processRecaptcha: drop dead class_exists('SettingsManager') +
  method_exists guard (always autoloadable now) → call SettingsManager::clearCache()
  directly. This was the only string-literal class reference.

Core/Config is now fully namespaced (ConfigReader, DomainResolver, SettingsManager,
SettingsRepository); the procedural constant files (AppConfig/Binaries/Paths)
remain global by design.

Verified: php -l clean (226 files); PHPStan no errors (baseline unchanged — it is
line-independent so the added use-lines don't disturb it); PHPUnit 295/295; all
Config FQCNs resolve via Composer; sample Public referrers lint-clean.
2026-06-24 20:52:00 +03:00
Divarion-D 1a296d0985 chore(psr4): phase 1 — rename 7 top-level dirs to PascalCase
Atomic case-rename of the seven class-holding source directories to match the
PSR-4 namespace casing, plus every consequent path reference. No code logic
changes — pure structural rename. (config/content/resources/signals/migrations/
ministra/storage/tmp/vendor/www/bin stay lowercase.)

- git mv: core→Core, domain→Domain, infrastructure→Infrastructure,
  streaming→Streaming, modules→Modules, cli→Cli, public→Public (module subdirs
  like Modules/plex stay lowercase; loaded by ModuleLoader, not Composer).
- PHP filesystem paths updated across src/ + tests/: require/include, glob/scandir
  bases, view includes, asset/nginx-alias paths, autoload.php registerDirectories(),
  ModuleLoader/ModuleManager module roots, console.php discovery dirs.
- src/composer.json PSR-4 vendored-lib paths → Core/Parsing/...; vendor/ regenerated.
- nginx.conf: docroot + SCRIPT_FILENAME → Public/. SCRIPT_NAME and the public-facing
  /streaming/*.php compat routes are URLs, left unchanged.
- Build/CI: Makefile LB_DIRS / LB_DIRS_TO_REMOVE / LB_FILES_TO_REMOVE PascalCased
  (closes the LB-archive privileged-leak blocker); phpstan.dist.neon paths/
  scanDirectories/excludePaths; phpunit.xml.dist coverage; phpstan-baseline.neon
  regenerated (294→294 errors, no new resolution failures).
- migrations/deleted_files.txt: old lowercase trees listed for client cleanup
  (assumes case-sensitive FS — the supported Linux target).

Verified: grep-gate 0 live lowercase-dir require/include in src+tests; php -l clean;
PHPUnit 292/292; PHPStan no errors; Composer first / XC_Autoloader last in SPL stack;
global classes resolve from the renamed dirs.
2026-06-24 20:10:34 +03:00