Commit Graph
47 Commits
Author SHA1 Message Date
Divarion_D aa207c9a39 Prepare release 2026-09-23 14:22:56 +03:00
Divarion_D 0039e889e2 style(cs): fix parenthesis spacing in XC_VM_BUILD_ID define
The multi-line grouping parens in the XC_VM_BUILD_ID define (added in
c289352d) tripped Generic.WhiteSpace.ArbitraryParenthesesSpacing, breaking
the green `make cs` baseline. Reflow as a multi-line define() call with the
`?:` grouping paren kept tight. Behaviour unchanged (still 'dev' in a source
checkout). `make cs` green again.
2026-09-18 16:13:53 +03:00
Divarion_D c289352d43 feat(build): per-build watermark (RELEASE_ID / XC_VM_BUILD_ID)
`make main`/`make lb` now stamp a unique per-build id — version + short git
SHA + UTC timestamp + 8 random bytes — into RELEASE_ID at the staged deploy
root (new stamp_release_id target, run after the copy step; the file is
generated per build, never git-tracked). ConstantsInitializer exposes it at
file scope as XC_VM_BUILD_ID by reading RELEASE_ID relative to __DIR__; a
source/dev checkout has no file and reports 'dev'.

This gives every build a traceable fingerprint so a leaked or rebranded copy
can be tied back to its origin (and, once activation ships, reported in the
phone-home alongside install_id). Kept at file scope to avoid adding a branch
to ConstantsInitializer::init() (CRAP ratchet). README discloses it.

Tests: XC_VM_BUILD_ID defined and defaults to 'dev' in a source checkout.
Full suite (817) + gates + CRAP + phpcs green; Makefile targets validated.
2026-09-18 15:45:34 +03:00
Divarion_D 13d4d6b077 Prepare release 2.5.2 2026-09-16 22:04:48 +03:00
Divarion_D 323d4aa455 refactor(config): hoist frequently-edited release constants to top-of-file define()s
XC_VM_VERSION / DEV_MODE / DB_ACCESS_ENABLED / DB_ACCESS_PWD are edited on every
release (and by the release automation). Buried as array entries in appConfig()
they were awkward to find and to sed. Move them back to guarded define()s at the
top of ConstantsInitializer.php; appConfig() reads them back, and init() skips
the already-defined ones. The guard keeps a pre-definition (e.g. the PHPStan
stub) from fataling.

Update the release checklist accordingly: the sed commands target the familiar
`define('XC_VM_VERSION', '...')` form in ConstantsInitializer.php again (they were
pointing at the deleted AppConfig.php).
2026-09-16 18:51:51 +03:00
Divarion_D 0ab6a8f6a9 refactor(bootstrap): collapse prelude shims into ConstantsInitializer::init()
The Paths/AppConfig/Binaries shims were byte-identical (each just called
ConstantsInitializer::init()), and ErrorCodes.php only bridged a $rErrorCodes
global that nothing reads any more. Every boot path required all five by name.

Replace those requires with a single ConstantsInitializer::init() at each of the
four call sites (ConstantsStage, WebApiBootstrap, StreamingRequestBootstrap, the
progress endpoint) and delete the four dead shim files. The generateError()/
generate404() functions are already provided globally via composer autoload.files,
so their require was redundant too — ErrorHandler.php stays (it is the
autoload.files target) but is no longer required by name.

Verified: init() defines the full constant set and the error functions autoload;
762 tests, PHPStan, phpcs and make gates green; all four boot smokes (cli/admin/
webapi/streaming) still PASS against a real MariaDB in the dev container.
2026-09-16 18:24:57 +03:00
Divarion_D 2489824f16 feat(security): per-install OPENSSL_EXTRA secret
OPENSSL_EXTRA was a hardcoded literal shared by every install, i.e. public in the
source tree. Give fresh installs their own secret while leaving existing installs
untouched.

- Installer: after writing config.ini, generate a 40-char secret into
  config/openssl_extra (chmod 600), but ONLY when the file is absent. It is key
  material for Encryption (hmac_keys rows, cached image filenames, proxy URL keys,
  stream tokens), so it must be generated once and never rotated — re-running the
  installer must not overwrite it or all existing encrypted data would orphan.
