Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Peststan Laravel Package

mrpunyapal/peststan

PHPStan extension for Pest PHP that adds type-safe expectations, proper $this binding in test closures, generic expect() inference, type-narrowing assertions, and accurate return types for all Pest functions. Requires PHP 8.2+, PHPStan 2, Pest 3-5.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • PestStan is a pure static-analysis extension: it hooks into PHPStan 2.x via phpstan/extension-installer (or an explicit includes: vendor/mrpunyapal/peststan/extension.neon). It adds no runtime code, no service provider, no config publish step — so it sits entirely in the dev/CI layer and cannot affect production behavior.
  • It works by (a) supplying generic signature stubs for Pest's global functions (expect(), it(), test(), describe(), hooks, dataset(), covers(), etc.), (b) narrowing the Expectation<T> generic through assertion chains, (c) inferring $this binding in closures by reading uses(TestCase::class)->in('...') out of tests/Pest.php, and (d) registering ~15 custom rules with stable identifiers (pest.test.emptyClosure, pest.expectation.impossible, pest.lifecycle.beforeAllThisUsage, …).
  • This is exactly the layer a Laravel shop with an existing PHPStan pipeline wants: it improves signal in the test half of the codebase, which is usually excluded from analysis or covered with an untyped baseline.

Integration feasibility

  • Low effort to wire: one composer require --dev, auto-registration, optional parameters.peststan.* block. A single engineer can validate in a spike branch in a few hours.
  • Real effort is not installation but baseline remediation. Enabling the rule set on a mature suite will surface new diagnostics (empty closures, duplicate descriptions, impossible/redundant assertions, $this in beforeAll). That is a cleanup wave, not a config change.
  • Requirement floor is PHP ^8.2, PHPStan ^2.0, Pest ^3.0–^5.0. Any team still on PHPStan 1.x or Pest 2.x is out of scope.

Technical risk

  • The dominant risk is Archived: yes. A package with zero dependents and 53 stars has no community safety net, and archiving means no future compatibility work. PHPStan custom rules and type-narrowing extensions lean on semi-internal APIs that change across majors; the moment we move to PHPStan 3.x or Pest 6.x, this extension is the most likely thing to break and nobody upstream will fix it.
  • Silent degradation, not loud failure. The Pest.php TestCase auto-detection is heuristic and path-scoped. In monorepos or multi-root test layouts it can quietly produce mixed or unbound $this, which reads to developers as "the extension is doing nothing" rather than an error.
  • False positives have asymmetric cost. pest.expectation.redundant and pest.test.staticClosure are opinionated; the cheap escape hatch is a blanket ignoreErrors entry, which permanently guts the value of the rule.
  • Low social proof. 53 stars / 0 dependents means we will be among the first production users of any given edge case.

Key questions

  1. Why was it archived — abandoned, superseded by native Pest/PHPStan support, or folded into another project? Is there a maintained fork?
  2. What is our PHPStan and Pest upgrade roadmap over the next 12 months, and are we willing to pin for this?
  3. Does per-directory uses() detection actually resolve for our test tree (multiple Pest.php files, tests/Unit, tests/Feature, modules with their own test roots)?
  4. Who will own the phpstan.neon and baseline diff, and what is our policy on @phpstan-ignore for these identifiers?
  5. Is dynamic-property inference in beforeEach a real win for us, or do our suites already annotate with @var?

Integration Approach

Stack fit

  • Assumes the standard Laravel shape: PHP 8.2+, Pest as the test runner, PHPStan already in CI with a committed baseline. If PHPStan is not already gating PRs, adding this package is premature — fix the base pipeline first.
  • Install as require-dev only; confirm it is excluded from production autoload/--no-dev deploys. Nothing else in the app changes.

