Now ask it what happened
Here is the difference. A plain pipeline hands you the final value and nothing else: if the title came out wrong, you are left guessing which step broke it. The hub kept notes. Ask it what happened:
<?php
use Keaton\PipeDream\PipeHub;
$post = (object) ['title' => ' hello, PIPE dream '];
$envelope = PipeHub::create()
->addPipes([
fn ($post) => $post->title = trim($post->title),
fn ($post) => $post->title = preg_replace('/\s+/', ' ', $post->title),
fn ($post) => $post->title = ucwords(strtolower($post->title)),
])
->sendIt($post);
foreach ($envelope->logs() as $record) {
printf("%-11s %-24s %s\n", $record['type'], $record['key'], $record['data']);
}
info PipeHub:sendIt Sending subject through all pipes
timerStart Closure@hub-log.php:9 Executing rule Closure@hub-log.php:9
returnValue Closure@hub-log.php:9 hello, PIPE dream
timerEnd Closure@hub-log.php:9 Finished executing rule Closure@hub-log.php:9
timerStart Closure@hub-log.php:10 Executing rule Closure@hub-log.php:10
returnValue Closure@hub-log.php:10 hello, PIPE dream
timerEnd Closure@hub-log.php:10 Finished executing rule Closure@hub-log.php:10
timerStart Closure@hub-log.php:11 Executing rule Closure@hub-log.php:11
returnValue Closure@hub-log.php:11 Hello, Pipe Dream
timerEnd Closure@hub-log.php:11 Finished executing rule Closure@hub-log.php:11
info PipeHub:sendIt Finalized result!Every pipe was timed, and what each one returned was recorded. You can see exactly where hello, PIPE dream became Hello, Pipe Dream, without adding a single line of logging to your code. $envelope->results() holds the same results, ready to check in code.
Now break it on purpose. Change the second step so it eats every space, and run the hub again:
<?php
use Keaton\PipeDream\PipeHub;
$post = (object) ['title' => ' hello, PIPE dream '];
$envelope = PipeHub::create()
->addPipes([
fn ($post) => $post->title = trim($post->title),
fn ($post) => $post->title = preg_replace('/\s+/', '', $post->title), // oops: '' instead of ' '
fn ($post) => $post->title = ucwords(strtolower($post->title)),
])
->sendIt($post);
foreach ($envelope->logs() as $record) {
if ($record['type'] === 'returnValue') {
printf("%-30s %s\n", $record['key'], $record['data']);
}
}
Closure@hub-log-broken.php:9 hello, PIPE dream
Closure@hub-log-broken.php:10 hello,PIPEdream
Closure@hub-log-broken.php:11 Hello,pipedreamYou don't have to guess which step ate the spaces. The log already points at the second one: it's the first line where they're gone.
That's the whole difference. A pipeline tells you the answer. A hub hands you the receipt.
And that's only the start. Once every step is a named, recorded thing, a lot more opens up:
- Rules as classes: each pipe becomes a small, testable class with a name, so the log reads like a description of your business.
- Stacked hubs: group steps into stages and run the stages as one flow, with one log.
- Control flow: skip a step, stop a flow early, redirect, or catch a failure without losing the log.
- Saved runs: store every log and query it later: which runs hit problems, and exactly where.
- A flow you can draw: read a flow back and render it as a page, then watch real traffic move through it.
All of it builds on what you just saw. The docs pick it up from here.