wundii/flowcrafter 问题修复 & 功能扩展

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

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

wundii/flowcrafter

Composer 安装命令:

composer require wundii/flowcrafter

包简介

PHP library for defining, executing, and monitoring message-driven workflows (state machines)

README 文档

README

PHP-Tests PHPStan VERSION PHP Rector ECS PHPUnit codecov Downloads

PHP-Engine für message-driven Workflows — Geschäftsprozesse als typsichere State Machines in reinem PHP, mit synchroner, asynchroner und zeitgesteuerter Ausführung und lückenlosem Audit-Log.

Ein Workflow besteht aus typisierten Messages, die zwischen Steps fließen. Das Routing schreibt man nicht — es ergibt sich aus den Constructor-Signaturen der Steps: Welche Message ein Step im Constructor verlangt, bestimmt, wann er läuft. Daraus entsteht ein typsicherer DAG, der sich versionieren, ausführen und in Echtzeit überwachen lässt. Kein YAML, kein XML, keine Annotations.

Features

  • Schema-as-Code — Workflows als PHP-Klassen, Routing direkt aus den Step-Constructors abgeleitet
  • Drei Ausführungsmodi — synchron (FlowRunner), asynchron über Queue (FlowObserver) und zeitgesteuert per Cron (FlowScheduler)
  • Read-Model-Projektionen — Handler reagieren asynchron auf einzelne Messages (ProjectionWorker)
  • Pluggable Storage — MySQL, Redis, EventSourcingDB; eigene Backends via StorageInterface
  • Lückenloses Audit-Log — jede Message, Exception und Statusänderung wird erfasst, Schemas via Hash versioniert
  • Automatischer Retry — pro Step konfigurierbare Wiederholungen bei transienten Fehlern
  • Observability — REST-API, Prometheus/OpenMetrics-Endpunkt und optionales Web-UI
  • Developer Experience — Console Commands, storageless Testing, Mermaid-Diagramme und Claude-Code-Plugin

Installation

composer require wundii/flowcrafter

Quickstart

vendor/bin/flowcrafter config:create   # 1. flowcrafter.php anlegen
vendor/bin/flowcrafter storage:init    # 2. Storage initialisieren
vendor/bin/flowcrafter dev             # 3. Dev-Server starten (API + Observer + Scheduler + Projection-Worker)

Schritt für Schritt: docs/getting-started.md.

Beispiel

Ein vollständiger Order-Flow in drei Bausteinen — Messages, Steps, Flow.

1. Messages

readonly Value-Objects. Drei Typen steuern das Routing: Init startet den Flow, Data fließt zwischen Steps, Return beendet ihn.

use Wundii\Flowcrafter\AbstractMessage;
use Wundii\Flowcrafter\Interface\MessageDataInterface;
use Wundii\Flowcrafter\Interface\MessageInitInterface;
use Wundii\Flowcrafter\Interface\MessageReturnInterface;

readonly class OrderInit extends AbstractMessage implements MessageInitInterface
{
    public function __construct(private string $sku) {}
    public function getSku(): string { return $this->sku; }
}

readonly class OrderValidated extends AbstractMessage implements MessageDataInterface
{
    public function __construct(private string $sku, private int $quantity) {}
    public function getSku(): string { return $this->sku; }
    public function getQuantity(): int { return $this->quantity; }
}

readonly class OrderCompleted extends AbstractMessage implements MessageReturnInterface
{
    public function __construct(private string $summary) {}
    public function getSummary(): string { return $this->summary; }
}

Braucht der erste Step keinen externen Input, gibt es die mitgelieferte Wundii\Flowcrafter\EmptyInitMessage statt einer eigenen Init-Klasse.

2. Steps

Reine PHP-Klassen. Der Constructor-Typ entscheidet das Routing, der Rückgabetyp den weiteren Verlauf: MessageData (Flow läuft weiter), MessageReturn (Flow endet) oder bool (Leaf-Result, kein Weiterleiten).

use Wundii\Flowcrafter\Interface\MessageDataInterface;
use Wundii\Flowcrafter\Interface\MessageReturnInterface;
use Wundii\Flowcrafter\Interface\StepInterface;

class ValidateStep implements StepInterface          // Init → Data
{
    public function __construct(private readonly OrderInit $init) {}

    /** @return class-string[] */
    public function returnTypes(): array { return [OrderValidated::class]; }

    public function process(): MessageDataInterface
    {
        return new OrderValidated($this->init->getSku(), quantity: 1);
    }
}

class CompleteOrderStep implements StepInterface     // Data → Return (beendet den Flow)
{
    public function __construct(private readonly OrderValidated $validated) {}

    /** @return class-string[] */
    public function returnTypes(): array { return [OrderCompleted::class]; }

    public function process(): MessageReturnInterface
    {
        return new OrderCompleted(sprintf(
            'Order %s x%d completed',
            $this->validated->getSku(),
            $this->validated->getQuantity(),
        ));
    }
}

class AuditStep implements StepInterface             // Data → bool (FlowResult)
{
    public function __construct(private readonly OrderValidated $validated) {}