- ConstantsInitializer now sources OPENSSL_EXTRA from that file directly rather
  than through the config extension. A standalone file is deliberate: it is
  independent of the config.ini -> config.enc migration and never rewritten, so
  the value is read back identically for the life of the install regardless of
  how the proprietary extension surfaces config keys. Missing/empty file falls
  back to the historical literal, so existing installs keep decrypting unchanged.

Verified: no file -> literal, file -> its value, empty file -> literal; installer
generation is idempotent (40 chars, 0600, not overwritten on re-run).
2026-09-16 17:43:53 +03:00
Divarion_D 320edb819c refactor(bootstrap): convert procedural prelude to BC shims
Point the five procedural prelude files at the new source-of-truth classes,
keeping them on disk as thin shims because the boot paths require_once them by
name and legacy code reads their globals/constants directly.

- Paths.php / AppConfig.php / Binaries.php -> ConstantsInitializer::init()
- ErrorCodes.php -> bridges ErrorResponder::codes() into $GLOBALS['rErrorCodes']
- ErrorHandler.php -> generateError()/generate404() shims (function_exists
  guarded) delegating to ErrorResponder; production still exit()s, so the ~139
  call sites are untouched.

Register ErrorHandler.php in composer autoload.files so the functions exist even
on paths that do not require the prelude; regenerate the production-only vendor
autoload accordingly.

The path/app-config constants are no longer literal define()s PHPStan can scan,
so add the four it previously learned from them but that were missing from the
stub (GIT_REPO_FANOUT, FANOUT_{RUN_PATH,CTL_SOCK,HTTP_SOCK}).

Debug/404 output stays byte-identical to the legacy functions (golden tests);
full suite, PHPStan, phpcs and make gates all green.
2026-09-16 16:34:16 +03:00
Divarion_D 6a39902fbf refactor(bootstrap): add ConstantsInitializer + ErrorResponder PSR-4 classes
Introduce the source-of-truth classes for the bootstrap testability refactor.
Purely additive — nothing is wired to them yet.

- ConstantsInitializer: pure value maps (paths/appConfig/binaries/statuses)
  plus the single define() site (init/initStatus). The maps evaluate with
  different MAIN_HOME/BIN_PATH in one process, which the one-shot define()
  constants they feed cannot — this is what makes them testable.
- ErrorResponder: the generateError()/generate404() logic extracted into pure
  codes()/renderDebug()/render404()/respond*() plus a single side-effecting
  emit(). A test-mode toggle throws ErrorResponseException instead of exit().
- ErrorResponseException: value carrier for a resolved error response.

OPENSSL_EXTRA is now sourced per-install via ConfigReader with a mandatory
fallback to the historical literal, so existing installs (whose persisted data
derives from it) keep decrypting; generation-at-install is left to the installer.

Verified byte-for-byte against the legacy prelude before wiring: 53/53 constants,
debug/404 HTML frozen as sha256 goldens, 65 error codes.
2026-09-16 16:34:02 +03:00
Divarion_D dc7fa83b0a refactor: apply Rector safe sets + fix the transform's fallout (reviewed)
Ran `make rector-fix` (deadCode + codeQuality prepared sets) over the PSR-4
class trees (Core/Domain/Cli/Infrastructure). Net -600 lines: dead-code removal
and the empty-if/else collapse (the feedback_simplify_empty_else pattern this
adoption targeted). 99 files mechanically transformed.

The output was reviewed against the full test suite + PHPStan + a targeted scan;
this commit is the CORRECTED pass (no broken state in history):

- FIX (Rector bug): its boolean inversion dropped the parens around an
  assignment-in-condition — `if (($rKey = array_search(...)) === false)` became
  `if ($rKey = array_search(...) !== false)`, assigning the bool to $rKey and
  unsetting the wrong array offset. Restored parens in 7 sites (BouquetService,
  ChannelService, CategoryService). PHPStan caught only 1 of the 7; the rest
  were silent. Added BouquetServiceTest as a regression (proven to fail on the
  broken form).
