PHPackages                             sandermuller/laravel-fluent-validation-phpstan - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. sandermuller/laravel-fluent-validation-phpstan

ActivePhpstan-extension[Testing &amp; Quality](/categories/testing)

sandermuller/laravel-fluent-validation-phpstan
==============================================

PHPStan rules for sandermuller/laravel-fluent-validation

0.1.0(1mo ago)12.7k↑15.9%MITPHPPHP ^8.2CI passing

Since Apr 22Pushed 3w agoCompare

[ Source](https://github.com/SanderMuller/laravel-fluent-validation-phpstan)[ Packagist](https://packagist.org/packages/sandermuller/laravel-fluent-validation-phpstan)[ Docs](https://github.com/sandermuller/laravel-fluent-validation-phpstan)[ RSS](/packages/sandermuller-laravel-fluent-validation-phpstan/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (16)Versions (3)Used By (0)

Fluent-Validation PHPStan rules
===============================

[](#fluent-validation-phpstan-rules)

[![Latest Version on Packagist](https://camo.githubusercontent.com/1a31d11f7e7aac6c146f17f9c65f458f9647e0af06f933c7de4948faf7344b82/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f73616e6465726d756c6c65722f6c61726176656c2d666c75656e742d76616c69646174696f6e2d7068707374616e2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/sandermuller/laravel-fluent-validation-phpstan)[![Tests](https://camo.githubusercontent.com/45d185f55bb884ff617f8ef9ac79a53cd8bea9aa3c81e55e243a14ebfba14d31/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f73616e6465726d756c6c65722f6c61726176656c2d666c75656e742d76616c69646174696f6e2d7068707374616e2f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/sandermuller/laravel-fluent-validation-phpstan/actions/workflows/run-tests.yml)[![PHPStan](https://camo.githubusercontent.com/9829b324c7fd8dbfef03599fd927d92982b170eb4a487fc346c7e2bd31b638f4/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f73616e6465726d756c6c65722f6c61726176656c2d666c75656e742d76616c69646174696f6e2d7068707374616e2f7068707374616e2e796d6c3f6272616e63683d6d61696e266c6162656c3d7068707374616e267374796c653d666c61742d737175617265)](https://github.com/sandermuller/laravel-fluent-validation-phpstan/actions/workflows/phpstan.yml)[![Total Downloads](https://camo.githubusercontent.com/333286723e35086abc1a92b7373bd4d67fe60560ce7c1422b61e748bc681b214/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f73616e6465726d756c6c65722f6c61726176656c2d666c75656e742d76616c69646174696f6e2d7068707374616e2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/sandermuller/laravel-fluent-validation-phpstan)[![License](https://camo.githubusercontent.com/5722693366b8db5cd10952ca4d458bc527ca549c5fdca4c03570db79f2ea895b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f73616e6465726d756c6c65722f6c61726176656c2d666c75656e742d76616c69646174696f6e2d7068707374616e2e7376673f7374796c653d666c61742d737175617265)](LICENSE)

PHPStan rules that flag misuse of [`sandermuller/laravel-fluent-validation`](https://github.com/sandermuller/laravel-fluent-validation) in consumer projects.

Requirements
------------

[](#requirements)

- PHP 8.2 or higher
- PHPStan 2.1 or higher
- `sandermuller/laravel-fluent-validation` (any version that ships `FluentRule::array()`, `FluentRule::list()`, and `ArrayRule::each/max/between/exactly/rule` — `^1.19` and up)

Compatibility
-------------

[](#compatibility)

`laravel-fluent-validation-phpstan``sandermuller/laravel-fluent-validation``^0.1``^1.19`Installation
------------

[](#installation)

```
composer require --dev sandermuller/laravel-fluent-validation-phpstan
```

With [`phpstan/extension-installer`](https://github.com/phpstan/extension-installer) the rules auto-register. Otherwise include the extension in your `phpstan.neon`:

```
includes:
    - vendor/sandermuller/laravel-fluent-validation-phpstan/extension.neon
```

Rules
-----

[](#rules)

### `NoUnboundedArrayEachRule`

[](#nounboundedarrayeachrule)

Flags `FluentRule::array()->each(...)` (and `FluentRule::list()->each(...)`) chains that have no upper-bound constraint. An unbounded array validated with per-item rules — especially `->exists()`, closure rules, or anything that hits the database — scales linearly with payload size. A caller that sends 10,000 items runs 10,000 per-item checks. Left unchecked, this is a classic N+1 / DoS footgun.

The rule fires on any `->each(...)` call whose receiver is an `ArrayRule` and whose chain has no `->max(N)`, `->between(N, M)`, `->exactly(N)`, and no predefined key whitelist. A rule-string form (`->rule('max:10')`, `->rule(['max', 10])`, `->rule('size:3')`, `->rule('between:1,5')`) also counts as a bound.

```
namespace App\Http\Requests;

use SanderMuller\FluentValidation\FluentRule;

// reported — no upper bound, per-item DB hit runs once per payload entry
FluentRule::array()->each(FluentRule::string()->exists('users', 'id'));

// fine — explicit ceiling on array size
FluentRule::array()->max(100)->each(FluentRule::string()->exists('users', 'id'));

// fine — trailing bound, chain still classified as bounded
FluentRule::array()->each(FluentRule::string()->exists('users', 'id'))->max(100);

// fine — keys constrained at the factory, array shape is bounded
FluentRule::array(['email', 'name'])->each(FluentRule::string());

// fine — rule-string escape hatch
FluentRule::array()->rule('max:50')->each(FluentRule::string());
```

**Identifier:** `sandermuller.fluentValidation.noUnboundedArrayEach` (stable across versions — suppressions written against this identifier will not rot across minor/patch releases).

**Message:**

> `FluentRule::array()->each(...)` (or `FluentRule::list()->each(...)`) has no upper bound. Unbounded arrays combined with per-item rules (e.g. `exists`, closures) can generate large query counts on big payloads. Add `->max(N)`, `->between(N, M)`, `->exactly(N)`, or constrain keys via `FluentRule::array([...])`.

#### Configuration

[](#configuration)

```
parameters:
    fluentValidationNoUnboundedArrayEach:
        namespaces:
            - App            # default
        excludeNamespaces: []
        boundingMethods:
            - max            # defaults
            - between
            - exactly
        boundingRuleStringPrefixes:
            - max            # defaults
            - size
            - between
```

`namespaces` restricts analysis to chains declared in the listed namespace prefixes (matched against `Scope::getNamespace()`, `str_starts_with`-style). `excludeNamespaces` subtracts; useful for bootstrap / response contract code where a raw `each(...)` without a bound is known-safe.

`boundingMethods` lists fluent methods that count as an upper bound when they appear in the chain. Extend this list when you've registered `ArrayRule::macro('boundedBy', ...)` or similar — e.g. add `boundedBy` so chains using your macro are treated as bounded. Values are lowercase-insensitive (PHP method calls are case-insensitive at runtime; the rule matches the same way).

`boundingRuleStringPrefixes` lists Laravel validation-rule prefixes that count as an upper bound when seen inside `->rule(...)` or `->rule([...])` hops. Extend when your codebase uses a bound-equivalent custom prefix (for example, an `in:...` allowlist where the cardinality of the `in` list is itself the cap, or a project-specific `MyRule::cap:N`).

#### Escape hatches

[](#escape-hatches)

**Single line**, when the payload is bounded by something the rule can't see (a paginator cap, an upstream form, a feature-flag check):

```
// @phpstan-ignore-next-line sandermuller.fluentValidation.noUnboundedArrayEach
FluentRule::array()->each(FluentRule::string()->exists('users', 'id'));
```

**Per project**, when a whole subsystem legitimately deals in small, bounded-by-convention arrays:

```
parameters:
    ignoreErrors:
        -
            identifier: sandermuller.fluentValidation.noUnboundedArrayEach
            paths:
                - app/Console/Commands/*
```

PHPStan's default `reportUnmatchedIgnoredErrors: true` will turn stale identifier ignores into errors when the underlying call is removed or bounded — keep that default on, so suppressions don't silently hide real fixes.

#### Known limitations

[](#known-limitations)

- **Helper-method origin.** `$this->makeBuilder()->each(...)` — when the `ArrayRule` comes from a method return, the factory *type* is known but the *chain* that built it isn't visible. The rule skips this case (zero-false-positive policy). If you want the rule to fire, inline the factory call or add the bound at the helper's definition.
- **Macro-registered bounders.** `ArrayRule::macro('boundedBy', fn (int $n) => $this->max($n))` is invisible to static analysis — the rule will flag chains using your custom bounder unless you add its name to `boundingMethods` in your `phpstan.neon` (see Configuration above).
- **Dynamic keys.** `FluentRule::array($keysVar)->each(...)` is only flagged when the chain also lacks a `max`/`between`/`exactly` hop; PHPStan can't prove the dynamic `$keysVar` is non-empty at analysis time.
- **Unknown-origin receivers.** `$this->builder->each(...)` where `$this->builder` is an injected `ArrayRule` property is not flagged. Same rationale as helper-method origin.

### Out of scope

[](#out-of-scope)

- `ArrayRule::children(...)` — children are a fixed set, no unbounded risk.
- Per-item DB-heavy rules specifically (`->exists()`, closures). The MVP flags any unbounded `each(...)` regardless of what's inside, to avoid false negatives. Suppress via identifier ignore for cheap-per-item cases.
- Autofix. There is no unambiguous "correct" bound; the rule stays advisory.

Testing
-------

[](#testing)

```
composer test
```

Before opening a PR, run the full pipeline (Pint, Rector, PHPStan, tests):

```
composer qa
```

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md) for release notes.

Contributing
------------

[](#contributing)

See [CONTRIBUTING.md](CONTRIBUTING.md).

License
-------

[](#license)

MIT. See [LICENSE.md](LICENSE.md).

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance93

Actively maintained with recent releases

Popularity25

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity37

Early-stage or recently created project

 Bus Factor1

Top contributor holds 90% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

48d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/abeb7bd51fd77656b4133bdcdf4eca99cc8b495b83ca72f63674ff15f2b72a39?d=identicon)[SanderMuller](/maintainers/SanderMuller)

---

Top Contributors

[![SanderMuller](https://avatars.githubusercontent.com/u/9074391?v=4)](https://github.com/SanderMuller "SanderMuller (9 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisRector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/sandermuller-laravel-fluent-validation-phpstan/health.svg)

```
[![Health](https://phpackages.com/badges/sandermuller-laravel-fluent-validation-phpstan/health.svg)](https://phpackages.com/packages/sandermuller-laravel-fluent-validation-phpstan)
```

###  Alternatives

[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k51.0M7.4k](/packages/larastan-larastan)[phpstan/phpstan-symfony

Symfony Framework extensions and rules for PHPStan

78973.3M2.0k](/packages/phpstan-phpstan-symfony)[phpstan/phpstan-doctrine

Doctrine extensions for PHPStan

66970.7M1.3k](/packages/phpstan-phpstan-doctrine)[shipmonk/dead-code-detector

Dead code detector to find unused PHP code via PHPStan extension. Can automatically remove dead PHP code. Supports libraries like Symfony, Doctrine, PHPUnit etc. Detects dead cycles. Can detect dead code that is tested.

4773.1M82](/packages/shipmonk-dead-code-detector)[spaze/phpstan-disallowed-calls

PHPStan rules to detect disallowed method &amp; function calls, constant, namespace, attribute, property &amp; superglobal usages, with powerful rules to re-allow a call or a usage in places where it should be allowed.

33321.8M504](/packages/spaze-phpstan-disallowed-calls)[mglaman/phpstan-drupal

Drupal extension and rules for PHPStan

20830.6M166](/packages/mglaman-phpstan-drupal)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