    /** @return class-string[] */
    public function returnTypes(): array { return []; }

    public function process(): bool
    {
        return $this->validated->getQuantity() > 0;
    }
}

3. Flow

Das Schema entsteht via FlowBuilder. Hier konsumieren CompleteOrderStep und AuditStep dieselbe OrderValidated-Message — also laufen sie parallel.

use Wundii\Flowcrafter\Attribute\FlowGroup;
use Wundii\Flowcrafter\FlowBuilder;
use Wundii\Flowcrafter\FlowSchema;
use Wundii\Flowcrafter\Interface\FlowInterface;

#[FlowGroup('Order Management')]   // optionale UI-Gruppierung, ohne Einfluss auf den Schema-Hash
class OrderFlow implements FlowInterface
{
    public static function schema(): FlowSchema
    {
        $builder = new FlowBuilder('flow.order.v1', OrderInit::class, OrderCompleted::class);
        $builder->addStep(ValidateStep::class);
        $builder->addStep(CompleteOrderStep::class, retries: 3, delay: 500);
        $builder->addStep(AuditStep::class);
        return $builder->build();
    }
}

Das passende Diagramm liefert vendor/bin/flowcrafter diagram:mermaid App\\OrderFlow:

---
title: flow.order.v1
theme: neo
---
stateDiagram-v2
[*]-->ValidateStep: OrderInit
ValidateStep-->CompleteOrderStep: OrderValidated
ValidateStep-->AuditStep: OrderValidated
CompleteOrderStep-->[*]: OrderCompleted
Loading

Flows auslösen

Synchron — direkter Aufruf, Ergebnis sofort verfügbar:

use Wundii\Flowcrafter\FlowRunner;

$flowRunner = new FlowRunner(
    type: 'flow.order.v1',
    flowSource: OrderFlow::class,
    flowSubject: 'sku-42',   // optionaler Geschäfts-Key zur späteren Suche
    storage: $storage,       // aus $flowcrafterConfig->getStorage()
);

$result = $flowRunner->run(new OrderInit('sku-42'));   // MessageReturnInterface|bool

Asynchron — Message in die Queue legen, ein FlowObserver-Worker führt sie aus:

$queue = $flowcrafterConfig->getQueue();

$queue->appendObserveItem(
    type: 'flow.order.v1',
    flowSource: OrderFlow::class,
    flowHash: null,                 // null = neuer Flow, sonst Re-Run einer bestehenden Instanz
    messageSource: OrderInit::class,
    message: (new OrderInit('sku-42'))->jsonSerialize(),
    flowSubject: 'sku-42',
);

Zeitgesteuert — Schedule-Klasse mit Cron-Ausdruck, automatisch vom FlowScheduler aus dem Composer-Classmap entdeckt:

use Wundii\Flowcrafter\Attribute\FlowSchedule;
use Wundii\Flowcrafter\Schedule\AbstractSchedule;

#[FlowSchedule('0 */6 * * *', name: 'order-cleanup', group: 'Maintenance')]
class OrderCleanupSchedule extends AbstractSchedule
{
    public function process(): void
    {
        $this->enqueue(OrderFlow::class, new OrderInit('scheduled-cleanup'));
        // oder synchron: $this->run(...)
    }
}

Per REST-API geht es auch: POST /api/flow/flow-run (synchron) bzw. POST /api/queue/enqueue (async) — siehe docs/api.md.

Weitere Funktionen

Step Retry

Steps können bei transienten Fehlern automatisch wiederholt werden — konfiguriert pro Step über addStep(retries:, delay:). retries: 3 bedeutet ein Erstversuch plus drei Wiederholungen; die Retry-Konfiguration fließt in den Schema-Hash ein.

$builder->addStep(ExternalApiStep::class, retries: 3);             // 3 Versuche, 200 ms Delay (default)
$builder->addStep(SlowServiceStep::class, retries: 5, delay: 500); // 5 Versuche, 500 ms Delay

Read-Model-Projektionen

Projection-Handler reagieren asynchron auf einzelne Messages eines Flows — ideal für Read Models, Benachrichtigungen oder Side-Effects. Jede Methode wird per #[FlowProjectionMessage] an einen Message-Source gebunden, die Klasse per #[FlowProjection] an einen oder mehrere Flow-Typen. Handler werden automatisch aus dem Composer-Classmap entdeckt und vom ProjectionWorker (vendor/bin/flowcrafter projection:worker) abgearbeitet.

use Wundii\Flowcrafter\Attribute\FlowProjection;
use Wundii\Flowcrafter\Attribute\FlowProjectionMessage;
use Wundii\Flowcrafter\FlowMessageReadonly;
use Wundii\Flowcrafter\Interface\ProjectionHandlerInterface;

#[FlowProjection(['flow.order.v1'])]
class OrderProjection implements ProjectionHandlerInterface
{
    #[FlowProjectionMessage(OrderCompleted::class)]
    public function onCompleted(FlowMessageReadonly $message): void
    {
        // Read Model aktualisieren, Benachrichtigung verschicken, ...
    }
}

