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

Larastan Strict Rules Laravel Package

canvural/larastan-strict-rules

Extra strict, opinionated PHPStan rules for Laravel. Install via Composer, include rules.neon, and enable or disable checks like no dynamic where, no facades, no global helpers, and no validation in controllers.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a rule pack, not an analyzer. It only does something once Larastan is installed and configured, because every rule inspects Laravel-specific type information that Larastan produces.

Minimal setup, in order:

composer require --dev larastan/larastan
composer require --dev canvural/larastan-strict-rules

Then in phpstan.neon (or phpstan.neon.dist):

includes:
    - vendor/larastan/larastan/extension.neon
    - vendor/canvural/larastan-strict-rules/rules.neon

parameters:
    level: 6
    paths:
        - app

Run vendor/bin/phpstan analyse and you should immediately see a wall of errors. That wall is the point — but do not try to clear it in one pass.

Where to look first: the eight rule names in the README are the whole surface area of the package:

  • NoDynamicWhereRule
  • NoFacadeRule
  • NoGlobalLaravelFunctionRule
  • NoValidationInControllerRule
  • NoLocalQueryScopeRule
  • ScopeShouldReturnQueryBuilderRule
  • NoPropertyAccessorRule
  • ListenerShouldHaveVoidReturnTypeRule

First use case: pick the one rule your team already argues about. In most codebases that is NoFacadeRule or NoGlobalLaravelFunctionRule. Start there, with everything else off:

parameters:
    larastanStrictRules:
        allRules: false
        noFacade: true

That single toggle is the right onboarding path. It produces a bounded, understandable set of errors that map to a refactoring pattern everyone recognizes (constructor injection), rather than an unbounded philosophical argument about whether query scopes are good.

Implementation Patterns

Incremental adoption via allRules: false. Never enable the full pack on an existing codebase. Turn rules on one at a time, fix the violations, commit, then enable the next. The config supports exactly this and it is the only sane migration strategy for a legacy app.

Baseline as a ratchet, not a hiding place. For a rule you want on but cannot fix today:

vendor/bin/phpstan analyse --generate-baseline

Commit phpstan-baseline.neon, include it, and treat baseline growth as a review failure. When you later delete the baseline file and the count is small enough, delete it for real. Because the rules are message-based, re-baselining after a PHPStan upgrade is normal and expected.

Per-rule toggling in CI jobs. You cannot scope a rule to a directory with this package — the toggles are global parameters. The workaround is multiple config files and multiple CI steps:

# phpstan-app.neon
includes:
    - phpstan.neon
parameters:
    paths:
        - app
    larastanStrictRules:
        allRules: false
        noFacade: true
        noValidationInController: true
# phpstan-domain.neon  (stricter, newer code)
includes:
    - phpstan.neon
parameters:
    paths:
        - app/Domain
    larastanStrictRules:
        allRules: true

This lets new modules be fully strict while app/Http lags behind. It is the single most useful pattern for day-to-day use.

Refactor recipes the rules force you into. These are the shapes you will write repeatedly:

// NoFacadeRule: inject instead of Cache::get()
public function __construct(private Repository $cache) {}

// NoGlobalLaravelFunctionRule: replace config()/event()/app() calls
public function __construct(private Config $config, private Dispatcher $events) {}

For NoGlobalLaravelFunctionRule, list the helpers you genuinely cannot live without rather than fighting them:

parameters:
    allowedGlobalFunctions:
        - app
        - event
        - report

NoValidationInControllerRule pushes validation into FormRequests — keep the authorize() method honest, since that is the usual casualty of the move.

NoDynamicWhereRule turns whereUserId($id) into where('user_id', $id), which is a readability gain and a grep-ability gain. Note that as of 2.1.3 wherePivot-style calls and, as of 2.1.8, model scopes are correctly excluded — so the rule is much less noisy than its reputation suggests.

Query scopes are an either/or. NoLocalQueryScopeRule and ScopeShouldReturnQueryBuilderRule are mutually redundant: once scopes are banned outright, the return-type rule has nothing to check. Enable the return-type rule if you are keeping scopes; enable the ban if you are moving to dedicated query builder classes. Do not enable both and expect meaningful output.

Event listeners. ListenerShouldHaveVoidReturnTypeRule needs listenerPaths to be useful:

parameters:
    listenerPaths:
        - app/Listeners
        - app/DomainA/Listeners

Without it, the rule inspects every class with a handle() method (commands and middlewares are skipped), which is slower and noisier. Set the paths explicitly in every config file — this is a top-level parameter, not nested under larastanStrictRules.

Gotchas and Tips

allowedGlobalFunctions and listenerPaths are top-level parameters, not children of larastanStrictRules. The README shows this, and it is easy to misread because every toggle lives under larastanStrictRules. If you nest them, PHPStan will silently accept the unknown key or ignore it, and the rule will not behave as configured.

allRules: false plus no explicit true means zero rules run. The include still loads the services; nothing reports. When you "turned the package on and it did nothing," this is almost always why.

Version lockstep with Larastan is mandatory. Roughly half the changelog entries (2.1.4 through 2.1.10, plus 0.1.4, 0.1.7, 1.0.0, 2.1, 2.1.6, 2.1.7) are compatibility fixes for Larastan or PHPStan releases, and 2.1.10 switched from nunomaduro/larastan to larastan/larastan. Pin both packages in composer.json and upgrade them together. A PHPStan minor bump with a stale rule pack produces either crashes or silent no-ops in the rule implementations.

Dev-only, CI-aware install. The package is --dev. Any pipeline step that runs composer install --no-dev and then invokes PHPStan will fail on a missing rules.neon include. Keep static analysis in a job that installs dev dependencies.

Expect false positives on facades in framework-adjacent code. NoFacadeRule also flags real-time facades. In packages, service providers, and test helpers, use parameters.ignoreErrors scoped to specific messages and paths rather than disabling the rule project-wide — a project-wide disable is how these packs quietly get removed six months later.

Debugging a rule that fires unexpectedly. Re-run with vendor/bin/phpstan analyse -v --debug to see which file and rule class is responsible. Because the rule classes are tiny and named after the config key, the error message usually tells you the exact toggle to flip. Isolate with paths pointing at a single file to confirm before changing global config.

Property accessors collide with modern Laravel casts. NoPropertyAccessorRule flags getFooAttribute() definitions and their usage. The idiomatic replacement is Attribute casts or explicit methods, which is genuinely better for static analysis — but the migration is mechanical and touches views and serialization. Budget for it properly; it is the most invasive rule in the pack.

Maturity check. 91 stars, zero known dependents, a single primary maintainer, and a release history dominated by dependency bumps. That does not make the rules bad — they are small, well-scoped, and MIT-licensed — but it does mean you should vendor-pin the version, read the diff before upgrading, and be prepared to disable a rule rather than wait for a fix. Treat this as an opinionated internal lint layer you are adopting, not a critical dependency, and it will serve you well.

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.
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
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