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

Phpstan Extension Laravel Package

php-standard-library/phpstan-extension

PHPStan extension for the PHP Standard Library (PSL) that teaches PHPStan about PSL types, generics, and collection APIs. It improves type inference, adds custom rules, reduces false positives, and catches PSL-specific errors during static analysis for safer, more reliable PHP code.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require --dev php-standard-library/phpstan-extension phpstan/extension-installer

If you use phpstan/extension-installer (recommended), it auto-registers via the package's extra.phpstan.includes entry — no phpstan.neon change needed. Otherwise, add the include manually:

# phpstan.neon
includes:
    - vendor/php-standard-library/phpstan-extension/extension.neon

Where to look first: the extension.neon shipped in the package tells you which services/stubs it registers (return type extensions, stub files, rules). Cross-reference that with the Psl API surface you actually use (Psl\Type, Psl\Vec, Psl\Dict, Psl\Str, Psl\Json).

First use case: take a snippet like this and run vendor/bin/phpstan analyse --level max:

use Psl\Type;

$value = Type\int()->assert($input);   // was `mixed`, now `int`
$list  = \Psl\Vec\map($items, fn(int $i) => $i * 2); // inferred `list<int>`

Before the extension, $value is mixed and $list is list<mixed>; after it, you get concrete types and downstream errors surface. That delta is the whole point of installing it.

Implementation Patterns

Type-object-first APIs. The biggest win is Psl\Type\TypeInterface<T> — its assert()/coerce() return type is what the extension narrows. Structure boundary code (HTTP input, JSON decoding, queue payloads) around Type\int(), Type\shape([...]), Type\union(...), and let the extension hand you back typed values instead of forcing casts and assert() blocks later.

$shape = Type\shape([
    'id'   => Type\positive_int(),
    'tags' => Type\vec(Type\string()),
]);
$payload = $shape->assert($decoded); // array{id: positive-int, tags: list<string>}

Lean on generics for collection helpers. Psl\Vec\map, Psl\Dict\map, Psl\Iter\filter, Psl\Vec\filter get @template annotations from the extension + Psl stubs. This means fn(int $x): string => ... propagates list<string>. Prefer these over array_map/array_filter when you want PHPStan to keep track of key/value types.

Keep analysis level high. Most of the extension's value is lost at level 0–2. Level 6+ (or --level max) is where narrow return types from assert() actually change reported errors.

CI integration. Add vendor/bin/phpstan analyse --no-progress --error-format=github to your pipeline, and commit phpstan.neon with level: max for the new code paths that touch Psl, even if legacy parts sit at a lower level via paths/excludePaths.

Wrap with your own helpers if you need stability. If you plan to swap Psl out later, wrap Type\int()->assert(...) behind a thin Validator::int() facade annotated with @phpstan-assert — then the extension's inference survives as your abstraction, not a vendor lock-in.

Pair with phpstan/extension-installer and phpstan-strict-rules. Order rarely matters, but extension-installer's alphabetical loading is predictable; list any manual includes after it so your overrides win.

Gotchas and Tips

  • Version alignment is the #1 failure mode. The extension's return-type extensions are written against a specific Psl major and a specific PHPStan major. Pinning php-standard-library/phpstan-extension:^X while running PHPStan 2.x against a Psl 3.x install frequently degrades to mixed silently. Check composer show azjezz/psl php-standard-library/phpstan-extension phpstan/phpstan together before debugging anything else.
  • Double registration. If you both install phpstan/extension-installer and hand-add extension.neon, you can get duplicate service registrations or confusing precedence. Pick one.
  • Stubs vs. runtime. Inference depends on Psl's own @template/@param-out stub files being visible to PHPStan. Don't add vendor/azjezz/psl to excludePaths, and don't strip vendor/ in scanDirectories.
  • Baselines hide regressions. If you generate a baseline before installing this, regenerate it afterward — narrowed types will surface new errors, and old baseline entries will now be stale (and sometimes mask the new inferred type entirely).
  • mixed fallbacks are the signal. If a call you expect to be typed returns mixed, first confirm the extension registered (phpstan --debug shows loaded configs), then confirm the Psl version supports that function — newer Psl APIs may be stubbed only in newer extension releases.
  • Debugging tips. Use \PHPStan\dumpType($x); in a method body (PHPStan 2.x) to inspect the inferred type, and --debug to see which extends/rules files loaded. If the extension isn't loading at all, phpstan analyse --debug will list only your project config.
  • assert() vs coerce() differ subtly. assert() preserves narrowing (mixed → T); coerce() can widen or transform. Prefer assert() at boundaries where you want the type-checker to prove correctness, and coerce() only when you genuinely want normalization, or you lose the strictness win.
  • Extension points. The extension is plain PHPStan DynamicReturnTypeExtension + DynamicStaticMethodReturnTypeExtension registrations — if you build your own library returning Psl\Type\TypeInterface<T>, mimic its patterns (register in your own extension.neon) rather than duplicating logic.
  • Rules vs. types. If the package also ships custom rules (e.g. for discouraging array_* where a Psl equivalent exists), they're opt-in via rules: in phpstan.neon — enabling them on a large existing codebase will produce noise; adopt them per-directory first.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
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
cgv/localized-resource