- FIX (pre-existing, same class): GroupService::removeGroupFromUsers had the
  identical dropped-parens bug already in the tree — corrected here too.
- FIX: ServersCronJob::pingServer returns floor() (float) under an `: int`
  return type — added an (int) cast.
- ACCEPT: LocallyCalledStaticMethodToNonStaticRector converted 25 locally-called
  private static helpers to instance methods (behaviour-preserving; call sites
  rewritten). Updated MonitorCommandTest's reflection helper to invoke on a
  constructor-less instance.

Verified: PHPStan level 5 clean (0), suite 691 tests / 0 errors.

NOTE: do not re-run `make rector-fix` on this tree without first skipping the
inversion rule — the dropped-parens bug is deterministic and would return.
2026-09-13 17:27:42 +03:00
Divarion_D 758a9cab0b style: apply K&R + tab formatting across the codebase (make cs-fix)
Mechanical, behaviour-preserving reformat produced by 'make cs-fix' under
the new build/phpcs.xml.dist ruleset: K&R braces, tab indentation, and the
other whitespace normalisations. No logic changes.
2026-09-13 14:14:23 +03:00
Divarion_D 71ccdac452 refactor: convert inline \XcVm\ FQNs to PSR-4 use imports
Replace fully-qualified \XcVm\... class references (in code and in
docblocks/@see/@param/@return/@throws) with short names backed by
top-of-file use imports, project-wide. Same-namespace references drop
the prefix with no import; view templates gain the top-level imports the
check-procedural-use gate expects. Purely mechanical, no behavior change.
2026-09-13 12:13:01 +03:00
Divarion_D dbdab9b050 Prepare release 2.5.1 2026-09-11 20:12:09 +03:00
Divarion_D 356c181652 Prepare release 2.5.0 2026-09-10 17:31:22 +03:00
Divarion_D defeae3a3c Prepare release 2.4.3 2026-09-01 16:07:06 +03:00
Divarion_D 0e2d64cdfd feat(ffmpeg): dynamic build discovery + capabilities, wire ffmpeg_gpu, drop 4.0 hardcoding
Discover the bundled FFmpeg builds from bin/ffmpeg_bin/<version>/ instead of a
hardcoded version list, and expose what each build can do.

- FfmpegBinaries (new): scans real version folders (skips *.backup.*), probes each
  binary's hardware encoders (nvenc/qsv/vaapi/…), flags GPU capability, caches the
  result to tmp/ffmpeg_capabilities.json keyed by a folder-mtime fingerprint. UI-only.
- FfmpegPaths::resolve() rewritten: builds paths dynamically from the version string
  (is_file check, no shell — stays on the bootstrap hot path) and takes a separate GPU
  version; the switch and the per-version constants are gone. Unknown CPU version falls
  back to the legacy 4.0 build; an empty/invalid GPU version falls back to the CPU build.
- Wire the previously-dead ffmpeg_gpu column: LegacyInitializer passes it to resolve();
  settings.php lists versions from the scanner and adds an ffmpeg_gpu <select> limited to
  GPU-capable builds ("Same as CPU" = empty). No migration — the column already exists.
- Info tab: a per-build FFmpeg table (version, build banner, GPU badge, hw encoders).
- De-hardcode: drop the unused FFMPEG_BIN_71/80 + FFPROBE_BIN_71/80 constants; keep only
  the 4.0 anchor (DTS override in StreamProcess + FfmpegPaths fallback). Llod/Loopback/
  Record now use FfmpegPaths::cpu()/probe() (config-selected version) with the 4.0
  constant as fallback, matching Scanner/Created/Thumbnail.
