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

Laravel Code Analyse Laravel Package

nunomaduro/laravel-code-analyse

Laravel Code Analyse brings static code analysis to your Laravel apps. Run the artisan command to scan your codebase against configurable checks, catch common issues early, and keep your code clean, consistent, and production-ready.

View on GitHub
Deep Wiki
Context7

Getting Started

Larastan is a require-dev dependency plus a CI invocation — there is nothing to register, migrate, or deploy.

Minimum viable setup (three steps):

composer require --dev larastan/larastan
# phpstan.neon
includes:
    - vendor/larastan/larastan/extension.neon

parameters:
    level: 5
    paths:
        - app/
vendor/bin/phpstan analyse --memory-limit=2G

Two setup rules worth internalizing immediately:

  1. Move PHPStan in the same commit. Larastan 3.13.x requires PHPStan ^2.3 within the 2.x series. Bump both in one PR and read the lockfile diff; a split upgrade leaves the extension resolving against an unsupported PHPStan and produces a broken intermediate state that is worse than not upgrading.
  2. Start at level 5, not max. Level 9 on a real application produces thousands of findings and the report becomes wallpaper. Level 5 already exercises the Eloquent, facade, container, and collection inference that justifies the package.

Where to look first:

  • The first run's report, filtered for Eloquent-related messages. Relationship shape, eager-loading, and collection key errors are the ones unit tests under-cover and the ones this package uniquely finds.
  • vendor/larastan/larastan/extension.neon — this is the source of truth for services, parameters, and defaults. Read it before copying config snippets from blog posts.
  • docs/custom-types.md in the package — where relation-of, model generics, and builder types are documented.

First use case: run it against app/Models/ only, and look at what it says about with(), scopes, and relationship return types. That single directory is where the inference is strongest and where the payoff is most visible in a first afternoon.

Then commit a baseline so the gate is green on day one, and ratchet from there:

vendor/bin/phpstan analyse --generate-baseline

Implementation Patterns

Ladder the level per directory, not globally. PHPStan has no per-path level, so use one config per band and run them as separate CI steps:

phpstan.neon            # level 6, app/
phpstan-tests.neon      # level 4, tests/

This lets the high-churn, high-value code (app/) sit at a stricter level than test scaffolding without one blocking the other.

Baseline as a ledger, not a mute button. Generate the baseline once, then treat every entry as an IOU with a documented reason. Regenerate only after triage — never before — so the file records decisions instead of masking them. Shrink it deliberately as debt is paid; a baseline that only grows is a gate that verifies nothing.

Annotation hotspots, in priority order. The signatures that pay for themselves fastest:

  • Repository and service methods returning query builders — annotate with the concrete builder and model generics.
  • Model scopes — @return EloquentBuilder<Model> and, new in 3.13, @return relation-of<Model, Related> where a relationship is returned.
  • Collection returns — @return Collection<int, Post> kills the "no value type specified in iterable" class of noise wholesale.
  • Container resolution — app(Foo::class) and resolve(Foo::class) infer correctly when the argument is a class-string; passing a string variable defeats it.

Use relation-of<Model, Relationship> at the boundary. The highest-value place is a method signature a human reads — a repository method, a scope, a service returning a loaded relationship. It documents intent and gives the analyzer a concrete type in one line. Don't spray it everywhere; the analyzer infers relationships inside with(), withOnly(), load(), and loadMissing() arrays automatically now, so reach for the explicit type when the value crosses a method boundary.

CI shape. Run it as a blocking job parallel to tests, with caching:

vendor/bin/phpstan analyse --no-progress --error-format=github --memory-limit=2G

--error-format=github gives inline PR annotations, which is the difference between a gate people fix and a gate people re-run until it passes. Keep a second, non-blocking job for anything experimental (bleeding-edge inference, a new rule) so you can evaluate it against real output before it can fail a PR.

Fast local loop. Run the full analysis before pushing, and scope to the file you're editing while working. Pair it with \PHPStan\dumpType($expr) dropped temporarily into a reproducer to see exactly what the analyzer inferred — this converts "why is it complaining?" into a one-minute answer instead of a thirty-minute guess.

Custom rules for project conventions. Larastan covers Laravel semantics; it does not know your house rules (no env() outside config, no DB:: in controllers). Those belong in src/Rules/ of your own small extension, registered in the same phpstan.neon. Start with one rule you already enforce in code review — that's the one with a demonstrated cost.

Gotchas and Tips

The 3.13 upgrade will produce a nonzero delta on unchanged code. New relationship inference and stricter collection key types legitimately surface previously silent issues. Capture the pre-upgrade run, upgrade, diff, then triage into three buckets: real bugs, false positives to baseline with a reason, and pre-existing debt for the backlog. Do not regenerate the baseline first.

with(['posts:id' => $callback]) is now reported, and will likely be your largest source of new failures. Column selection combined with a callback is a real correctness and performance pitfall — one of the two is redundant at best. Fix by dropping the column constraint or the callback, and let this one block.

Don't enable PHPStan 2.3's bleeding-edge inference in the blocking gate. Evaluate it in an advisory job first. Opt-in inference for empty collections and closures is high-value but the failure mode is a sudden wave of findings across code nobody is touching that week.

Bootstrap failures in stripped CI environments. The analyzer needs a bootstrapped application — composer install must have run so the package manifest exists. Recent releases fixed a null package-manifest path crash, but the surrounding class of failure (missing .env, missing app key, service providers that reach for the network) still bites. Use a dedicated phpstan-bootstrap.php and set APP_ENV=testing rather than relying on the pipeline's ambient environment.

Exclude generated and IDE-helper files from paths, but don't exclude migrations from the scan. Larastan reads migrations and casts to infer model properties; excluding them from analysis is correct and expected, but the schema scan still needs to find them. If model property inference looks wrong, check squashed_migrations_path and the migration scan settings before adding @property docblocks.

Validate custom query builders explicitly. If you override toBase() or return a custom builder from a model, exercise those classes directly — forwarded-call resolution through custom builders has been an active fix area, and it's exactly the kind of code that lives in one file and is never analyzed on its own.

Wrapper vs. engine: pick one layer deliberately. The zero-configuration wrapper (one command, curated strict defaults, Pint/Rector/GitHub Actions glue) is an accelerator; larastan/larastan is the durable dependency. The moment you need a custom rule, a targeted suppression, or a level change, you are writing PHPStan config anyway. If the wrapper's defaults conflict with team policy, reconcile them into your own phpstan.neon rather than layering — two competing profiles is how a gate becomes decorative. Note also that a wrapper pinned to an older engine lags the inference fixes above; check its release cadence against the engine's.

Prefer inline @phpstan-ignore with an identifier for new code, baseline for legacy. An inline ignore sits next to the thing it excuses and gets noticed on the next edit. A baseline entry is invisible and rots. Reserve the baseline for code you have decided not to touch.

Memory and parallelism. Large Eloquent-heavy apps need --memory-limit=2G or more; CI containers OOM silently and report as a truncated run. Cache the analysis directory across runs. If a run hangs, --debug shows the file being analyzed and -vvv usually identifies the offending expression.

It is not a test suite, a security scanner, or a performance profiler. It changes nothing at runtime, so it is not an input to availability or incident planning. Sell it as what it is: a cheap way to catch the relationship, eager-loading, and collection-key defects that reach production because nothing else looks at them.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo