PHPackages                             respect/fluent - 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

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

respect/fluent
==============

Namespace-aware fluent class resolution

2.0.1(2mo ago)1310↓90.8%1[2 PRs](https://github.com/Respect/Fluent/pulls)2ISCPHPPHP ^8.5CI passing

Since Mar 23Pushed 4w ago1 watchersCompare

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

READMEChangelog (3)Dependencies (12)Versions (7)Used By (2)

Respect\\Fluent
===============

[](#respectfluent)

Build fluent interfaces from class namespaces. PHP 8.5+, zero dependencies.

Fluent maps method calls to class instances. You define classes in a namespace, extend `FluentBuilder`, and get a chainable API where each call resolves a class name, instantiates it, and accumulates it immutably.

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

$stack->getNodes(); // [Cors('*'), RateLimit(100), Auth('bearer'), JsonBody()]
```

Middlewares, validators, processors: anything that composes well as a chain can leverage Respect/Fluent.

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

[](#installation)

```
composer require respect/fluent
```

Quick Start
-----------

[](#quick-start)

### 1. Choose a namespace and interface

[](#1-choose-a-namespace-and-interface)

Fluent discovers classes from one or more namespaces. Giving them a shared interface lets your builder enforce type safety and expose domain methods.

```
namespace App\Middleware;

interface Middleware
{
    public function process(Request $request, Handler $next): Response;
}

final readonly class Cors implements Middleware
{
    public function __construct(private string $origin = '*') {}
    public function process(Request $request, Handler $next): Response { /* ... */ }
}

final readonly class RateLimit implements Middleware
{
    public function __construct(private int $maxRequests = 60) {}
    public function process(Request $request, Handler $next): Response { /* ... */ }
}
// etc...
```

### 2. Extend FluentBuilder

[](#2-extend-fluentbuilder)

The `#[FluentNamespace]` attribute declares where your classes live and how to resolve them. The builder inherits `__call`, immutable accumulation, and `withNamespace` support, you only add domain logic:

```
namespace App;

use Respect\Fluent\Attributes\FluentNamespace;
use Respect\Fluent\Builders\Append;
use Respect\Fluent\Factories\NamespaceLookup;
use Respect\Fluent\Resolvers\Ucfirst;
use App\Middleware\Middleware;

#[FluentNamespace(new NamespaceLookup(new Ucfirst(), Middleware::class, 'App\\Middleware'))]
final readonly class MiddlewareStack extends Append
{
    public function __construct(Middleware ...$layers)
    {
        parent::__construct(static::factoryFromAttribute(), ...$layers);
    }

    /** @return array */
    public function layers(): array
    {
        return $this->getNodes();
    }
}
```

The attribute carries the full factory configuration: the resolver (`Ucfirst`), optional type constraint (`Middleware::class`), and namespace to search. The inherited `factoryFromAttribute()` reads it at runtime so there's a single source of truth.

Now `MiddlewareStack::cors()->auth('bearer')->jsonBody()` builds the layers for you.

### 3. Add composition if you want

[](#3-add-composition-if-you-want)

Prefix composition lets `optionalAuth()` create `Optional(Auth())`. You're not limited to `Optional` cases, you can design nesting as deep as you want.

Annotate wrapper classes with `#[Composable]`:

```
namespace App\Middleware;

use Respect\Fluent\Attributes\Composable;

#[Composable(self::class)]
final readonly class Optional implements Middleware
{
    public function __construct(private Middleware $inner) {}

    public function process(Request $request, Handler $next): Response
    {
        // Skip the middleware if a condition is met
        return $this->shouldSkip($request)
            ? $next($request)
            : $this->inner->process($request, $next);
    }
}
```

Then switch the attribute to use `ComposingLookup`, it automatically discovers `#[Composable]` prefixes from the same namespace:

```
use Respect\Fluent\Factories\ComposingLookup;

#[FluentNamespace(new ComposingLookup(
    new NamespaceLookup(new Ucfirst(), Middleware::class, 'App\\Middleware'),
))]
final readonly class MiddlewareStack extends Append { /* ... */ }
```

Now `MiddlewareStack::optionalAuth('bearer')` creates `Optional(Auth('bearer'))`.

### 4. Add custom namespaces

[](#4-add-custom-namespaces)

Users can extend your middleware stack with their own classes. `withNamespace` is inherited from `FluentBuilder`:

```
$stack = MiddlewareStack::cors();
$extended = $stack->withNamespace('MyApp\\CustomMiddleware');
$extended->logging();  // Finds MyApp\CustomMiddleware\Logging
```

How It Works
------------

[](#how-it-works)

Fluent has three layers:

- **Resolvers** transform method names before lookup (e.g., `'email'` → `'Email'`, or `'notEmail'` → wrapper `'Not'` + inner `'Email'`).
- **Factories** search namespaces for the resolved class name and instantiate it.
- **Builders** (`Append`, `Prepend`) chain factory calls immutably via `__call`.

Resolved classes are called **nodes** because consumer libraries (like Respect/Validation) often arrange them into tree structures.

A **FluentNode** carries the resolution state between resolvers and factories: a name, constructor arguments, and an optional wrapper.

```
                         +----------+
  'notEmail' -------->   | Resolver |  ------>  FluentNode('Email', wrapper: FluentNode('Not'))
                         +----------+
                              |
                              v
                         +----------+
  FluentNode --------->  | Factory  |  ------>  Not(Email())
                         +----------+

```

**NamespaceLookup vs ComposingLookup:** use `NamespaceLookup` for simple name-to-class mapping. Wrap it with `ComposingLookup` when you need prefix composition like `notEmail()` → `Not(Email())`. `ComposingLookup` supports recursive unwrapping, so `notNullOrEmail()` → `Not(NullOr(Email()))` works too.

Assurance attributes
--------------------

[](#assurance-attributes)

Node classes can declare what they assure about their input via `#[Assurance]`. Assertion methods are marked with `#[AssuranceAssertion]`, and `#[AssuranceParameter]`identifies specific parameters. Constructor parameters for composition use `#[ComposableParameter]`.

This metadata is available at runtime through reflection and is also consumed by tools like [FluentAnalysis](https://github.com/Respect/FluentAnalysis)for static type narrowing.

```
#[Assurance(type: 'int')]
final readonly class IntType implements Validator { /* ... */ }

final readonly class ValidatorBuilder extends Append
{
    #[AssuranceAssertion]
    public function assert(#[AssuranceParameter] mixed $input): void { /* ... */ }

    #[AssuranceAssertion]
    public function isValid(#[AssuranceParameter] mixed $input): bool { /* ... */ }
}
```

See `Assurance`, `AssuranceParameter`, `ComposableParameter`, and the enum types in the [API reference](docs/api.md#assurance) for the full set of options.

API Reference
-------------

[](#api-reference)

See [docs/api.md](docs/api.md) for the complete API reference covering attributes, builders, factories, resolvers, and exceptions.

###  Health Score

47

—

FairBetter than 93% of packages

Maintenance90

Actively maintained with recent releases

Popularity16

Limited adoption so far

Community16

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 58.8% 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 ~1 days

Total

3

Last Release

89d ago

Major Versions

1.0.0 → 2.0.02026-03-25

### Community

Maintainers

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

---

Top Contributors

[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (10 commits)")[![alganet](https://avatars.githubusercontent.com/u/202642?v=4)](https://github.com/alganet "alganet (7 commits)")

---

Tags

respectbuilderfluent

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[kris/laravel-form-builder

Laravel form builder - symfony like

1.7k2.3M46](/packages/kris-laravel-form-builder)[gherkins/regexpbuilderphp

PHP port of thebinarysearchtree/regexpbuilderjs

1.3k181.3k1](/packages/gherkins-regexpbuilderphp)[optimistdigital/nova-menu-builder

This Laravel Nova package allows you to create and manage menus and menu items.

243374.7k](/packages/optimistdigital-nova-menu-builder)[respect/stringifier

Converts any value to a string

2623.5M8](/packages/respect-stringifier)[outl1ne/nova-menu-builder

This Laravel Nova package allows you to create and manage menus and menu items.

243260.1k4](/packages/outl1ne-nova-menu-builder)[kartik-v/yii2-builder

Build forms (single-row or multi-row/tabular) easily for Yii Framework 2.0

961.2M33](/packages/kartik-v-yii2-builder)

PHPackages © 2026

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