2026-08-30 14:05:34 +03:00
Divarion_D 25e2adfcac Prepare release 2.4.2 2026-08-27 18:15:50 +03:00
Divarion_D fe287163f6 fix(ministra): treat empty Allowed STB Types as allow-all
An unset Allowed STB Types multiselect was stored as [""] (an array holding one blank string), so the get_profile gate's empty() check in Ministra/portal.php was false and every real STB was rejected. Normalise the list in SettingsRepository::getAll() — the single JSON decode point — by trimming/lowercasing entries and dropping blanks, so an empty selection collapses to a truly empty array. Empty now means all STB types are accepted, as intended.
2026-08-25 17:38:40 +03:00
Divarion_D cb44da3c73 feat(config): add typed SettingsManager accessors
Add has()/getBool()/getInt()/getString()/getArray() to SettingsManager so
call sites can read individual settings by key instead of materialising the
whole array via getAll()['key'] and indexing into it.

getBool() uses a plain (bool) cast to faithfully preserve the existing PHP
truthiness of `if (getAll()['key'])` ('0'/'' -> false), and getArray() relies
on SettingsRepository already JSON-decoding array fields. All accessors take a
default, avoiding PHP 8 "Undefined array key" warnings on missing keys.
2026-08-24 21:52:09 +03:00
Divarion_D 474e466d51 Prepare release 2.4.1 2026-08-23 19:21:29 +03:00
Divarion_D 6c2933cd9b chore(release): prepare 2.4.0
Bump XC_VM_VERSION and changelog.json to 2.4.0; regenerate src/migrations/deleted_files.txt from git diff 2.3.9..HEAD.
2026-08-23 13:49:58 +03:00
Divarion_D 8661460868 style: remove unused imports + normalize use/namespace spacing (phpcs)
Apply the new phpcs + Slevomat ruleset across src/ (phpcbf): 85 files. The bulk
are unused `use` imports that PHP-CS-Fixer's no_unused_imports missed (class
name only present in a PHPDoc description), plus blank-line normalization around
the use/namespace blocks. Verified safe: `make phpstan` stays green (0 errors) —
no import used in code or a real docblock type was removed.
2026-08-21 17:55:25 +03:00
Divarion_D 881a2e795a feat(streaming): panel installer/updater for the xc_fanout daemon
The daemon moves to its own repo (XC_VM_Fanout, GIT_REPO_FANOUT) that ships
per-arch static binaries as GitHub Release assets. Since we're fully
migrating onto the daemon, the panel needs to fetch and update it.

- FanoutBinaryCommand (`console.php fanout_binary`): reads the installed
  version straight from the binary (`xc_fanout -version`), compares to the
  latest release, and when they differ downloads the arch-matched asset,
  verifies its SHA-256 against the release SHA256SUMS, installs it
  atomically and restarts the daemon (the service keepalive respawns it).
  `force` reinstalls the current version. Modelled on BinariesCommand.
- GIT_REPO_FANOUT constant.
- docs/xc_fanout.md: separate-repo + install/update section.

The daemon repo itself (source + release.sh + a v* release GitHub Action,
binaries gitignored) is prepared under XC_VM_Fanout/ for the user to push.
2026-08-16 20:22:34 +03:00
Divarion_D f8f8012dcb refactor(streaming): keep xc_fanout sockets in bin tree, not /run
Move the daemon's control/client sockets from /run/xc_fanout/ to
bin/xc_fanout/sockets/{control,http}.sock, next to the daemon binary and
mirroring the existing php-fpm sockets layout (bin/php/sockets/) that nginx
already reaches over unix:. Keeps runtime sockets in the app tree the panel
owns rather than a separate FHS runtime dir; a unix socket stores no stream
bytes (pure IPC), so its location is orthogonal to the tmpfs-free byte-path
goal.

- Paths: FANOUT_RUN_PATH now BIN_PATH.'xc_fanout/sockets/' (was /run/...).
- nginx: proxy_pass unix:/home/xc_vm/bin/xc_fanout/sockets/http.sock.
- ADR 0002: §3 placement + §2.4/S3/S4/diagram paths updated; corrected the
  imprecise "tmpfs-free runtime dir" wording.

Daemon must launch with -sock .../sockets/http.sock -ctl .../sockets/control.sock
(wired into supervision in S5).

Refs ADR 0002.
2026-08-16 16:04:06 +03:00
Divarion_D 6d3c84a473 feat(streaming): P2/S3 — proxy-mode live TS via xc_fanout, flag-gated
Route proxy-mode live TS through the xc_fanout daemon behind the
`live_fanout` settings flag, taking PHP out of the byte path. When the
flag is on, live.php runs its existing auth unchanged, then instead of the
socket_read→echo relay it registers the source with the daemon over the
control unix socket and hands the viewer to nginx via
`X-Accel-Redirect: /xc_fanout/live/<id>` — the FPM worker is freed
immediately, same pattern as P1 HLS delivery. Flag off (the default) keeps
the legacy relay; a failed registration falls through to it in-request, so
a down daemon degrades gracefully.

- FanoutClient: control-socket client (PUT/DELETE /streams/<id> over
  FANOUT_CTL_SOCK via cURL unix socket) + buildSource(), a pure mirror of
  ProxyCommand's stream_source/user_agent/proxy/cookie extraction whose
  JSON matches the daemon's streamConfig.
- Paths: FANOUT_RUN_PATH / FANOUT_CTL_SOCK / FANOUT_HTTP_SOCK under /run.
- nginx: internal `location ^~ /xc_fanout/live/` → proxy_pass unix daemon
  client socket (buffering off, long read timeout).
- FanoutClientTest: 8 cases covering source-config parity and the
  empty-urls short-circuit.

Refs ADR 0002. Daemon lives in XC_VM_Binaries (not this repo).
2026-08-16 15:54:40 +03:00
404errordeveloper 51a3e399a4 Implemented dynamic symlink directory setter with dropdown in settings menu. Rather than the hardcoded path in previous commit, this one the user will be able to set themseleves 2026-08-15 17:46:47 +02:00
Divarion_D 33faee10ea Prepare release 2.3.9 2026-08-10 19:55:15 +03:00
Divarion_D f20cf039f7 Prepare release 2.3.8 2026-08-09 20:07:38 +03:00
Divarion_D 18cd757cc2 Prepare release 2.3.7 2026-08-02 11:55:34 +03:00
Divarion_D 22e422bb1c fix(streams): report real transcode speed instead of a stuck "1x"
The Stream Info column always showed a transcode speed of "1x" even though
ffmpeg's real speed fluctuates. Two independent bugs were involved.

1. Progress was never captured while a stream ran.
   ffmpeg's `-progress` posted to the HTTP /progress endpoint, but ffmpeg
   holds one open-ended chunked POST for the stream's whole life. PHP-FPM's
   php://input can't read a length-less body, and nginx only invokes PHP
   once the request body is complete, so the endpoint fired only at stream
   end. `progress_info` therefore stayed empty and the view fell back to the
   hardcoded "1x".

   Switch ffmpeg to write `-progress` to a local file (the mechanism the
   created-channel path already uses). StreamsCronJob now tails that file,
   keeps the last complete report block, re-encodes it as the JSON the panel
   expects, and truncates it each cycle so it stays tiny on disk.

2. Speeds just above 1 were swallowed by the display.
   `floor($speed * 100) / 100` dropped e.g. 1.01 to "1x" because 1.01 * 100
   is 100.9999… in floating point. Use round() instead, and apply the same
   robust parsing to the radios path (which multiplied the "1.01x" string).

Files:
- Domain/Stream/StreamProcess.php   ffmpeg -progress -> local file
- Cli/CronJobs/StreamsCronJob.php    tail/parse/truncate the progress file
- Public/Controllers/Admin/TableController.php  round() speed display
2026-08-01 23:35:05 +03:00
Divarion-D d02827d4a0 Prepare release 2.3.6 2026-07-10 19:58:19 +03:00
Divarion-D b7b9d13b5f feat(proxy): fetch proxy.tar.gz from XC_VM_Proxy releases at install + keep fresh via cron
proxy.tar.gz is no longer served from Git LFS. A new  proxyArchiveUpdater (Core/Proxy, mirrors MaxMindUpdater) downloads it from XC_VM_Proxy GitHub releases,
verifies hashes.md5, publishes atomically into bin/install/ and records a proxy_version.json index. It runs at panel startup (StartupCommand prefetch), on a daily cron:proxy (crontab seed + migration 009), and as a self-heal before proxy-node install (ServerInstallCommand, status=4 on failure). The streaming download primitive is extracted to CurlClient::downloadToFile and reused by ModuleManager. Clean-latest per channel, force-local kill-switch, last-known-good fallback when GitHub is unreachable.
2026-07-08 18:21:58 +03:00
Divarion-D 414e2c0eda Prepare release 2.3.5 2026-07-08 14:47:26 +03:00
Divarion-D 5d6a30918a Prepare release 2.3.4 2026-07-05 21:29:16 +03:00
Divarion-D de3316f968 fix(core): tolerate transient DB/API failures, report errors over HTTPS
Hardening driven by a 2.3.3 panel error log (166 entries):

