Fiber Throws Where The Fiber Is¶
<?php
// Illustrates: $fiber->throw($exception) resumes the fiber and raises the
// exception exactly where Fiber::suspend() paused it, not in the caller's scope.
$fiber = new Fiber(function (): void {
echo "fiber: starting
";
try {
$value = Fiber::suspend('paused, waiting for input');
echo "fiber: resumed normally with '{$value}'
";
} catch (RuntimeException $e) {
// This ordinary try/catch, wrapped around suspend(), catches the
// exception injected by Fiber::throw() -- no special API needed.
echo "fiber: caught injected exception: '{$e->getMessage()}'
";
}
echo "fiber: finishing
";
});
echo "main: getCurrent() outside any fiber is: ";
var_dump(Fiber::getCurrent());
$suspendedValue = $fiber->start();
echo "main: fiber suspended with '{$suspendedValue}'
";
// This does NOT throw here, in main's scope. It resumes the fiber and the
// exception surfaces at the suspend() call above, inside the fiber's body.
$fiber->throw(new RuntimeException('something went wrong'));
echo "main: fiber finished? " . ($fiber->isTerminated() ? 'yes' : 'no') . "
";
// Starting an already-started (and now terminated) fiber is not a silent
// no-op: it throws a FiberError.
try {
$fiber->start();
} catch (FiberError $e) {
echo "main: caught FiberError: '{$e->getMessage()}'
";
}
Fiber is the generator’s sibling that nobody writes tips about, so here is one.
$fiber->throw($exception) does not throw in the caller’s own scope. It resumes the suspended fiber and raises the exception exactly at the Fiber::suspend() call that paused it, as if that call itself had thrown.
That means an ordinary try/catch written around the suspend() call inside the fiber’s body will catch it, with no special API needed on the fiber’s side.
Also note that Fiber::getCurrent() returns null outside of any fiber, and that starting an already-started fiber throws a FiberError, not a silent no-op.
See Also¶
PHP Features¶
Last updated: 14 July 2026