Die Zustellung ist at-least-once — Handler-Methoden müssen idempotent sein. Wirft eine Methode, wird die Exception als ProjectionException protokolliert und mit der nächsten Message weitergemacht; die Queue blockiert nicht.

Dependency Injection

Steps erhalten neben Messages auch externe Services per Constructor-Injection. Registriert werden sie über eine DependencyRegistry (per FlowcrafterConfig::setDependencyRegistry() bzw. im Konstruktor von FlowRunner, FlowScheduler, FlowObserver, ProjectionWorker). Jede Registrierungsart ist eine benannte Methode:

Methode Verhalten
instance(object) Konkrete Instanz, gebunden an die eigene Klasse
bind(string $id, object|class) Interface-Binding: Objekt → synthetic + Alias; class-string → autowire + Alias
autowire(class) Einzelklasse per Autowiring
autowireNamespace(string) Alle instanziierbaren Klassen unter einem PSR-4-Namespace
autowireDirectory(string) Alle instanziierbaren Klassen unter einem Verzeichnis
factory(class, Closure, ?alias) Lazy: Closure erhält den PSR-11-Container und liefert den Service
use Wundii\Flowcrafter\DependencyInjection\DependencyRegistry;
use Wundii\Flowcrafter\Env;
use Psr\Container\ContainerInterface;

$registry = (new DependencyRegistry())
    ->bind(HttpClientInterface::class, new CurlHttpClient())  // Interface → Instanz
    ->instance(new MyLogger())                                // Instanz ohne Interface
    ->autowireNamespace('App\\Service')                       // ganze Namespaces autowiren
    ->factory(ApiClient::class,                               // lazy, für rohe Skalare
        fn(ContainerInterface $c) => new ApiClient($c->get(HttpClientInterface::class), Env::string('API_KEY')),
    );

$flowRunner = new FlowRunner(
    type: 'flow.order.v1',
    flowSource: OrderFlow::class,
    storage: $storage,
    dependencyRegistry: $registry,
);

Testing

Storageless mit FlowTestCase — kein Docker nötig. Vollständiger Leitfaden: docs/testing.md.

use Wundii\Flowcrafter\Testing\FlowTestCase;

final class OrderFlowTest extends FlowTestCase
{
    public function testHappyPath(): void
    {
        $this->runFlow(
            flowType: 'flow.order.v1',
            flowSource: OrderFlow::class,
            initMessage: new OrderInit('sku-42'),
        );

        $this->assertFlowOk();
        $this->assertStepExecuted(CompleteOrderStep::class);
        $this->assertFlowBoolResult(true);   // AuditStep lieferte true

        $return = $this->assertFlowReturned(OrderCompleted::class);
        $this->assertSame('Order sku-42 x1 completed', $return->getSummary());
    }
}

Web-UI

Das optionale Frontend FlowCrafter UI visualisiert Flows, Messages, Exceptions, Schedules und Queues in Echtzeit:

docker run -p 5173:5173 -v ./data:/flowcrafter/data wundii/flowcrafter-ui:latest

Overview

Flow Detail

Flow Input Modal

Flow Devtool

Claude Code Plugin

Das optionale Plugin flowcrafter-claude erweitert Claude Code um Flowcrafter-Wissen — Flows, Steps, Messages, Projektionen und Schedules lassen sich per Slash-Command generieren und analysieren.

/plugin marketplace add wundii/flowcrafter-claude
/plugin install flowcrafter@flowcrafter-claude
Command Beschreibung
/create-flow Flow-Klasse mit FlowBuilder-DSL generieren
/create-step Step-Klasse mit Message-Injection generieren
/create-message Message-Klasse (init / data / return) generieren
/create-projection Projection-Handler für asynchrone Read Models generieren
/create-schedule Schedule-Klasse mit Cron-Ausdruck generieren
/analyze-flow Flow auf Fehler und Verbesserungen prüfen

Der flowcrafter-Skill aktiviert sich zusätzlich automatisch, sobald Flowcrafter-Begriffe im Gespräch auftauchen.

Dokumentation

Kapitel Inhalt
Getting Started Erste Schritte: Config, Storage, Dev-Server
Konzepte Flow, Status, Schema, Messages, includeSteps, Observer, Scheduler, Projektion
Konfiguration flowcrafter.php, Storage-Backends, Server-Einstellungen
Console Commands Command-Referenz
REST-API Endpunkte, Pagination, Auth
Testing Flows & Steps testen mit PHPUnit 11+
Deployment Produktion: FrankenPHP + Docker
Monitoring Prometheus / OpenMetrics, CheckMK
Entwicklung QA-Scripts für Contributor

Lizenz

MIT — siehe LICENCE.

wundii/flowcrafter 适用场景与选型建议

wundii/flowcrafter 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 176 次下载、GitHub Stars 达 2, 最近一次更新时间为 2026 年 03 月 10 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「flow」 「workflow」 「state-machine」 「orchestration」 「message-driven」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 wundii/flowcrafter 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 wundii/flowcrafter 我们能提供哪些服务?
定制开发 / 二次开发

基于 wundii/flowcrafter 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 176
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 2
  • 点击次数: 36
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 2
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-03-10