- TmdbClient: _call() returns an empty/error payload on network or API
  failure — all 20 `$result['results']` accesses guarded with `?? []`
  (was: hourly "Undefined array key results" + foreach(null) warnings
  from the popular cron when the TMDb API was unreachable or the key
  was invalid).
- get_rows()/get_row() return false while MySQL is briefly unavailable
  (restart windows) — guarded with `?: []` in ServerRepository,
  BouquetService, BlocklistService (all 7 sites) and SettingsRepository,
  plus `?? ''` for the allow_countries / allowed_stb_types /
  stalker_lock_images json_decode inputs (was: foreach(bool) +
  "Undefined array key" bursts during DB restarts).
- ServersCronJob: @fsockopen — an unreachable node is an expected state
  already handled via the return value; the warning only polluted the
  panel log on every ping of an offline server.
- DiagnosticsService: send error reports to the xcvm.tech endpoint over
  HTTPS instead of plain HTTP.
2026-07-05 20:29:44 +03:00
Divarion-D bd6aad99f6 Prepare release 2.3.3 2026-07-04 18:46:10 +03:00
Divarion-D 938aede7e9 fix(admin): wire phpMiniAdmin access password to DB_ACCESS_PWD
database.php set $ACCESS_PWD to the boolean result of defined('DB_ACCESS_ENABLED'),
so the configured DB_ACCESS_PWD was never used as the phpMiniAdmin password. Source
it from the DB_ACCESS_PWD constant instead.

AppConfig ships safe defaults: DB_ACCESS_ENABLED=false and an empty DB_ACCESS_PWD
(a repo-committed password is public and protects nothing) — admins opt in and set
a strong password per install. Access stays behind the admin session + is_admin;
an empty password admits only local IPs.
2026-07-03 20:19:06 +03:00
Divarion-D 8d6debee8b Prepare release 2.3.2 2026-06-30 21:46:45 +03:00
Divarion-D 9d51092ace Prepare release 2.3.1 2026-06-26 21:22:27 +03:00
copilot-swe-agent[bot]andGitHub 64f25ff517 Merge remote-tracking branch 'origin/main' into chore/psr4-migration
# Conflicts:
#	src/migrations/deleted_files.txt
2026-06-26 13:45:45 +00: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 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 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 481e805e83 chore(psr4): phase 3 (Core leaf) — namespace ConfigReader + DomainResolver
Move ConfigReader and DomainResolver into XcVm\Core\Config (Composer PSR-4).
SettingsManager/SettingsRepository stay global for now (migrated together later
due to SettingsManager's large fan-out).

- Add namespace to both classes.
- Qualify still-global / ioncube refs: \XC_VM:: in ConfigReader; \CacheReader::,
  \ConnectionTracker:: in DomainResolver.
- Add 'use XcVm\Core\Config\...;' to the 15 referencing files (bootstrap, CLI
  commands/cron, LegacyInitializer, ModuleManager, stream + player controllers,
  player views, PlaylistGenerator, ...) — call sites unchanged.
- ModuleManager::isLoadBalancer: string class_exists('ConfigReader') →
  class_exists(ConfigReader::class) (the literal would now always be false).

Verified: php -l clean; PHPStan no errors; PHPUnit 295/295.
2026-06-24 20:43:40 +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