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

script-development/phpstan-warroom-rules

Custom PHPStan rules for Laravel projects that catch script-development conventions and common pitfalls. Drop-in config, clear error messages, and fast static analysis for CI pipelines.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. This is a PHPStan rule extension for Laravel codebases, distributed as a dev-only Composer package — no runtime footprint, no production dependency, no migration surface in the application itself. That makes it an unusually cheap thing to adopt from an architecture standpoint. More importantly, the design decisions in this release are the ones a TPM should care about: detection is shape-keyed (left-hand call resolving to a first-party namespace, a required mixed parameter, a nullable-scalar return) rather than maintained against a helper-name inventory. That removes the perennial "who updates the list when a helper is renamed" maintenance tax and makes the rule's behaviour predictable across territories. Configuration is a single narrowingHelperNamespacePrefixes list (default ['App']) matched on namespace boundaries, so per-territory namespace roots are tunable without forking. The stated ADR-0021 posture — false negatives acceptable, false positives are not — is the correct bias for a rule that gates merges, and the release is explicit about where it trades coverage for precision.

Integration feasibility. Low effort, medium coordination. Adoption is a single ^0.11 pin bump plus CI green, with the escape hatch being an inline @phpstan-ignore naming the rule identifier. There is no data migration, no API contract change, no deploy step. The real work is remediation of newly-surfaced findings, not integration.

Technical risk. The central risk is that this is a 0.x MINOR that is consumer-impacting: ^0.10 excludes it, so nobody is auto-adopted, but every territory that bumps will get new errors in previously clean code. That is desirable (real bugs were found: a sentinel-seeded array that unlocked a forceDelete(), a gender persisted as an empty SET, and a missing node parsed as now() written as a date of birth) but it means a pin bump is a remediation project, not a version bump. Secondary risks:

  • Residual false positive is known and shipped. A first-party lookup helper with a required mixed key returning a nullable scalar is indistinguishable from a boundary reader by signature. The per_page default hits in lokalekeuze are flagged in the notes as likely this shape. Unmanaged, this converts the rule into ignore-noise.
  • Silent rules create false confidence. ForbidCredentialDerivedCacheKeyRule is silent on a consumer with no encrypted cast. A clean run there is not evidence the rule works — the notes explicitly require a planted credential-keyed cache as a positive control. That is an operational requirement, not a nice-to-have.
  • Deliberate misses bound the guarantee. Non-literal fallbacks, helper results laundered through a variable, and — critically — the motivating date-of-birth bug (a ?? null whose null is swallowed one call later by Carbon::parse) are explicitly out of scope. A green build must not be read as "data-integrity verified."
  • Coupling to PHPStan internals. The rule uses reflection and the Type API (TypeCombinator, per-member scalar probes, union-receiver branch resolution, parent::casts() override order). Correct, but it means PHPStan upgrades are a compatibility event for this package.
  • The release notes as supplied are truncated mid-sentence in the ForbidCredentialDerivedCacheKeyRule / CacheKeyTaintCollector description, so that rule's full behaviour and its consumer audit demands are not yet assessable.

Key questions.

  1. What is the authoritative consumer inventory, and is the fleet-scan date (2026-09-23) the baseline we plan remediation against?
  2. Does every territory's first-party boundary code actually live under App, or do any need a custom narrowingHelperNamespacePrefixes?
  3. What is our policy on inline ignores — tracked with an owner and expiry, or ad hoc?
  4. Is a planted positive control part of each consumer's CI for the credential-cache and cast-bypass rules?
  5. Which PHPStan version range does v0.11.0 support, and what is the upgrade path if we pin PHPStan forward?
  6. Who owns the transition to 1.0, given this release is self-described as a "candidate MAJOR"?

Integration Approach

Stack fit. The package sits in the existing PHPStan/CI lane of a Laravel stack. It requires a Composer dev dependency, a PHPStan config include, and — where non-default — the namespace-prefix parameter. Nothing else in the stack is touched.

Migration path. Per-territory, not fleet-wide. The pre-1.0 caret convention is the migration mechanism: each repository opens its own ^0.11 pin-bump PR, absorbs the new errors, remediates, and goes green on its own schedule. Remediation is a closed set of three patterns — skip the write, fail closed (throw), or handle null explicitly (?? null and pass it on) — plus the documented escape hatch for genuine lookup helpers. That is learnable in a single review cycle.

Compatibility. Two compatibility obligations to encode now: (a) ignore identifiers are part of consumer config, so any upstream rename of forbidSentinelFallbackOnNarrowingHelper.sentinelFallback or forbidCredentialDerivedCacheKey.keyFromEncryptedAttribute breaks baselines and must be treated as a breaking change; (b) because every 0.x minor is consumer-impacting by convention, each future bump carries the same audit cost — budget for recurrence, not a one-off.

Sequencing.

  1. tc-api first — it is the seed, already remediated, and validates the rule against known-true positives.
  2. emmie (8 hits) and lokalekeuze (6 hits) next, in parallel. Expect emmie's to be genuine (clustered in FormatLogbookEntriesAction, including a CarbonImmutable::parse(… ?? 'now') shape) and lokalekeuze's to be partly the known lookup false positive, split between real fixes and documented inline ignores with rationale.
  3. The ten clean territories last, and only with a positive control planted so "0 findings" is evidence rather than a null result.
  4. Gate: green-with-control becomes the merge requirement; baseline files drained rather than ratcheted.

Operational Impact

Maintenance. Modest but recurring. Each 0.x bump is a per-territory audit against a single documented identifier, plus config upkeep for namespace prefixes. The shape-keyed design means no helper inventory to maintain — the largest ongoing cost is ignore hygiene: every inline suppression needs an owner and a reason or it becomes permanent debt.

Support. Two support surfaces. Internally: engineers hitting a new error need a decision tree (is this a real sentinel? a lookup? → fix, or ignore with justification) and reviewers need to be able to tell a legitimate escape hatch from a silenced bug. Externally: the residual false positive is a known upstream limitation, so there should be a clear channel for reporting shapes that match by signature but aren't boundary readers.

Scaling. Cost is roughly linear in territories (one PR each) and constant in rule complexity, which is favourable. The scaling concern is CI time and signal quality: the per-node cost is deliberately cheap on non-matching expressions (one instanceof), but the rule set is growing, and every false positive that gets ignored rather than fixed erodes the rule's authority across all twelve-plus territories.

Failure modes.

  • Pin-bump blockage: a territory opens the bump, the pipeline turns red with unfamiliar errors, and the PR stalls. Mitigation: remediation playbook written before the first bump.
  • Suppression creep: the residual false positive drives a norm of inline ignores, and genuine findings get ignored alongside them. Mitigation: require the identifier plus a one-line justification in every ignore.
  • False confidence: a clean run on a consumer with no encrypted cast, or on a codebase whose boundary helpers don't match the shape, reads as "we're safe." Mitigation: positive controls in CI, and explicit communication that the out-of-scope cases (sink-swallowed null, laundered variables, non-literal fallbacks) are not covered by a green build.
  • Silent misconfiguration: a wrong namespace prefix means the rule scans nothing and reports success. Mitigation: verify a known hit fires in each territory before trusting a clean result.
  • PHPStan coupling: an upgrade breaks reflection assumptions. Mitigation: pin PHPStan alongside this package and treat upgrades as coordinated changes.

Ramp-up. Low for individuals (three remediation patterns, one config knob, one ignore syntax), moderate for the organisation — the habit to build is "a new static-analysis error is a finding to triage, not a blocker to suppress," and the discipline to build is positive-control verification so clean results mean something.

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.
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
noith/settings