teknoo/recipe
Teknoo Recipe is a lightweight PHP package for defining and running recipes as reusable, ordered steps. It helps you compose simple workflows and task sequences with a clean API, minimal dependencies, and testable code, making automation straightforward in PHP projects.
Architecture fit
Bus, Pipeline, scheduler, Octane) must be written by us as an adapter layer.Draft → Written, Free → Trained → Cooking), and #east-style Promise callbacks instead of return values. This conflicts with the framework's dominant imperative, return-value conventions and with how most Laravel developers reason about control flow.require() ingredients), reorderable steps, sub-recipe delegation, and error handlers. That is a credible model for credit checks, onboarding/KYC ladders, pricing rules, or document-approval flows — domains where "which steps run depends on what inputs exist" is the core requirement.process() only.FiberBowl, CookingSupervisor) provide cooperative concurrency inside a single process, and the chef never drives the supervisor — a step must call switch()/loop()/finish(). That is a manual, non-preemptive event loop. It is powerful for CPU-bound or IO-multiplexed step graphs, but it is an advanced concurrency contract that most product teams will misapply (starvation, forgotten fibers, blocked loops).Integration feasibility
PlanInterface/Recipe construction, wiring Chef lifecycle into request or job scope, and surfacing results through Promise/fetchResult() back into ordinary Laravel returns.^8.4 and declare(strict_types=1) throughout. If the target application is on 8.2/8.3 (common in Laravel 10/11 estates), adoption is blocked until a framework/PHP upgrade lands — treat that as a hard gate, not a nice-to-have.require(), cook(), execute(), onError() all return new instances. A team that ignores return values silently ships a no-op recipe with zero runtime error. Mitigation: lint rule or static analysis plus a mandatory "compile and assert step count" test per recipe.train() compiles a clone, so the retained instance stays Draft. Any caching/pre-warming strategy must cache the trained clone explicitly, or we recompile per request.Technical risk
teknoo/immutable, teknoo/states), GitLab CI as primary with GitHub only a mirror. Bus-factor of effectively one. This is the dominant risk and it is not mitigated by code quality.src/, PHPStan at max level, PSR-12, Behat acceptance suite, composer audit in CI, PHP 8.4 and 8.5 tested at lowest and latest dependencies, documented public-API stability commitment for minor/patch releases. This is better engineering discipline than most small packages we depend on.DynamicBowl/DynamicFiberBowl execute a callable read from the workplan; object ingredients accept a class name string checked with is_a(). Any recipe whose workplan is populated from user input is a remote-code-execution or class-instantiation vector. Documentation flags this, which is good, but it must become a hard rule in our integration layer: no workplan value derived from request input may ever reach a dynamic bowl key or an unnamed object ingredient.step, step1). Both are reasonable defaults but produce "worked as coded, not as intended" bugs. Needs convention tests, not just unit tests.CookingSupervisor (never calling finish(), catching an exception outside throw()) yields hung requests under PHP-FPM/Octane. Under Octane the chef is reusable and cloneable, but fiber/supervisor state must be strictly per-cooking — needs a proof under Octane before committing.Key questions
Pipeline plus a precondition guard gets 70% of the value with zero new vendor risk. If it's "steps discoverable and reorderable at runtime from configuration," Recipe is genuinely differentiated and hard to replicate.teknoo/* stops shipping? (Estimate the adapter rewrite if we had to replace it.)DynamicBowl entirely and pin to static recipes?Stack fit
WorkflowRunner contract in the domain layer (run(WorkflowDefinition, array $inputs): WorkflowResult) and implement it with Recipe inside an infrastructure adapter. No controller, job, or model should ever import a Teknoo\Recipe\* symbol.PlanInterface implementation (a class, typed, container-injectable) rather than as an ad-hoc Recipe built inline. This gives us compile-time discoverability, one class per flow, and a clean place to unit-test step composition.RecipeServiceProvider; the Chef is reusable and cloneable, so scope one trained chef per execution (or per Octane worker with a proven-clean clone) and never share a Cooking-state chef across requests.Promise/fetchResult() outcome into a domain event or result DTO. Recipe handles intra-flow sequencing only; it does not replace the queue.Migration path
Pipeline + guards, once with Recipe. Compare on readability, testability, change cost for a reordering request, and lines of adapter code. This is the go/no-go artefact.WorkflowRunner adapter, a ChefFactory, a Promise-to-result bridge, and a static-analysis rule banning direct Teknoo\ imports outside Infrastructure\Workflow.Compatibility
~x.y.z per the package's documented minor-version stability promise) and rely on composer.lock; do not float. Add composer audit and an automated composer update --dry-run canary to a scheduled pipeline so transitive Teknoo bumps surface early.teknoo/immutable and teknoo/states are hard transitive dependencies with their own version ranges; verify their PHP 8.4/8.5 support matrix matches our runtime before pinning.Sequencing
Maintenance
CHANGELOG.md for behaviour changes; the package commits to not breaking public API or observable behaviour in minor/patch, which is a meaningful but self-declared guarantee.Support
vcs repository entry) from day one so we are never blocked by an unresponsive upstream.Scaling
process() calls, so scaling is the host app's scaling.Failure modes
CookingSupervisor (missing finish(), uncaught exception bypassing throw()). Under PHP-FPM this consumes a worker; under Octane it degrades a shared process.DynamicBowl key or an unnamed object ingredient — a user-influenced callable or class name. Treat as a P1 threat model item; forbid dynamic bowls in flow definitions that touch untrusted input.null means absent: a null ingredient is treated as missing, which will surface as "required ingredient not available" errors that look like bugs in unrelated code. Document loudly.teknoo/states or teknoo/immutable on a CI canary. Pin, lock, and canary-test.finish() is the only path to result validation; a recipe that never calls it terminates without a validated result. Add an explicit "must finish" assertion in the adapter's result mapping.Ramp-up
Promise/#east semantics (fetchResult() exists precisely because this is non-obvious); plus a hard 5+ days for FiberBowl/CookingSupervisor before anyone is allowed to write one in production.TPM recommendation: conditionally proceed to a timeboxed spike, with the PHP 8.4 floor and workplan security rules as hard gates and a Laravel-native fallback defined up front. The engineering quality justifies the evaluation; 3 stars and 0 dependents justify a strict kill criterion and a vendored-fork contingency before the first production flow.
How can I help you explore Laravel packages today?