Adds a `make phpstan-deadcode` target and build/phpstan-deadcode.neon that
layer tomasvotruba/unused-public on top of the main PHPStan setup to report
unused PUBLIC methods/properties/constants. It is an on-demand audit, NOT a
CI gate (expect false positives for dynamically-invoked code — routes,
#[ListensTo] subscribers, CLI handlers, view templates).
unused-public is a require-dev package (installed by `make dev-tools`); the
committed vendor/ stays production-only.
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.
Relocate the dev-tooling config into build/ so the project root only holds
source and first-class project files:
- phpstan.dist.neon -> build/phpstan.dist.neon
- phpstan-baseline.neon -> build/phpstan-baseline.neon
- .php-cs-fixer.dist.php -> build/.php-cs-fixer.dist.php
- .php-cs-fixer.cache -> build/ (regenerated there; gitignored)
Because neon/CS-Fixer resolve relative paths against the config file's own
directory, the internal references are re-anchored one level up (src/ ->
../src/, tools/ -> ../tools/, Finder in(__DIR__.'/../src'); the baseline's
path: entries likewise). The Makefile now points PHPStan at the config with
-c build/phpstan.dist.neon (it previously relied on root auto-discovery),
generates the baseline into build/, and passes --config=build/... to CS-Fixer;
the CS-Fixer cache is pinned to build/ via setCacheFile and re-gitignored.
No behaviour change. Verified: make phpstan (No errors), make cs (0 fixable),
make gates. CI runs through these make targets, so it is covered.
gen-constants-stub.php had no Makefile/CI target, so tools/phpstan/
constants.stub.php (a bootstrapFile in phpstan.dist.neon) silently drifted:
regenerating it added DB_ACCESS_PWD and GIT_REPO_PROXY (real runtime define()s
in src/Core/Config/AppConfig.php that PHPStan could not see). Added `make
phpstan-stub` so the stub is regenerable/discoverable, and refreshed it
(129 -> 131 constants).
main_copy_files already copies the git-tracked migrations/deleted_files.txt into
the archive, so the separate delete_files_list step just re-copied and reprinted
it (looking like regeneration). Drop it from the `main` chain; keep it as an
optional manual preview. LB still needs lb_delete_files_list (migrations/ is not
in LB_DIRS).
`php -l` syntax checking is redundant with the real linters/validators (PHPStan
parses the code, PHP-CS-Fixer and the PHPUnit bootstrap also fail on parse
errors). Remove the script and every reference to it:
- Makefile: drop the `syntax_check` target and its .PHONY entry.
- CI (ci.yml): drop the dedicated `lint` (PHP Syntax Check) job.
- Release workflows (build-release, build_pre-release): drop the "Check syntax"
step from the Quality Gate (PHPUnit remains).
- CONTRIBUTING.md: replace the syntax-check pre-commit guidance with the real
checks (make dev-tools / phpstan / cs / gates / phpunit).
- updates_checklist (en/ru): replace `make syntax_check` with the quality-check
suite and drop the stale "Security scan" snippet that referenced the removed
script and a non-existent tools/run_scan.sh (Semgrep runs automatically in CI).
The committed src/vendor/ is production-only now, so PHPStan / PHP-CS-Fixer are no
longer present until installed. Update the Makefile accordingly:
- Drop the stale 'ships as a committed dev dependency' comments.
- Add a 'dev-tools' target (cd src && composer install) to install the require-dev
tooling locally; CI runs the equivalent step itself.
- phpstan / phpstan-baseline / cs / cs-fix now guard on the binary and print a
clear 'run make dev-tools' hint instead of a cryptic 'No such file' when the
dev tools have not been installed.
Switch from "commit vendor with dev deps + strip at release" to the standard
application model: the committed src/vendor/ is PRODUCTION-ONLY, and dev tooling
(PHPStan, PHP-CS-Fixer + ~37 transitive deps) is installed on demand with
"composer install".
- Regenerate the committed src/vendor/ via "composer install --no-dev"
(34 MB -> ~0.5 MB; only the Composer autoloader + gemorroj/m3u-parser +
chrisyue/php-m3u8 remain). This also stops PHPStan\PharAutoloader registering
in production.
- Commit src/composer.lock (un-ignored) — this is an application, so the lock is
committed to make "composer install" reproducible across dev/CI.
- Revert the release-time strip step (Makefile hooks + tools/build/
strip-dev-vendor.sh) — no longer needed; the archive ships the prod vendor as-is.
- CI: the phpstan and code-style jobs now run "composer install --working-dir=src"
(with tools: composer) to obtain the dev tools before running.
- New gate tools/ci/check-vendor-prod-only.sh (+ make check-vendor-prod-only,
wired into "make gates"): asserts no require-dev package from composer.lock is
committed under src/vendor/ — guards against accidentally committing a
dev-bloated vendor. Inspects git-tracked files, so it is correct even in a CI
job that already ran "composer install".
- Fix verify-lb-archive.sh: LB legitimately ships most of Cli/Commands and
Cli/CronJobs (edge commands + certbot/cache/cleanup crons), so flag only the
genuinely privileged dirs + the specific install/root files, not the whole dirs.
- .gitignore / composer.json notes updated.
Verified before pruning: PHPStan no errors, PHPUnit 303, cs + gates green. After
pruning: PHPUnit 303 (prod-only vendor), all three gates green. Local dev tools
restored afterwards with "composer install" (not committed).
The committed src/vendor/ carries require-dev packages (PHPStan, PHP-CS-Fixer +
their transitive symfony/react/psr deps, ~33 MB) so they are available for local
dev and CI. They must not ship to production — besides the weight, PHPStan's
files-autoload registers PHPStan\PharAutoloader at runtime on every request.
Add tools/build/strip-dev-vendor.sh and hook it into both `make main` and
`make lb` right after the file-copy step (before archiving). It runs against the
staged TEMP_DIR, not the repo:
- removes every dev package dir listed in vendor/composer/installed.json
(dev-package-names) and their bin shims;
- regenerates the autoloader with `composer dump-autoload --no-dev` (drops the
dev files-autoload, incl. the PharAutoloader);
- prunes the emptied vendor namespace dirs.
LB does not stage composer.json (not in LB_ROOT_FILES); the script copies it in
so dump-autoload can run (harmless metadata in the LB archive).
Result: shipped vendor/ goes 34 MB -> ~0.8 MB, contains only the Composer
autoloader + the two prod packages (gemorroj/m3u-parser, chrisyue/php-m3u8);
M3uParser/Chrisyue still autoload and no PharAutoloader is registered. The
committed src/vendor/ is left untouched (dev tooling stays for local use).
The XC_Autoloader fallback was already retired (no-op stub); this deletes it for
good. Resolution is now 100% Composer PSR-4 (+ ModuleLoader for modules), no
legacy scanner, no class-map cache.
- Move `define('MAIN_HOME', ...)` into bootstrap.php (autoload.php used to define
it); bootstrap.php now requires only vendor/autoload.php.
- Drop `\XC_Autoloader::clearCache()/warmCache()` from StartupCommand.
- tests/bootstrap.php: locate-guard + require switched to vendor/autoload.php.
- Entry points that required autoload.php directly — Public/index.php,
Public/admin/index.php, Public/stream/index.php, Public/progress/index.php,
ministra/portal.php and Admin/Reseller TableController — switched to
vendor/autoload.php (defining MAIN_HOME where they did not already). These were
not in the plan's checklist; found via grep during execution.
- phpstan.dist.neon: drop src/autoload.php from scanFiles.
- Makefile: drop autoload.php from LB_ROOT_FILES.
- deleted_files.txt: add autoload.php (client cleanup on update).
- AutoloadOrderTest: now asserts the XC_Autoloader class and file are gone.
- git rm src/autoload.php.
- PSR4_MIGRATION_PLAN.md: mark final-phase step 2 done.
Verified: grep XC_Autoloader:: = 0; php -l clean; PHPStan no errors; PHPUnit
303/303; make gates pass; bootstrap smoke — MAIN_HOME + XC_Bootstrap present,
XC_Autoloader gone, only the Composer autoloader registered.
Adds the automated gates the PSR-4 plan specified but that were verified only
manually per phase:
PHPUnit (run by the existing test job):
- AutoloadOrderTest — Composer autoloader registered; the retired
XC_Autoloader scanner is NOT in the SPL stack; init()
is a no-op; no igbinary class-map cache is written.
- BootstrapPathsTest — no live require/include points at a lowercase renamed
dir (the Фаза-1 grep-gate, as a runtime guard).
- ConsoleDiscoveryTest — console.php FQCN discovery resolves every Cli command
file and the concrete command surface stays stable.
Shell gates (new 'PSR-4 Regression Gates' CI job + 'make gates'):
- tools/ci/check_procedural_use.php — procedural/view files must import every
migrated class they use, with the `use` ABOVE the usage (PHP imports are
positional). Runs with short_open_tag=1 so short-tag templates are analysed.
- tools/ci/verify-lb-archive.sh — reproduces the Makefile LB file selection from
the real LB_* vars and asserts no privileged tree (Admin/Reseller/Player
controllers, Domain/User|Device, Cli/CronJobs|Commands) ships to an LB node
(security blocker 1).
Makefile: cs/cs-fix now force short_open_tag=1; new print-%, check-procedural-use,
verify-lb-archive and aggregate `gates` targets.
Add friendsofphp/php-cs-fixer as a committed Composer dev dependency (src/vendor/,
same model as PHPStan — no composer install on deploy).
- .php-cs-fixer.dist.php: deliberately NARROW ruleset — no_unused_imports,
ordered_imports, no_leading_import_slash, single_line_after_imports,
blank_line_after_namespace, no_extra_blank_lines[use]. NO @PSR12 / indentation
rules: the codebase is tab-indented legacy and a full reformat would be
unreviewable. Indent forced to tabs, LF endings. Excludes vendor, the bundled
Modules/tmdb/lib, tmp/, backups/.
- Makefile: 'make cs' (dry-run, fails on diff — CI) and 'make cs-fix' (apply).
- CI: new 'Code Style (PHP-CS-Fixer)' job running 'make cs' on PHP 8.3.
- .gitignore: ignore .php-cs-fixer.cache.
The downloaded phpstan.phar could not resolve namespaced third-party
packages now living in src/vendor/ (M3uParser, PhpM3u8), failing CI with
class.notFound on StreamService::parseM3U.
- add phpstan/phpstan 2.1.17 as a Composer dev dependency (committed to
src/vendor/); the vendor/bin/phpstan binary auto-loads
src/vendor/autoload.php, so vendor symbols resolve
- Makefile: drop the phpstan-install PHAR download; run src/vendor/bin/phpstan
- remove the stale Core/Parsing/M3uParser excludePath from phpstan.dist.neon
- drop the obsolete tools/phpstan/phpstan.phar gitignore entry
make phpstan: No errors (278 files).
Introduce a committed Composer PSR-4 autoloader without changing class
resolution behavior, as the foundation for the incremental PSR-4 migration.
- src/composer.json: PSR-4 (XcVm\ -> ./, M3uParser\, Chrisyue\PhpM3u8\),
platform php 8.1.33 (deploy runtime), optimize-autoloader/classmap-authoritative
false (live path resolution, no class-map cache). autoload.files left empty:
global functions are still loaded by existing require glue; moving them is
deferred until that glue is removed.
- src/vendor/ + src/composer.lock: committed (deploy path has no Composer);
generated with 'composer update' from src/. Regenerate with dump-autoload.
- src/bootstrap.php, tests/bootstrap.php: require vendor/autoload.php first,
then the legacy autoload.php.
- src/autoload.php: drop the igbinary disk cache (enableFileCache/saveCache/
shutdown handler/root-chown + bottom call); register at the END of the SPL
queue (prepend=false) so Composer wins for XcVm\* and only still-global
classes fall through to the in-memory scanner.
- Makefile: add vendor to LB_DIRS so load-balancer archives ship the loader.
- phpstan.dist.neon: exclude src/vendor/* from analysis.
- .gitignore: document that src/vendor/ is intentionally tracked.
- ci.yml: add composer-audit job (no-op until real require deps exist).
Verified: php -l clean; Composer first / XC_Autoloader last in the SPL stack;
tmp/cache/autoload_map no longer written; PHPUnit 292/292; PHPStan no errors.
- Implemented `ServerInstallCommand` for installing and configuring servers via SSH.
- Created `ProxyInstallFlow` class to handle proxy-specific installation tasks.
- Updated `ServerService` to use the new command structure for server installations.
- Modified API endpoint to initiate server installations using the new command format.
- Enhanced error handling and logging during installation processes.
Co-authored-by: Copilot <copilot@github.com>
Replace separate install/update build targets with a single archive that
serves both purposes. The update script (src/update) now extracts to a
temp directory, removes excluded dirs (binaries, config, user data), and
copies remaining files over the live installation.
Changes:
- Remove main_update, lb_update, lb_update_copy_files,
main_update_copy_files Makefile targets and UPDATE_EXCLUDE_DIRS var
- Move exclude dirs list into src/update (Python) where filtering
actually happens at runtime
- Rewrite doUpdate() to use tempdir extraction with try/finally cleanup
- Make delete_files_list/lb_delete_files_list gracefully skip when
LAST_TAG is empty (warn instead of error)
- Simplify CI workflows: one make command per variant instead of
conditional install + update steps
- Add ARCHITECTURE.md §5.5 documenting update flow
- Update en/ru docs: update-system.md, updates_checklist.md
- Update makefile-build.instructions.md with new targets
delete_files_list:
- Add LAST_TAG empty guard to prevent git diff against empty ref
- Use --diff-filter=DR to catch both deletions and renames
- Add sort -u to deduplicate entries
New target lb_delete_files_list:
- Filters deleted files to only those relevant to LB builds
- Uses awk to match against LB_DIRS and LB_ROOT_FILES
- lb_update now uses lb_delete_files_list instead of delete_files_list
CLI consolidation:
- Delete 13 legacy CLI scripts from includes/cli/ (ondemand, proxy, queue, record, scanner, signals, startup, thumbnail, tools, update, watchdog, plex_item, watch_item)
- Convert status and tools entry points to thin proxies that delegate to console.php
- Update all shell_exec() calls across admin controllers, views, and API layer to use console.php command syntax instead of direct CLI file paths
- Update src/service to launch daemons via console.php (signals, watchdog, queue, cache_handler, startup)
- Update Python src/update script to call console.php update instead of includes/cli/update.php
- Update test_installer to use console.php startup
Streaming deduplication:
- Extract StreamAuthMiddleware — common response headers and token decryption shared by live/vod/timeshift
- Extract ShutdownHandler — unified shutdown logic replacing 3 duplicate function shutdown() blocks
- Refactor live.php, vod.php, timeshift.php to use new middleware classes
- Add streaming micro-router to www/stream/index.php as fallback entry point
Routing fixes:
- Fix admin index.php redirect to use relative path (supports access code prefixes)
- Add access code root redirect in public/index.php to prevent broken CSS/JS asset resolution
- Fix init.php for CLI compatibility: guard $_SERVER access, define PHP_ERRORS safely
Migrations:
- 001_update_crontab_filenames.sql — strip .php suffix from crontab filenames
- Fix cache.php view query to match new filename format (cache_engine instead of cache_engine.php)
- Updated multiple files to use global variables for `$db` and `$rSettings` instead of calling `SettingsManager::getAll()` repeatedly.
- Removed unnecessary variable assignments and improved code readability.
- Deleted unused scripts for cleaning and scanning PHP file headers.
- Removed a file containing PHP syntax errors.
- Adjusted various repository and service classes to streamline database interactions.
- Replaced instances of `Database` with `DatabaseHandler` in `playlist.php` and `xplugin.php` for improved database handling.
- Updated logging calls from `StreamingUtilities::clientLog` to `DatabaseLogger::clientLog` in various stream handling files (`auth.php`, `live.php`, `vod.php`, `timeshift.php`, `rtmp.php`) to centralize logging functionality.
- Refactored bruteforce checking methods to utilize `BruteforceGuard` instead of `CoreUtilities` for consistency and better encapsulation.
- Enhanced error handling by ensuring all relevant error logs are captured through the new logging structure.