psalm.xml
Rama master · réplica de lectura del repositorio original.
<?xml version="1.0"?>
<!--
Psalm configuration for phlix-server (S146).
HISTORY — why this file exists. The "Psalm Static Analysis" job in
.github/workflows/coding-standards.yml has existed since the very first CI
commit (b7c728ca, 2026-05-15) but `vimeo/psalm` was NEVER added to
composer.json, so `vendor/bin/psalm` never existed, the job's
`if [ -f vendor/bin/psalm ]` guard was always false, and the job went GREEN
having analysed zero files. It was counted as real coverage in PR reviews.
S146 finishes the abandoned rollout: the dependency, this config, the guard
removal and the code fixes all land together, because adding the dependency
on its own makes psalm abort with "Could not locate a config XML file" and
turns master red instantly.
errorLevel="5" is a deliberate, measured choice — NOT a baseline. Measured
finding counts for src/ on 2026-07-27 with the analysis extensions loaded:
level 1: 4343 level 2: 1484 level 3: 620 level 4: 484
level 5: 89 level 6: 78 level 7: 30 level 8: 6
Level 4 is the cliff: 348 of the 395 findings it adds over level 5 are
RedundantCondition / TypeDoesNotContainType / RedundantCast, and in this
codebase those are overwhelmingly deliberate is_array()/is_string() guards
on database rows and decoded JSON. Deleting them to satisfy psalm would
strip real runtime protection, so level 5 is where the tool stops arguing
with defensive code and starts reporting defects. Raise the level if and
when those guards are replaced by typed row objects; do NOT add a
psalm-baseline.xml, which would reproduce the exact "gate that proves
nothing" defect S146 exists to remove.
phpVersion is pinned to the version CI and production analyse against, so
the result does not drift with whatever PHP the runner happens to ship.
S256-residual (2026-09-10) — scripts/ joined the production corpus, mirroring
the hub's S248 shape. The level ladder above was measured over src/ only and
is UNCHANGED by this step; the scripts/ addition was measured separately:
Psalm 6.5.0 reported 10 level-5 findings across scripts/ (43 PHP files
enumerated at the time of writing), and every one was fixed at the source.
Five were live runtime defects: two boot-blocking constructor failures
(backfill-chapters.php passed the raw Connection to MarkerCandidateRepository,
run-media-asset-worker.php passed it to FfmpegRunner — whose review follow-up
also gave it the ConnectionPool::init() every sibling script has), a wrong
config key (backfill-streams.php read 'probe_path', so a custom ffprobe binary
was silently ignored), a collect() name collision with the global crell/fp
ships (add-headers.php), and a ParadoxicalCondition from a ternary that
re-read an offset after narrowing (ci-browser-e2e-prereqs.php). The remaining
findings were type honesty — chiefly 4 UndefinedClass reports for
Swoole\\Coroutine\\WaitGroup resolved by the <stubs> entry below (the same
drift-tested declaration the phpstan configs scanFiles) — and the add-headers
rewrite. No baseline, no ignoreErrors, no IssueHandler, zero
<file> exclusions here. tests/ is NOT part of this config; it keeps its own
measured-L5 file, psalm-tests.xml (S306). Scope is asserted, with negative
fuzz, by tests/Unit/Support/StaticAnalysisScopeTest.php.
-->
<psalm
errorLevel="5"
phpVersion="8.3"
resolveFromConfigFile="true"
findUnusedCode="false"
findUnusedBaselineEntry="false"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<directory name="scripts" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
<!-- S256-residual: same single-source-of-truth stub the tests config and
phpstan-tests.neon load. scripts/bench/coroutine_bench.php constructs a
Swoole\Coroutine\WaitGroup behind an extension_loaded('swoole') guard;
it is a userland class the swoole binary compiles in, invisible to
file-based analysis even when the extension is loaded (measured in
S306). The declaration is drift-tested against the real extension by
StaticAnalysisScopeTest::testTheWaitGroupDeclarationStillMatchesTheRealExtension. -->
<stubs>
<file name="phpstan-stubs/Swoole/Coroutine/WaitGroup.stub" />
</stubs>
</psalm>