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 Rules Laravel Package

oro/phpstan-rules

Oro PHPStan Rules adds custom static analysis checks to PHPStan for Laravel and PHP projects, helping teams catch risky patterns, enforce coding standards, and improve code quality early in CI. Lightweight and easy to integrate.

View on GitHub
Deep Wiki
Context7

Getting Started

oro/phpstan-rules is a small, single-purpose package: a bundle of custom PHPStan Rule implementations that encode Oro's house conventions. It is not a general-purpose ruleset like phpstan-strict-rules — treat it as an opinionated extension you adopt because you are working in an Oro-derived codebase, or as a working reference for writing your own rules.

Read these three things before installing anything:

  1. composer.json — check the phpstan/phpstan constraint. Last release was 2024-10-22, which predates PHPStan 2.x. If it pins ^1.x, you cannot use it on a PHPStan 2 project without a fork or --ignore-platform-req gymnastics.
  2. rules.neon (the package's own NEON config) — this is the authoritative list of which rules are registered and with what constructor arguments. Rule classes are useless until they are wired here.
  3. src/ — one class per rule. Each getNodeType() tells you which AST node it inspects, and each processNode() shows exactly what it forbids.

Minimal install (Symfony/Oro-style project, PHPStan already present):

composer require --dev oro/phpstan-rules

Then in phpstan.neon:

includes:
    - vendor/oro/phpstan-rules/rules.neon

If you have phpstan/extension-installer in allow-plugins, the includes: line is usually unnecessary — the installer auto-registers any package shipping a rules.neon. Verify with vendor/bin/phpstan analyse -c phpstan.neon --debug and look for the rules in the bootstrap output.

First use case: do not turn it on repo-wide. Point it at one module and one level, and read the diff:

vendor/bin/phpstan analyse src/Module/Catalog --level=5 --memory-limit=1G

For a Laravel app, the same command works, but scope paths: tightly — see the gotchas below.

Implementation Patterns

Baseline-and-ratchet. The package's rules are conventions, not bug detectors, so a legacy codebase will light up immediately. Generate a baseline once, commit it, and forbid new violations:

vendor/bin/phpstan analyse --generate-baseline=phpstan-baseline.neon
includes:
    - vendor/oro/phpstan-rules/rules.neon
    - phpstan-baseline.neon
parameters:
    level: 6
    reportUnmatchedIgnoredErrors: true

Keep reportUnmatchedIgnoredErrors: true — with a generated baseline it is tempting to disable, but leaving it on is what forces you to delete stale baseline entries as you refactor, which is the entire point of the ratchet.

Layer configs per bounded context. PHPStan merges includes: arrays, so you can have a root phpstan.neon that includes the Oro rules plus per-module phpstan.neon files with narrower paths: and higher level:. This lets one mature module run at level 8 with all rules while a legacy module stays at 5.

Use it alongside Larastan, not instead of it. The two solve different problems:

includes:
    - vendor/larastan/larastan/extension.neon
    - vendor/oro/phpstan-rules/rules.neon
parameters:
    paths: [app/]
    excludePaths:
        - app/Console/Kernel.php
        - app/Exceptions/Handler.php

Larastan gives you Eloquent/container/facade type inference (the thing that actually catches bugs in a Laravel app); the Oro rules add convention checks on top.

CI wiring. Cache the analysis result and parallelise — custom rules are AST visitors and are not free:

- run: vendor/bin/phpstan analyse --configuration=phpstan.neon --no-progress --error-format=github

--error-format=github gives inline PR annotations; --error-format=json is what you want if you are building a custom diff-based "only fail on new errors" gate.

Use it as a template for in-house rules. This is where a Laravel team extracts the most value. The package demonstrates the whole shape end to end: a Rule class, RuleErrorBuilder with an identifier(), and a NEON services: block tagged phpstan.rules.rule. Copy that skeleton to enforce your conventions — "no DB:: in controllers", "every Job implements ShouldQueue", "no env() outside config/" — which is the classic Laravel equivalent of what Oro is doing here.

Suppress precisely, by identifier. Once a rule emits ->identifier('oro.someRule'), ignore with the identifier rather than a message regex — message strings change between releases and regex ignores silently rot.

Gotchas and Tips

The PHPStan 2.x wall. This is the first thing to check and the most likely reason to walk away. Custom rules depend on PHPStan's internal AST and RuleErrorBuilder API, which had breaking changes in 2.0. If the package's constraint is ^1.x and you are on PHPStan 2, you will get either a dependency conflict or runtime errors from rules extending removed/renamed internals. Options: pin PHPStan to 1.x for that project, or fork and bump — forks are usually a handful of lines because the rules are small.

Class-not-found and type noise in Laravel. Rules written for an Oro codebase may reference Doctrine or Symfony types in docblocks and instanceof checks. In a pure Laravel app you may see "Class not found" or "Call to an undefined method" leaking from the rules' own assumptions. Scope paths: to app/ and add excludePaths: for bootstrap/cache, storage, database/migrations and generated files. Also set scanDirectories/scanFiles only if you actually need cross-package analysis — it is a large performance cost.

Configuration changes vs. cache. PHPStan caches aggressively in tmpDir (default %currentWorkingDirectory%/tmp or the system temp dir). Most config changes invalidate it correctly, but when a newly registered rule does not appear to run, rm -rf tmp/ (or your configured tmpDir) before debugging anything else. Then use --debug to see the loaded rule list and per-file timings.

Performance. Custom rules typically run as full AST visitors over every file. Two mitigations: increase parallel.maximumNumberOfProcesses (leave one core for the DB/IDE), and keep level honest — level 9 with three custom rulesets on a large app can push past the default memory limit. Pass --memory-limit=2G explicitly rather than relying on ini defaults.

--debug disables parallelism. Easy to miss: adding --debug to a slow CI run makes it dramatically slower, because it forces sequential analysis. Use it locally only.

Rules that inspect service definitions need the container. If any rule in the package reasons about DI wiring or tagged services, it depends on PHPStan's Symfony/container extensions being loaded and on bootstrapFiles having run. In a Laravel context those extensions do nothing, so such a rule is a no-op at best. Check the NEON file for services: entries that reference Symfony extension types before assuming the rule is active.

Pin and update in lockstep. Whatever you do, pin phpstan/phpstan to an exact minor (1.12.*) and bump it deliberately. A patch-level PHPStan upgrade that changes an internal API will break custom rules in ways that surface as fatal errors mid-analysis, not as a clean composer conflict. Add vendor/bin/phpstan analyse to CI on the lowest supported PHP version too — rules often rely on reflection over typed properties, and behaviour differs across PHP 8.1/8.2/8.3.

Evaluate the alternative first. With 13 stars and zero dependents, this package has essentially no community surface — no issue triage, no compatibility guarantee with your PHPStan version, no docs beyond the source. Before adopting, confirm that phpstan/phpstan-strict-rules plus a dozen lines of your own NEON does not already cover the conventions you care about. The honest value here is 20% "these specific rules" and 80% "a clean worked example of packaging PHPStan rules", and the latter you can get by reading the repo for ten minutes without depending on it.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core