PHPackages                             respect/fluent-analysis - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. respect/fluent-analysis

ActivePhpstan-extension[Utility &amp; Helpers](/categories/utility)

respect/fluent-analysis
=======================

PHPStan extension for Respect/Fluent builder method resolution

2.0.1(3mo ago)286↓90%1ISCPHPPHP ^8.5CI passing

Since Mar 25Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/Respect/FluentAnalysis)[ Packagist](https://packagist.org/packages/respect/fluent-analysis)[ RSS](/packages/respect-fluent-analysis/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (3)Dependencies (10)Versions (4)Used By (0)

Respect\\FluentAnalysis
=======================

[](#respectfluentanalysis)

PHPStan extension for [Respect/Fluent](https://github.com/Respect/Fluent) builders. Provides method resolution, parameter validation, tuple-typed `getNodes()`, and type narrowing through assertion methods.

Fluent builders use `__call` to resolve method names to class instances. Since those methods don't exist as real declarations, PHPStan reports errors and can't validate arguments. This extension teaches PHPStan what each method does: its parameters, return type, and the exact tuple of accumulated nodes.

```
$stack = Middleware::cors('*')->rateLimit(100);

// PHPStan knows:
//   cors()      accepts (string $origin = '*')
//   rateLimit() accepts (int $maxRequests = 60)
//   getNodes()  returns array{Cors, RateLimit}
//   $stack      is Middleware

$stack->getNodes()[0]; // PHPStan infers: Cors
$stack->typo();        // PHPStan error: method not found
$stack->cors(42);      // PHPStan error: int given, string expected
```

Installation
------------

[](#installation)

```
composer require --dev respect/fluent-analysis
```

Requires PHP 8.5+ and PHPStan 2.1+.

Setup
-----

[](#setup)

Libraries that ship a Fluent builder declare it in their `fluent.neon`:

```
parameters:
    fluent:
        builders:
            - builder: App\MiddlewareStack
```

The extension loads automatically via [phpstan/extension-installer](https://github.com/phpstan/extension-installer). Method maps are built from `#[FluentNamespace]` attributes at PHPStan boot.

### Adding custom namespaces

[](#adding-custom-namespaces)

To add extra node namespaces to an existing builder (e.g. custom validators):

```
parameters:
    fluent:
        builders:
            - builder: Respect\Validation\ValidatorBuilder
              namespace: App\Validators
```

Entries from multiple neon files are merged automatically. Each package, extension, or user project can append entries independently.

### Generating config for new projects

[](#generating-config-for-new-projects)

For projects that define their own `#[FluentNamespace]` builders:

```
vendor/bin/fluent-analysis generate
```

This scans your `composer.json` autoload entries for builder classes and writes a `fluent.neon` with the builder list and service registrations.

Features
--------

[](#features)

### Method resolution

[](#method-resolution)

Every method on your builder is resolved to its target class. PHPStan reports unknown methods as errors: typos are caught at analysis time.

### Constructor parameter forwarding

[](#constructor-parameter-forwarding)

Method parameters come from the target class constructor. If `Cors` has `__construct(string $origin = '*')`, then `->cors()` accepts the same signature. Type mismatches are reported.

### Tuple-typed `getNodes()`

[](#tuple-typed-getnodes)

The extension tracks which node types are accumulated through the chain. `getNodes()` returns a precise tuple instead of `array`:

```
$builder = new MiddlewareStack();
$chain = $builder->cors('*')->rateLimit(100)->auth('bearer');

// PHPStan knows: array{Cors, RateLimit, Auth}
$nodes = $chain->getNodes();

// Individual elements are typed
$nodes[0]; // Cors
$nodes[1]; // RateLimit
$nodes[2]; // Auth
```

Tuple tracking works through variable assignments and static calls:

```
$a = MiddlewareStack::cors('*');
$b = $a->rateLimit(100);
$b->getNodes(); // array{Cors, RateLimit}
```

### Deprecation forwarding

[](#deprecation-forwarding)

If a target class is marked `@deprecated`, the fluent method inherits the deprecation. PHPStan reports it wherever the method is called.

### Composable prefix support

[](#composable-prefix-support)

For builders using Respect/Fluent's composable prefixes (like Validation's `notEmail()`, `nullOrStringType()`), the extension resolves composed methods with correct parameter signatures.

### Type narrowing

[](#type-narrowing)

Builders can narrow the type of a variable through assertion methods. Node classes declare their assurance via the `#[Assurance]` attribute, assertion methods are marked with `#[AssuranceAssertion]`, and `#[AssuranceParameter]`identifies the validated parameter and constructor parameters used for type resolution.

Void assertion methods narrow unconditionally:

```
$builder->intNode()->doAssert($x);
// PHPStan now knows $x is int
```

Bool assertion methods work as type guards:

```
if ($builder->intNode()->isOk($x)) {
    // $x is int here
}
// $x is not int here
```

Chained nodes intersect their assurances:

```
$builder->intNode()->numericNode()->doAssert($x);
// int ∩ (int|float|numeric-string) = int
```

The extension supports several assurance modes through the `#[Assurance]`attribute:

- **`type`** — a fixed type string (e.g. `int`, `float|int|numeric-string`)
- **`#[AssuranceParameter]`** — the type is taken from a constructor parameter annotated with the attribute (e.g. a class-string parameter)
- **`from: value`** — narrows to the argument's literal type
- **`from: member`** — narrows to the iterable value type of the argument
- **`from: elements`** — narrows to an array of the inner assurance type
- **`compose: union|intersect`** — combines assurances from multiple builder arguments

How it works
------------

[](#how-it-works)

The extension registers three PHPStan hooks:

1. **`FluentMethodsExtension`** (`MethodsClassReflectionExtension`) — tells PHPStan which methods exist on each builder, with parameters extracted from the target class constructor.
2. **`FluentDynamicReturnTypeExtension`** (`DynamicMethodReturnTypeExtension` + `DynamicStaticMethodReturnTypeExtension`) — intercepts each method call to track accumulated node types as a `GenericObjectType` wrapping a `ConstantArrayType` tuple. When `getNodes()` is called, the tuple is returned directly. Also accumulates assurance types through the chain.
3. **`FluentTypeSpecifyingExtension`** (`MethodTypeSpecifyingExtension`) — enables type narrowing in control flow. When a builder's assertion method is called, accumulated assurances are applied to narrow the input variable's type. Supports void assertions (unconditional) and bool guards (conditional).

The extensions share a `MethodMap` for method resolution and an `AssuranceMap`for type narrowing configuration, both with parent-class fallback for builder inheritance.

At PHPStan boot, `MethodMapFactory` reads the `builders` parameter, reflects each builder's `#[FluentNamespace]` attribute, discovers classes in the declared namespaces, and builds the method/assurance maps. Extra namespaces from user config are merged via `withNamespace()`.

FluentAnalysis vs FluentGen
---------------------------

[](#fluentanalysis-vs-fluentgen)

Another similar project is [FluentGen](https://github.com/Respect/FluentGen).

Both are complementary, offering IDE support and type inference as separate packages.

FluentAnalysisFluentGenGenerated filesNoneInterface files per builder + prefixReturn type`Builder``Builder` (via `@mixin`)`getNodes()` type`array{A, B, C}` (exact tuple)`array` (generic)Element access`$nodes[0]` typed as `A``mixed`DeprecationForwarded automaticallyMust regenerateComposable prefixesResolved from attributeFull method signaturesType narrowingAssertion methods narrow input typesNot supportedIDE supportPHPStan-powered (PhpStorm, VS Code)Direct IDE autocomplete

###  Health Score

42

—

FairBetter than 89% of packages

Maintenance82

Actively maintained with recent releases

Popularity13

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% 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

Every ~0 days

Total

3

Last Release

90d ago

Major Versions

1.0.0 → 2.0.02026-03-26

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/202642?v=4)[Alexandre Gomes Gaigalas](/maintainers/alganet)[@alganet](https://github.com/alganet)

---

Top Contributors

[![alganet](https://avatars.githubusercontent.com/u/202642?v=4)](https://github.com/alganet "alganet (4 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/respect-fluent-analysis/health.svg)

```
[![Health](https://phpackages.com/badges/respect-fluent-analysis/health.svg)](https://phpackages.com/packages/respect-fluent-analysis)
```

###  Alternatives

[friendsoftypo3/content-blocks

TYPO3 CMS Content Blocks - Content Types API | Define reusable components via YAML

101466.4k45](/packages/friendsoftypo3-content-blocks)[php-soap/wsdl

Deals with WSDLs

183.8M18](/packages/php-soap-wsdl)

PHPackages © 2026

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