calebdw/phpstan-laravel
A PHPStan extension for Laravel that teaches static analysis about Laravel’s magic, including facades, helpers, Eloquent models, and container bindings, reducing false positives and surfacing more accurate type errors in Laravel apps.
Install the extension as a dev dependency alongside PHPStan:
composer require --dev calebdw/phpstan-laravel phpstan/phpstan
Register it in your phpstan.neon (or phpstan.neon.dist):
includes:
- vendor/calebdw/phpstan-laravel/extension.neon
parameters:
level: 5
paths:
- app
- routes
- config
If the package ships a bootstrap helper, point PHPStan at a Laravel-aware bootstrap so the container, facades, and config are available during analysis:
parameters:
bootstrapFiles:
- bootstrap/phpstan.php
That bootstrap should boot the application (or at least register the service providers and aliases the extension needs) without hitting the database or external services.
Where to look first:
includes path and any required bootstrap convention — extension names and paths differ between releases.vendor/calebdw/phpstan-laravel/extension.neon to see what rules and services are registered.rules.neon, config.neon, or stubs directory for Laravel-specific type definitions.First use case: run analysis on the smallest meaningful slice before widening scope.
vendor/bin/phpstan analyse app/Models app/Http/Controllers --memory-limit=1G
Start by confirming that facade calls, Eloquent model properties, and app()/container resolutions produce useful types. If they do, the extension is wired correctly; if everything is mixed, the bootstrap or includes are usually the problem.
Adopt incrementally with a baseline. Run at a low level on a small path, generate a baseline, then raise the level and shrink the baseline over time:
vendor/bin/phpstan analyse --generate-baseline
Scope analysis per directory in monorepos. Laravel applications and packages have different bootstrapping needs. Use separate config files:
# packages/billing/phpstan.neon
includes:
- ../../vendor/calebdw/phpstan-laravel/extension.neon
parameters:
level: 6
paths:
- src
- tests
bootstrapFiles:
- tests/phpstan-bootstrap.php
Then run vendor/bin/phpstan analyse -c packages/billing/phpstan.neon.
Use it in CI with a fixed PHPStan version and cache warmup. A typical job:
- run: composer install --no-interaction --prefer-dist
- run: vendor/bin/phpstan analyse --no-progress --error-format=github
Pin phpstan/phpstan and the extension to compatible major versions in composer.json; PHPStan extensions are tightly coupled to PHPStan internals.
Pair static analysis with Laravel-specific tests. The extension catches type errors, but not runtime container misconfigurations. Keep feature tests for bindings and use PHPStan to catch signatures, nullability, and dead code around them.
Layer custom rules on top. The extension handles framework typing; project conventions (e.g., “controllers must not call repositories directly”) belong in custom PHPStan\Rules\Rule implementations registered in your own neon file. This keeps framework noise separate from domain rules.
Use it for package development. For a Laravel package, analyze src/ with the package’s own service provider registered in a test bootstrap. This catches missing illuminate/* dependencies and incorrect facade/container assumptions that only appear when the package is installed in a host app.
Combine with strict parameters deliberately. Enable checkMissingIterableValueType, checkGenericClassInNonGenericObjectType, and treatPhpDocTypesAsCertain only after the codebase is clean at the current level. Turning them on early produces a wall of framework-related errors that obscures real issues.
Auth::user() or app(Foo::class) resolves to mixed, the extension likely isn’t seeing your providers. Fix the bootstrap before adding ignores.--memory-limit=1G or higher, enable resultCachePath, and avoid analyzing storage/, bootstrap/cache/, and generated files.phpstan.neon, the bootstrap, or the extension version, clear tmpDir/result cache before debugging further.extension.neon errors at startup, not as analysis errors. Check composer why-not phpstan/phpstan when the extension won’t load.$appends may still need @property annotations. Use model PHPDoc as the source of truth where inference falls short.Builder::macro, custom collection macros, and dynamic facade methods generally require stubs or @method annotations. Add a small stub file and include it via scanFiles rather than suppressing errors globally._ide_helper.php and _ide_helper_models.php can conflict with extension-provided stubs or duplicate symbol definitions. Exclude them from paths and scanDirectories unless the extension explicitly expects them.config('foo.bar') often returns mixed; use Config::get() with a cast or a typed config class when you need precise types. Don’t expect the extension to validate config keys.stubFiles), and a project bootstrap are the supported levers. If you need new framework inference, check whether the package exposes a service you can decorate before forking it.How can I help you explore Laravel packages today?