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

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

respect/fluentgen
=================

Code generation for fluent builder interfaces

2.0.0(3mo ago)1183↓90.9%1ISCPHPPHP ^8.5CI passing

Since Mar 23Pushed 3mo ago1 watchersCompare

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

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

Respect\\FluentGen
==================

[](#respectfluentgen)

Generate PHP mixin interfaces from class namespaces, so IDEs can autocomplete `__call`-based fluent builder chains.

When a builder resolves method calls dynamically, IDEs can't see the available methods. FluentGen solves this by scanning your classes, reflecting their constructors, and generating interface files that declare every method with proper signatures and return types. Your builder class then references the generated interface via a `@mixin` docblock, and autocompletion works.

FluentGen works with any class namespace that follows a naming convention. If your classes use the `#[Composable]` attribute from [Respect/Fluent](https://github.com/Respect/Fluent), FluentGen additionally generates per-prefix composed interfaces, but that is not required.

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

[](#installation)

```
composer require --dev respect/fluentgen
```

Requires PHP 8.5+.

What it generates
-----------------

[](#what-it-generates)

Given a namespace full of classes like `AreaFormatter`, `DateFormatter`, `MaskFormatter`, FluentGen produces two interfaces:

- A **Builder** interface with static methods (`Builder::area()`, `Builder::date()`, etc.) for starting chains.
- A **Chain** interface with instance methods (`->area()`, `->date()`, etc.) for continuing them.

Each generated method mirrors the constructor signature of the underlying class. If `MaskFormatter` has `__construct(string $range, string $replacement = '*')`, the generated `mask()` method has the same parameters. Doc comments on the constructor are carried over too.

Setting it up
-------------

[](#setting-it-up)

FluentGen is typically wired into a Symfony Console command that you run during development. Here's how it looks like:

First, configure what to scan and where to write:

```
$config = new Config(
    sourceDir: __DIR__ . '/src',
    sourceNamespace: 'App\\Formatters',
    outputDir: __DIR__ . '/src/Mixins',
    outputNamespace: 'App\\Formatters\\Mixins',
);
```

`sourceDir` and `sourceNamespace` tell the scanner where your classes live. `outputDir` and `outputNamespace` control where the generated interfaces go.

Next, set up scanning. The `NamespaceScanner` reflects every concrete class in the directory. You can filter by interface and exclude specific classes:

```
$scanner = new NamespaceScanner(
    nodeType: Formatter::class,
    excludedClassNames: ['FormatterBuilder'],
);
```

Without filters, the scanner picks up every non-abstract class it finds. The `nodeType` filter restricts to classes implementing a given interface. The exclusion list removes specific classes by short name, useful for excluding the builder class itself if it lives in the same namespace.

Then configure the generator. `MixinGenerator` needs to know what interfaces to produce. Each `InterfaceConfig` describes one:

```
$generator = new MixinGenerator(
    config: $config,
    scanner: $scanner,
    methodBuilder: new MethodBuilder(classSuffix: 'Formatter'),
    interfaces: [
        new InterfaceConfig(
            suffix: 'Builder',
            returnType: Chain::class,
            static: true,
        ),
        new InterfaceConfig(
            suffix: 'Chain',
            returnType: Chain::class,
            rootExtends: [Formatter::class],
        ),
    ],
);
```

The `suffix` determines the interface name. The `returnType` is what every generated method returns, typically your Chain interface, enabling fluent chaining. Set `static: true` for the builder entry point. Use `rootExtends`when the chain interface should extend your domain interface.

The `MethodBuilder` handles how class names map to method names. The `classSuffix` option strips a suffix before generating: `AreaFormatter` becomes `area()`, `DateFormatter` becomes `date()`.

Finally, call `generate()` to get a filename-to-content map:

```
$files = $generator->generate();

foreach ($files as $filename => $content) {
    file_put_contents($filename, $content);
}
```

Run this as part of your dev tooling: a console command, a Composer script, or CI check that verifies generated files are up to date.

Composition support (optional, requires Respect/Fluent)
-------------------------------------------------------

[](#composition-support-optional-requires-respectfluent)

Some libraries, like Respect/Validation, use prefix composition where `notEmail()` creates `Not(Email())`. If your classes use the `#[Composable]`attribute from Respect/Fluent, FluentGen handles this automatically.

Install the optional dependency:

```
composer require respect/fluent
```

The `MixinGenerator` discovers composable prefixes and generates per-prefix interfaces. For example, a `Not` class with `#[Composable(self::class)]` produces a `NotBuilder` interface containing `notEmail()`, `notString()`, etc., and a root `Builder` interface that extends all prefix interfaces.

Composition constraints (`without`, `with`, `optIn` on the `Composable`attribute) are respected during generation. Forbidden combinations are excluded from the generated interfaces.

For the runtime prefix map, `PrefixConstantsGenerator` produces a constants class with `COMPOSABLE`, `COMPOSABLE_WITH_ARGUMENT`, and `FORBIDDEN` arrays that `ComposableMap` uses at resolve time.

Customization
-------------

[](#customization)

**MethodBuilder** controls how constructor parameters become method signatures. Beyond `classSuffix`, it supports `excludedTypePrefixes` and `excludedTypeNames` to skip parameters whose types come from external packages you don't want in your public interface.

**FileRenderer** handles the final output, printing the generated namespace via Nette PHP Generator and applying the `OutputFormatter`. The formatter preserves existing SPDX license headers, converts tabs to spaces, normalizes nullable syntax (`?Type` becomes `Type|null`), and collapses single-line doc comments. Both are used with sensible defaults; you rarely need to customize them.

**InterfaceConfig** has a few more options for the root interface: `rootComment` adds a docblock (like `@mixin FormatterBuilder`), `rootUses`adds use statements, and `rootExtends` makes the interface extend others.

###  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 ~1 days

Total

3

Last Release

90d ago

Major Versions

1.0.1 → 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

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

---

Tags

respectfluentmixinfluentgen

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[tempest/framework

The PHP framework that gets out of your way.

2.2k31.1k12](/packages/tempest-framework)[respect/stringifier

Converts any value to a string

2623.5M8](/packages/respect-stringifier)[samrap/acf-fluent

A fluent interface for the Advanced Custom Fields WordPress plugin

28457.1k4](/packages/samrap-acf-fluent)[fab2s/yaetl

Widely Extended Nodal Extract-Transform-Load ETL Workflow AKA NEJQTL or Nodal-Extract-Join-Qualify-Tranform-Load

64187.4k](/packages/fab2s-yaetl)

PHPackages © 2026

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