tetthys / wrap
Minimal Result-like wrapper with fluent map/filter/reduce and conditional helpers for PHP 8.3+.
Installs: 19
Dependents: 1
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/tetthys/wrap
Requires
- php: ^8.3
Requires (Dev)
- pestphp/pest: ^2.34
README
A minimal, fluent, Result-like wrapper for PHP 8.3+ — featuring safe execution, functional operators (
map,filter,reduce), and expressive conditional helpers (when,unless,branch, etc.).
🚀 Installation
composer require tetthys/wrap
💡 Quick Example
use Tetthys\Wrap\Wrap; $result = Wrap::handle(fn() => riskyOperation()) ->ok(fn($v) => \Log::info("Success: {$v}")) ->fail(fn($e) => \Log::error($e->getMessage())) ->rescue(fn() => 42) ->then(fn($v) => $v * 2) ->always(fn($ok, $err, $val) => \Log::debug('Done', compact('ok', 'val'))) ->getValueOr(0); echo $result; // e.g. 84
🧩 Core API
Wrap::handle(callable $callback)
Safely executes a callable and captures either its return value or any thrown Throwable.
$wrap = Wrap::handle(fn() => 1 / 0);
ok(callable $callback)
Runs only when successful — typically used for logging or side effects.
->ok(fn($v) => \Log::info("Value: {$v}"));
fail(callable $callback)
Runs only when failed — ideal for alerts or error logs.
->fail(fn($e) => \Log::error($e->getMessage()));
rescue(callable $callback)
Provides a fallback value on failure, flipping the state to success so the chain continues.
->rescue(fn() => 'default value');
then(callable $callback)
Transforms the stored value when successful (not limited to iterables).
->then(fn($v) => $v + 1);
safeThen(callable $callback)
Same as then(), but catches exceptions and marks the chain as failed instead of throwing.
->safeThen(fn($v) => riskyTransform($v));
map(callable $mapper)
Applies a function to each element when the value is iterable.
->map(fn($v) => $v * 2);
safeMap(callable $mapper)
Like map(), but automatically invalidates on exceptions inside the mapper.
filter(callable $predicate)
Filters iterable values by predicate; preserves keys.
->filter(fn($v) => $v > 10);
safeFilter(callable $predicate)
Variant of filter() that invalidates if the predicate throws.
reduce(callable $reducer, mixed $initial)
Reduces an iterable into a single accumulated value.
->reduce(fn($acc, $v) => $acc + $v, 0);
safeReduce(callable $reducer, mixed $initial)
Catches reducer exceptions and marks the chain as failed.
always(callable $callback)
Always runs, like a finally block.
Receives (bool $ok, ?Throwable $error, mixed $value).
->always(fn($ok, $err, $val) => \Log::debug('Finished', compact('ok', 'val')));
finally(callable $callback)
Similar to always(), but keeps the fluent chain alive; if the callback throws, the chain becomes failed.
->finally(fn() => cleanup());
⚙️ Accessors & Extraction
| Method | Description | Example |
|---|---|---|
isOk() |
Returns whether the chain succeeded. | if ($wrap->isOk()) echo "OK"; |
getError() |
Returns the captured exception or null. |
$err = $wrap->getError(); |
getValue() |
Returns the stored value (may be null). |
$v = $wrap->getValue(); |
getValueOr($default) |
Returns value or default. | $v = $wrap->getValueOr(0); |
getValueOrCall(fn $factory) |
Lazy default — calls the function only when needed. | $v = $wrap->getValueOrCall(fn() => 99); |
getValueOrNull() |
Returns value on success, otherwise null. |
$v = $wrap->getValueOrNull(); |
getOrThrow(?callable $factory = null) |
Returns value or throws (optionally map error via $factory). |
$v = $wrap->getOrThrow(); |
🔀 Conditional Helpers
when(fn($value): bool, fn($value))
Runs a callback only when the predicate returns true.
Wrap::handle(fn() => 10) ->when(fn($v) => $v > 5, fn($v) => echo "✅ Greater than 5");
unless(fn($value): bool, fn($value))
Runs a callback only when the predicate returns false.
Wrap::handle(fn() => 3) ->unless(fn($v) => $v > 5, fn($v) => echo "❌ Less or equal to 5");
whenTrue(fn($value))
Boolean-specialized shortcut for truthy values.
Wrap::handle(fn() => true) ->whenTrue(fn() => echo "It's true!");
whenFalse(fn($value))
Boolean-specialized shortcut for falsy values.
Wrap::handle(fn() => false) ->whenFalse(fn() => echo "It's false!");
branch(fn($onTrue), fn($onFalse))
Fluent if / else-style branching.
Wrap::handle(fn() => 10 > 5) ->branch( fn() => echo "✅ Enough balance", fn() => echo "❌ Not enough" );
💬 Example: Conditional Flow
Wrap::handle(fn() => 10) ->then(fn(int $v) => $v > 5) ->branch( fn() => echo "✅ Enough balance", fn() => echo "❌ Not enough" );
Output:
✅ Enough balance
🪄 Optional Global Helper
You can define a global helper wrap() for concise syntax.
helpers.php
<?php if (!function_exists('wrap')) { /** * Shortcut for Wrap::handle() * * @template TResult * @param callable(): TResult $callback * @return \Tetthys\Wrap\Wrap<TResult, \Throwable> */ function wrap(callable $callback): \Tetthys\Wrap\Wrap { return \Tetthys\Wrap\Wrap::handle($callback); } }
composer.json
{
"autoload": {
"files": ["helpers.php"]
}
}
Then run:
composer dump-autoload
Usage:
$result = wrap(fn() => riskyOperation()) ->rescue(fn() => 42) ->getValueOr(0);
🧠 Why Use Wrap?
- ✨ Clean, functional, and chainable syntax
- 🧱 Zero dependencies (pure PHP)
- ⚡ Safe and exception-aware
- 🧩 Fluent conditional flow (
when,unless,branch) - 🧰 Optional global helper (
wrap())
📜 License
MIT © Tetthys