respect/fluent 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

respect/fluent

最新稳定版本:2.0.1

Composer 安装命令:

composer require respect/fluent

包简介

Namespace-aware fluent class resolution

README 文档

README

Respect\Fluent

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

composer require respect/fluent

Quick Start

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

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<int, Middleware> */
    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

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

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

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

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 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 for the full set of options.

API Reference

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

统计信息

  • 总下载量: 309
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 1
  • 依赖项目数: 3
  • 推荐数: 1

GitHub 信息

  • Stars: 1
  • Watchers: 1
  • Forks: 1
  • 开发语言: PHP

其他信息

  • 授权协议: ISC
  • 更新时间: 2026-03-23

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固