The header's Usage block only listed --duration/--json/--ua; expand it to the
complete run command, every argparse option (--stall-timeout, --tolerance,
--live, --prebuffer, --buffer-target, --no-color) with defaults, and a couple
more examples. Matches `--help` 1:1. No code change.
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.
Documents each tool in tools/ and its purpose, grouped by role: CI gates,
PHPStan support, the PHPUnit runner, manual panel test/QA utilities
(test_player_api.sh, stream_queue_check.py, test-stream-generator,
dts-audio-test, test-install), and repo maintenance
(update_top_contributors.py). Makes the previously-unreferenced manual test
utilities discoverable.
- tools/test-install/README.md listed /home/xc_vm/autoload.php among the
post-install sanity files, but src/autoload.php was removed at the Composer
migration; the shipped autoloader is vendor/autoload.php.
- tools/stream_queue_check.py had a real server IP (45.90.13.217) in a usage
example; replaced with a "host" placeholder.
The skip list keyed on /Modules/tmdb/lib/, which matches nothing: there is no
src/Modules/tmdb* dir, and the vendored non-namespaced TMDB library lives at
src/Infrastructure/Tmdb/lib/. So the intended skip was dead and that library
was being scanned. Point the skip at the real path. Gate still passes
(411 migrated classes, no violations).
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).
phpstan-bootstrap.php and phpstan.dist.neon claimed the project "has no
Composer and no PSR-4 namespaces" and referenced the removed src/autoload.php
— both false since the Composer PSR-4 migration (and self-contradicted by the
neon's own excludePaths note about src/vendor/autoload.php). Comment-only:
they now state the bootstrap defines constants and deliberately wires no
project autoloader (PHPStan discovers symbols via paths + scanDirectories).
No behavior change.
The script was never wired up (no `make module-hashes` target, no CI/Makefile
caller). Module hash_id generation now lives in the standalone Module_Template
kit; for existing modules, generate inline with
`php -r 'echo bin2hex(random_bytes(16));'`.
Updated the module-dev docs (en + ru) that referenced the removed script /
non-existent `make module-hashes` target to use the one-liner.
Standalone Python (stdlib-only) tool that verifies a stream delivers
segments correctly and its delivery queue does not break:
- HLS: EXT-X-MEDIA-SEQUENCE contiguity, no dropped/rewound segments, no
EXT-X-DISCONTINUITY, every newly appearing segment downloadable.
- MPEG-TS: per-PID continuity_counter, sync-byte loss, TEI, delivery stalls,
with a --tolerance for rare source glitches relayed by -c copy.
- --live: colored TUI dashboard modelling a virtual player — received
timeline from PCR (TS) / EXTINF (HLS), playhead, and buffered cache
seconds graphed over time.
Documented in docs/{en,ru}/development/streaming-subsystem.md.
tools/gen-module-hashes.php ensures every module.json carries a stable,
immutable hash_id (random 32-hex, generated once). Idempotent: modules that
already have a hash_id are untouched; new ones get it inserted after "name",
preserving file formatting. Run directly (php tools/gen-module-hashes.php).
Standalone xdebug installer helper that nothing references (not wired into
Makefile, CI, docs or any script) and is no longer needed. No other changes —
it had zero references in the repo.
`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).
Belt-and-suspenders so dev packages can never land in git:
- .gitignore: whitelist the committed production set under src/vendor/ and ignore
everything else (src/vendor/* + !autoload.php/!chrisyue/!composer/!gemorroj,
then re-ignore composer/{pcre,semver,xdebug-handler,autoload_files.php}). A
local `composer install` (for PHPStan/PHP-CS-Fixer) now writes dev packages
into ignored paths, so `git add` can never stage them. gitignore never
untracks, so the committed prod files stay tracked and updatable; a NEW prod
dep just needs a `!` whitelist line.
- check-vendor-prod-only.sh: add a second check on the committed
vendor/composer/installed.json — it must list no dev package. This covers the
one thing .gitignore cannot: installed.json is a tracked file that
`composer install` rewrites with dev entries. Both checks read the git INDEX,
so a local dev install does not trip the gate, but a committed dev artifact does.
Verified: clean tree passes; a dev `composer install` in the working tree leaves
the gate green (index-based); a fabricated dev installed.json is detected; no dev
dir is stageable after `composer install`.
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).
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.
- constants stub: use mt_rand()-based exprs so PHPStan infers GENERAL types,
not literal 0/'' — fixes false division-by-zero (PACKET_SIZE) and
foreach-over-false (str_split with len 0). Load stub via bootstrapFiles so
result-cache invalidates on change.
- return contracts: explicit returns where a path fell through to null and
violated the declared type:
- StreamRepository::getById/getWatchFolder, GroupService::getById,
getStream() → return false (declared array|false).
- ServerRepository::getPublicURL → return '' when server missing (array→string).
- MagService::resetSTB → return query() result (declared bool).
- StreamUtils::getPlaylistSegments → explicit return null.
- NetworkUtils::stopDownload → @return null corrected to @return void.
- PlexController: getPlexToken() called with 5 args but accepts 4 — dropped
the dead 5th argument.
- DropboxClient::getMetaFromHeaders: array_shift() on an array_filter()
expression (not a variable, by-ref error) — assign to a var first.
- WatchdogCommand: wrap numeric-string subtractions (nginx/proc-stat values)
in floatval()/intval().
- Encryption::generateUniqueCode(): null-coalesce $pass to prevent
md5(null) deprecation when live_streaming_pass is not yet set
- CacheCronJob: mkdir() with 0755 perms, recursive flag, and
chown to xc_vm user — fixes Permission denied on tmp/cache/*
- WatchdogCommand: null-safe decode of watchdog_data — fixes
array offset on null when server has no watchdog history
- ConnectionTracker: guard file_put_contents with is_dir() check
to prevent writes when CACHE_TMP_PATH does not exist yet
- Dockerfile: install cron package and enable cron.service for
systemd-based test container
- Add tools/php_syntax_check.sh (supports full scan + single-file mode)
- CI workflow now calls the shared script
- All 6 agents updated to reference the script
- CONTRIBUTING.md: add Pre-Commit Checks section
- Exclude src/bin/* (third-party stubs) from lint
Steps 11.1–11.6: migrated all standalone API entry points to controller classes
dispatched via the Front Controller, then deleted the legacy PHP files.
Controllers created:
- PlayerApiController (player_api.php, 12 actions + numeric aliases)
- Enigma2ApiController (enigma2.php + xplugin.php)
- PlaylistApiController (playlist.php)
- EpgApiController (epg.php)
- InternalApiController (api.php, ~40 server-to-server actions)
Nginx changes (MAIN + LB configs):
- Add location block for streaming API endpoints → FC with XC_SCOPE=api
- Add location block for internal API (/api.php) → FC with XC_API=internal
- Change 6 rewrite rules from break→last for deleted file URLs
- Remove allow/deny from /api.php location — auth handled by PHP
(password + IP whitelist + brute-force guard)
Front Controller (public/index.php):
- Add section 3a: REST API dispatch (XC_SCOPE=includes/api/*)
- Add section 3b: Streaming API dispatch (XC_SCOPE=api, XC_API=*)
- Move autoloader require to top (section 1b) for all code paths
- Controller map: endpoint name → controller class → init → dispatch
Bootstrap changes:
- www/init.php, www/stream/init.php: replace FC_API_NAME constant with
direct $rFilename variable (set by FC before require, checked via isset)
Bug fixes:
- PlayerApiController: fix 3 PHP 8 warnings ($rBouquets undefined,
missing 'rating'/'subtitle' array keys in get_vod_info)
- Fix server-to-server API calls blocked by nginx deny all (api_url_ip
uses external server IP, not 127.0.0.1)
Deleted files (8):
- www/player_api.php, www/enigma2.php, www/xplugin.php
- www/epg.php, www/playlist.php, www/api.php
- includes/api/admin/index.php, includes/api/reseller/index.php
New files:
- tools/test_player_api.sh — comprehensive Player API test suite (13 sections)
- 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.