Migration path

  • Phase 0 — Spike: install in a throwaway branch, capture the full diagnostic delta against current main, and quantify it (count by identifier, by directory). This is the go/no-go number.
  • Phase 1 — Adopt without enforcing: commit the config, generate baselines scoped per identifier, and run analysis in report-only mode so CI stays green.
  • Phase 2 — Burn down: fix genuine findings (empty test bodies, static closures, duplicate descriptions are all real bugs); suppress only the noisy/opinionated identifiers with documented rationale.
  • Phase 3 — Enforce: make analysis blocking, and treat new occurrences as review comments.
  • Phase 4 — Exit plan: because the package is archived, mirror the resolved version into an internal Composer repository (or vendor-fork it) so a future Packagist/upstream change cannot break our build. If maintenance becomes a burden, the highest-value rules (empty closure, static closure, beforeAll $this) are inexpensive to reimplement in-house; the generic Expectation<T> narrowing is not.

Compatibility

  • Pin the exact version, not a range, and record the PHPStan/Pest version triad in the repo README.
  • Verify against our Pest major specifically — the README's version table (fixture() "when available") implies behavior varies by installed Pest version, which is a compatibility surface worth testing.
  • Use parameters.peststan.pestConfigFiles explicitly rather than relying on path discovery if the test tree is non-standard.

Sequencing

  1. Confirm ownership and the archive/successor question (blocking — do not roll out on an unmaintained package without a named owner).
  2. Spike + diagnostic delta measurement on one representative app.
  3. Internal mirror/pin of the chosen version.
  4. Baseline and report-only rollout.
  5. Remediation sprint, then enforcement.
  6. Add a recurring calendar check tied to PHPStan/Pest major upgrades.

Operational Impact

Maintenance

  • Zero runtime maintenance, non-trivial lifecycle maintenance. Archived status means we are the maintainers of last resort: a PHPStan major bump becomes our incident, not an upstream fix. Budget a half-day per PHPStan/Pest major upgrade to validate, patch, or vendor-hack.
  • Dependency surface is small (PHP dev tooling only) but the coupling is deep — the extension reaches into PHPStan's type system and generic narrowing, which is the least stable part of PHPStan's API.

Support

  • No upstream issue tracker, no PR path, no backport. Internal support burden falls on whichever engineer owns the static-analysis pipeline (typically DevEx/QA/platform).
  • Offsetting that: the README is unusually complete — rule catalog, identifiers, config knobs, and suppression recipes are all documented. Onboarding cost for a new engineer is genuinely low, which is the single strongest argument for adoption despite the archive status.

Scaling

  • Cost scales linearly with the number and size of test files analyzed, plus hook-count in beforeEach/beforeAll closures (dynamic property inference walks those bodies). On large suites this is measurable but usually small relative to PHPStan's existing analysis time — benchmark before/after rather than assuming.
  • Benefit scales with suite size and test-file type density: a 2,000-test Pest suite with heavy $this usage gets real value; a 50-test suite with three files does not justify the ownership burden.

Failure modes

  • Archived-and-abandoned: dist archives and Packagist metadata persist indefinitely in practice, but there is no one to patch a break. Mitigation: internal mirror + pinned version.
  • Hard break on PHPStan/Pest major upgrade: CI goes red or, worse, the extension loads and silently stops narrowing. Mitigation: pin tool versions; assert expected diagnostics with a small fixture test so a silent no-op is detectable.
  • Noisy rules → blanket ignores: teams suppress entire identifiers and lose the value permanently. Mitigation: named owner, per-identifier baseline entries with justification, review-time convention against pest.* wildcards.
  • Heuristic Pest.php detection failure: $this is mixed and dynamic properties are untyped — looks like the tool "isn't working." Mitigation: set testCaseClass explicitly and document it.
  • Config drift: two test roots, one properly configured. Mitigation: enforce config in a shared phpstan.neon.dist inherited by all apps.

Ramp-up

  • One engineer: roughly half a day to install, validate auto-detection, and produce the diagnostic delta; 1–2 sprints of cleanup if we enforce on a mature suite.
  • Team-wide: a short internal doc covering the identifier catalog and the sanctioned suppression pattern is sufficient — no training program required.
  • Decision framing: treat this as a time-boxed, low-cost experiment with a pre-committed exit. Given archiving, 0 dependents, and 53 stars, adopt only the rules that pay for themselves immediately, keep the version pinned and mirrored, and plan for replacement at the next PHPStan major rather than assuming the package carries forward.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel