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

Psalm Laravel Package

vimeo/psalm

Psalm is a static analysis tool for PHP that finds type errors, bugs, and unsafe code before runtime. It offers configurable strictness, baseline support, and CI integration to help teams catch issues early and improve code quality.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • Psalm is a dev-time-only dependency (require-dev). It ships no runtime code path, so there is zero impact on Laravel's bootstrap, container, autoload graph, or production artifact size. Architecture review is correspondingly cheap compared to a runtime library.
  • It is orthogonal to application architecture — works identically on a monolith, a modular Laravel app, or a DDD-style codebase. Value scales with type-annotation density, not with structural choices.
  • It occupies the same slot as Larastan/PHPStan. A TPM must make an explicit "one analysis backbone" decision up front; running both doubles CI cost and triage load, and produces overlapping-but-divergent findings.
  • The differentiated capability for a Laravel team is taint analysis, and 7.0.0-rc2 materially widens it:
    • Sources now include $_SERVER and $_FILES entries, DNS answers, paths/URLs consumed by builtins, and exception messages caught across @throws propagation.
    • Sinks now include printf/vprintf/print_r/var_dump/var_export/debug_zval_dump/vfprintf/gzpassthru/highlight_file/highlight_string and writes to php://output / php://stdout, i.e. the HTTP response body.
    • New taint kinds for query injection and path injection, alongside the existing SQL/HTML/shell/SSRF/header categories.
    • Propagation now covers closures (args and return through variables and callable parameters), generators and iteration over objects, trait bodies, builtin object dictionaries, __toString, implode/join, and array element write/unset tracking; superglobals are invalidated after calls that may write globals, and $_SESSION is reset after session_unset().
    • Net effect: fewer silent false negatives than the rc1 cycle, and a much larger finding surface. Both are true at once.
  • The expanded sink set maps to OWASP categories more completely, which strengthens the case for evidencing this as an SDLC security control — but it also means the tool now flags patterns (debug dumps, direct php://output writes) that Laravel teams historically considered harmless.
  • Complements, does not replace, PHPUnit/Pest, Rector, Pint, runtime APM, or DAST. It is static only; it does not observe runtime data flow.

Integration feasibility

  • composer require --dev vimeo/psalm, MIT license — no legal or procurement friction.
  • Laravel ergonomics depend on the community psalm-plugin-laravel package (facades, Eloquent magic properties, container resolution, validation). This plugin, not Psalm itself, is the real feasibility gate — and with rc2 it is now also the gate on whether the new sink and $_SERVER behaviour is usable inside framework code.
  • CI integration is one CLI invocation with multiple machine-readable reporters (text, JSON, checkstyle, GitHub Actions annotations, SARIF in recent releases) — drops into GitHub Actions, GitLab, Jenkins, Bitbucket with no bespoke tooling.
  • Baselines (--set-baseline) remain the decisive unlock for brownfield adoption. rc2 raises the stakes: the taint job should have a separate baseline from the type-analysis baseline, because the new sinks and $_SERVER sources generate a distinct class of finding that does not belong in a type-error baseline and will churn independently.
  • --init auto-selects an error level matching the current error count, giving a sane starting strictness without manual tuning.
  • Language-server mode provides incremental IDE feedback, which is what actually changes engineer behaviour day to day.
  • Shepherd taint-resolution blowup is fixed in rc2 (widened flows reaching a node through many call entries). This removes a concrete blocker to using Shepherd as a hosted coverage/level KPI for large codebases.

Technical risk

  • Bus factor is the top risk. The README states plainly that there is a single active maintainer, who monetizes via direct support contracts; two former maintainers have departed. This is a supply-chain dependency for a release-blocking CI gate.
  • Version volatility, and 7.0.0-rc2 is still pre-GA. The taint changeset grew again versus rc1, with new sources, new sinks, and new taint kinds. Do not put an RC into a release-blocking gate.
  • Sink expansion is a predictable false-positive source. print_r, var_dump, debug_zval_dump, and writes to php://output are common in Laravel debug paths and Blade templates. Security-correct to flag, but expect a loud first run. Mitigate by shipping the taint job report-only and triaging the sink policy explicitly.
  • $_SERVER / $_FILES tainting intersects with Laravel's request path. Framework internals read REQUEST_URI, HTTP_HOST, and similar. Expect a spike of findings in framework-adjacent code. This is a "verify before trusting" area, not a "disable the tool" area.
  • Silent false negatives are substantially reduced — closures, generators, iterated objects, trait bodies, superglobals after globals-writing calls, $_SESSION, list/array writes, and exception messages are now tracked. Confidence in taint as a control goes up; it is still not a security guarantee.
  • False-positive load on framework-heavy code remains the primary adoption killer. Unbounded suppressions or a disabled gate are the typical failure outcome, and rc2 raises the volume before it lowers it.
  • Plugin lag: a Psalm major can outrun psalm-plugin-laravel, blocking upgrades on unrelated PRs. rc2 does not change this; it makes the plugin compatibility check more consequential because the new sinks are the value proposition.
  • Performance/memory on large codebases can blow default memory limits and CI wall-clock budgets. The Shepherd fix helps large-graph taint resolution; the added analyzers increase per-run work.
  • Ignore the "Dependents: 0" signal — it reflects Psalm being a dev dependency, not low adoption. 5.9k stars, Vimeo provenance, and a public Shepherd coverage/level service indicate real production usage.

Key questions

  1. Psalm or Larastan/PHPStan — do we standardize on one, and who decides?
  2. What is the target error level (1–8) and the per-directory ratchet policy?
  3. Is taint analysis in scope as a security control, and who owns triage of its findings?
  4. What is our supported PHP version matrix, and does it force Psalm 5.x/6.x rather than 7.x? (rc2 adds PHP 8.4 Dom\ stubs on the 6.x line.)
  5. Do we purchase a support contract given the single-maintainer risk?
  6. What is the acceptable CI wall-clock budget for the analysis step, and is that budget split between the type job and the taint job?
  7. Who is the named internal owner (DevEx/platform) for config, baselines, and plugin upgrades?
  8. Are we willing to fund baseline burn-down as planned work, or will the baseline become permanent debt?
  9. rc2-specific: what is our policy on the new response-body sinks (print_r, var_dump, debug_zval_dump, php://output / php://stdout writes)? Suppress project-wide in the initial taint baseline, or triage case-by-case?
  10. rc2-specific: how do we handle $_SERVER / $_FILES taint findings surfacing through framework request internals — plugin-side suppression, per-directory exclusion, or vendor-ignore?
  11. Do we run type analysis and taint analysis as one invocation with a shared baseline, or as separate jobs with separate baselines? (Recommendation: separate.)

Integration Approach

Stack fit

  • Laravel 10/11/12 on PHP 8.1–8.4 + Composer. Psalm 7.x RC adds PHP 8.4 Dom\ stubs; verify the runtime matrix against whichever major we adopt.
  • CI-agnostic; first-class GitHub Actions annotation output. Optional pre-commit lane (Husky/CaptainHook) running changed-files-only for fast feedback.
  • Coexists cleanly with Pint, PHP-CS-Fixer, PHPUnit/Pest, Rector — no shared configuration or ordering constraints.
  • Shepherd (shepherd.dev) provides hosted coverage/level reporting with no infrastructure to operate, giving a TPM-visible KPI for free. rc2's taint-resolution fix makes this viable for large codebases.

Migration path Baseline-first, five steps:

  1. Install as dev dependency; run psalm --init to auto-pick an error level.
  2. Generate psalm-baseline.xml for the type job to freeze existing debt — the gate is green on day one.
  3. Run in CI report-only for 2–4 sprints; measure finding volume, false-positive ratio, and runtime.
  4. Flip type analysis to blocking, then ratchet: raise the level per directory, enforce "no new baseline entries" in review, and file burn-down tickets.
  5. Enable --taint-analysis as a separate, initially non-blocking CI job with its own baseline. It is noisier and slower than type analysis and should not share a gate with it. In rc2, seed the taint baseline only after the var_dump/print_r/php://output and $_SERVER questions are answered.

Compatibility

  • Pin the Psalm major (^6.0 or ^7.0) and pin psalm-plugin-laravel to a compatible major; group both in a single Dependabot/Renovate PR so upgrades are deliberate and reviewable.
  • Commit psalm.xml, type baseline, taint baseline, and any custom stubs as reviewed source.
  • Do not put a 7.0 RC into a release-blocking gate. Wait for GA plus confirmed plugin compatibility with the new sinks.
  • Treat rc1→rc2 as a baseline-regeneration event, not a patch: the taint finding set is a superset for most apps because of the new sources, sinks, and taint kinds. Regenerate the taint baseline in a dedicated commit.
  • Prefer standard PHPDoc where it expresses the same intent as @psalm-* annotations, to keep a future migration to PHPStan mechanical.

Sequencing

  • Phase 0 (1–2 days): spike on one representative service — measure runtime, memory, finding count, and false-positive ratio for both type and taint analysis. Explicitly count findings attributable to the rc2 sinks and $_SERVER sources. This single data point decides the programme.
  • Phase 1 (1 sprint): config + type baseline + non-blocking CI job + IDE rollout + named owner.
  • Phase 2 (1–2 sprints): triage, fix the high-yield classes (undefined methods, wrong types, dead code), publish a suppression policy, establish the ratchet. Add taint analysis as a second, non-blocking job with its own baseline.
  • Phase 3 (ongoing): raise levels, raise taint severity, wire Shepherd dashboards, schedule baseline burn-down for both baselines.
  • De-scope 7.0 until post-GA; schedule it as its own migration epic with a dedicated baseline-regeneration commit and a plugin-compatibility gate.

Operational Impact

Maintenance

  • Steady state is low: quarterly dependency bumps, monthly baseline review (now two baselines), config updates when modules are added.
  • Upgrades are the real cost: ~0.5–2 engineer-days per minor; the 7.x taint changeset realistically 3–5 engineer-days plus baseline churn, and rc2 adds another round of churn on top of rc1. Budget ~5–10% of one platform engineer's capacity.
  • Baselines decay without policy — a baseline nobody burns down is masked debt, not a control. Two baselines means two burn-down queues and two owners.

Support

  • MIT, community-only, no vendor SLA. Commercial support contracts (full integration, issue support, feature development) are offered directly by the sole active maintainer — the pragmatic mitigation for both bus factor and taint-tuning effort.
  • Community channels are Telegram-based with best-effort latency; do not plan to depend on them for triage.
  • Internal ownership is mandatory: one named DevEx/platform engineer owns config, both baselines, suppressions, and upgrades.

Scaling

  • Cost grows with codebase size. Use --threads, the incremental cache, and changed-files-only runs for PR fast lanes; full nightly analysis for the authoritative gate.
  • Monorepo: per-package psalm.xml with project references, or scope PR analysis to changed paths.
  • Large baselines slow startup and cause merge conflicts in high-velocity repos — regenerate in dedicated periodic commits rather than per-PR. This applies doubly to the taint baseline, which rc2 will churn.
  • Shepherd badges give org-level level/coverage trends with no infra ownership; rc2 removes the taint-resolution blowup that previously capped its usefulness at scale.

Failure modes

  • New false positive blocks CI on framework magic → team disables the gate. Mitigation: reviewed @psalm-suppress with mandatory justification comments and a documented triage SLA.
  • rc2 sink expansion floods the taint job with var_dump/print_r/php://output findings in debug code and Blade. Mitigation: separate non-blocking taint job, explicit sink policy decision (question 9), seed baseline after policy is set.
  • $_SERVER / $_FILES tainting produces a wall of findings in Laravel request internals. Mitigation: verify against psalm-plugin-laravel, per-directory or vendor exclusion, escalate to the maintainer if the plugin does not model Request correctly.
  • Plugin/Psalm version skew after a dependency bump breaks unrelated PRs. Mitigation: pinned versions, grouped upgrade PRs, canary branch.
  • Silent false negatives create false assurance. Materially reduced in rc2 but still present. Mitigation: position as one control among SAST/DAST/pen-test; never as a security guarantee.
  • Memory/timeout failures on large runs produce flaky CI. Mitigation: --memory-limit, sharding, nightly full runs, split type and taint jobs.
  • Maintainer abandonment or a stalled major blocks upgrades. Mitigation: retain a PHPStan/Larastan fallback and budget 5–15 engineer-days for a swap on a mid-size codebase; keep annotations portable.
  • Baseline absorbs every new error (type or taint). Mitigation: CI check that rejects PRs adding entries to either baseline; burn-down tickets with capacity attached.

Ramp-up

  • Engineers: near-zero day-one cost — findings are readable prose. 1–2 weeks to fluency on generics/@template, purity/@psalm-immutable, and taint annotations. The binding constraint is triage discipline, not tool knowledge.
  • Taint-specific training now needed because rc2 adds new taint kinds and sink semantics — engineers must be able to distinguish a true query/path-injection finding from a debug-dump sink hit. Budget ~1 extra day per engineer.
  • TPM: one sprint to establish KPIs (error level, absolute error count, type-baseline size and trend, taint-baseline size and trend, taint-finding count by kind, CI duration per job) and publish a ratchet policy.
  • Team onboarding: ~3–5 engineer-days total for training plus a runbook, amortized quickly at 100k+ LOC.
  • The dominant organizational risk is not technical: without a named owner and ring-fenced capacity for findings in both baseline queues, expect the baselines to absorb everything and the gate to be disabled within two quarters. Decide the ownership model before the tool is installed.
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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator