alies-dev/psalm-tester
Run Psalm assertions and traces as phpt test files inside PHPUnit. Write --FILE--/--EXPECT-- fixtures, use EXPECTF for flexible matching, pass custom Psalm arguments globally or per test via --ARGS--, and skip tests conditionally with --SKIPIF--.
.phpt --EXPECT-- / --EXPECTF--), a Psalm or psalm-plugin-laravel upgrade produces an expected-vs-actual diff of type inference changes — missing issues, unexpected issues, and CheckType mismatches — before anything merges. That converts "should we take this upgrade?" from a vibe call into a reviewed diff.PsalmPhptTestCase supplies php-src-style .phpt discovery, --SKIPIF-- handling, one batched Psalm run per argument group, and PHPUnit --filter narrowing. In-house you would be writing a fixture parser, a batched process runner, issue diffing, and per-run cache isolation — real weeks of work with zero product differentiation. Adopt, don't build.withConcurrency(), withTimeout() (kills the Psalm process and its children; Windows: Psalm only), withWorkingDirectory(), withEnv(), and per-run cache directories make analysis bounded and deterministic. A hung worker errors the affected tests instead of stalling the pipeline — that is a pipeline-reliability decision with a cost attached.--XFAIL-- reports an expected failure as incomplete and, critically, as a failure once it passes. Teams can land fixtures for upstream Psalm bugs or pending refactors without creating a silently rotting skip.--CONFLICTS-- (php-src semantics) isolates conflicting fixtures into their own Psalm run, prevents runs sharing a key from overlapping, and makes all run alone — the mechanism that lets you raise concurrency without flaky analysis.PsalmTester::create() + with*() methods and run()/runOne() return a Result with Outcome, formatted output, and structured Issues. Trend collection and gating hang off a typed API. Caveat: the old human-facing progress output is gone; the only console signal is one STDERR start line (psalm-tester: N phpt files (S skipped), G Psalm runs).--EXPECT_EXTERNAL--/--EXPECTF_EXTERNAL-- rejected, --ARGS-- now appends to configured arguments instead of replacing them, withArguments() takes one argument per parameter (no shell), and repeated sections throw. Budget an upgrade pass against UPGRADING.md; do not treat it as a drop-in.Adopt if:
--filter to a single fixture for fast local iteration.--XFAIL-- semantics that flip to failure when the underlying issue is fixed.--CONFLICTS-- scheduling instead of hand-split test groups.Result/Outcome/Issue API rather than regexing console output.Look elsewhere — or plan to fork — if:
.phpt corpus is the asset and the maintenance burden. Without a named owner, fixture drift becomes noise and the suite gets skipped.composer.lock.Adoption prerequisites to confirm before committing: maintenance health of the upstream repo (release cadence, issue responsiveness), that the released version actually matches the changelog you're reading (verify tag provenance — some metadata in this evaluation looks anomalous), and a spike that ports ~10 existing type expectations to .phpt to measure real authoring cost.
To executives (one paragraph): We maintain Laravel-specific type information that our static analysis depends on, and today we have no way to prove a Psalm or plugin upgrade didn't silently change what the analyzer infers. alies-dev/psalm-tester lets us assert inferred types as ordinary PHPUnit tests — so an upgrade that changes type inference fails the build with a precise expected-vs-actual diff instead of surfacing as confusing errors weeks later. It is a dev-only, MIT-licensed dependency with no production runtime footprint, so the downside is bounded to engineering time. The trade-off is maturity: it is a young, single-maintainer package with essentially no external adoption, so we should pin versions and treat the option to fork as part of the plan. Estimated cost is a short adoption spike plus an upgrade pass for the 0.4 breaking changes; the return is a repeatable, auditable quality gate on the type layer our tooling is built on.
To engineering (the ask):
.phpt, wire PsalmPhptTestCase, measure authoring time and suite runtime.--filter locally, fails loudly on Psalm crashes/timeouts, and one real Psalm upgrade is diagnosed from its diff.UPGRADING.md review per bump), fixture maintenance ownership (name an owner), and single-maintainer bus factor (mitigate with MIT fork contingency).How can I help you explore Laravel packages today?