whatthejeff/fab
A lightweight functional programming library for PHP by Jeff Welch, offering helpers for currying, partial application, and function composition so you can build cleaner, more reusable code with less boilerplate.
Fab is a small, framework-agnostic PHP library that wraps strings in ANSI escape sequences, letting you build styled terminal output with a chainable API. It ships no Laravel service provider, config, or facade — you pull it in with Composer and call it directly.
composer require whatthejeff/fab
A first, minimal use inside an Artisan command:
use function Fab\fab;
class ReportCommand extends Command
{
protected $signature = 'report:run';
public function handle()
{
$this->line((string) fab('Deploy complete')->white()->bgGreen()->bold());
}
}
Where to look first, in order:
fab()) and underlying formatter classes, and the docs are thin enough that the README plus src/ is the real reference.src/ for the modifier methods available (foreground/background colors, bold, underline, blink, reverse, etc.) — there is no generated API reference.tests/ for expected output shape; it doubles as the most reliable spec of what each modifier emits.First productive use case: a "status" line in a long-running console command, where you want [ OK ], [WARN ], and [FAIL ] prefixes to be visually scannable and to carry a background color, not just a foreground change.
Wrap the dependency in one helper. Because the API surface is small and the package is lightly maintained, isolating it behind a single class means a future swap (or a fallback to Symfony's OutputFormatter) is a one-file change.
namespace App\Support;
final class Status
{
public static function ok(string $msg): string
{
return (string) fab(" {$msg} ")->black()->bgGreen()->bold();
}
public static function fail(string $msg): string
{
return (string) fab(" {$msg} ")->white()->bgRed()->bold();
}
}
Call Status::ok('migrated') from commands instead of chaining fab() inline everywhere.
Chain, then cast once. The fluent object is a value object with __toString(); concatenating it in a larger string works, but explicit (string) casts keep intent obvious and avoid subtle issues when passing to functions typed string.
$line = sprintf('%s %s', Status::ok('OK'), 'cache warmed');
$this->line($line);
Compose with Symfony Console output, don't replace it. Laravel's $this->info() / $this->error() already call into Symfony's styled formatter. Use Fab where you want richer combinations — background colors with foreground contrast, reverse video for headers, multi-style banners — and leave plain verbosity-style messages to Laravel's helpers. Mixing both in one command is fine; they emit the same class of escape codes.
Guard for non-decorated output. Before emitting styled strings, respect the output's own decision about color:
if ($this->output->isDecorated()) {
$this->line(Status::ok('done'));
} else {
$this->line('[ OK ] done');
}
This covers --no-ansi, redirection to a file, CI runners with no TTY, and cron.
Testing. Buffer the command output and normalize before asserting:
$output = new BufferedOutput;
Artisan::call('report:run', [], $output);
$plain = preg_replace('/\e\[[0-9;]*m/', '', $output->fetch());
$this->assertStringContainsString('[ OK ]', $plain);
Asserting on the raw escape sequences couples your tests to Fab's exact byte output and to whichever modifiers you happened to chain.
\e[...m sequences. Always route Fab output through a decorated check as above.NO_COLOR and TERM=dumb conventions. Fab predates widespread adoption of these, so it won't honor them itself. If you wrap it in a helper, check getenv('NO_COLOR') there once and return the plain string.cmd.exe. Modern Windows Terminal and ConEmu are fine; if you support old Windows CI, fall back to plain text via the same isDecorated() path.use function Fab\fab;, not use Fab\fab;. If your autoloader or tooling complains, instantiate the formatter class directly — behavior is identical, and this avoids depending on autoload.files being present.reset semantics. Every styled string should end with a reset; if you build strings by substr or sprintf with %s truncation, you can cut off the trailing reset and tint the rest of the terminal. Keep styled values as whole lines.app/Support if you'd rather not carry the dependency; the wrapper pattern above makes that swap trivial.<info>/<comment>/<error> tags cover it with zero dependencies. Fab earns its place when you want backgrounds, reverse video, and long modifier chains without writing escape codes by hand.How can I help you explore Laravel packages today?