wujunze/php-cli-color
wujunze/php-cli-color is a lightweight PHP library for adding ANSI colors and styles to command-line output. It supports foreground and background colors, 256-color terminals, and simple chained methods to make CLI scripts, logs, and console tools easier to read.
This is a tiny, dependency-free wrapper around ANSI SGR escape codes. There is nothing to configure, publish, or register — no service provider, no facade, no config file.
composer require wujunze/php-cli-color
Then use the static API directly. Methods follow the CliColor::{color}(string $text) pattern (check src/CliColor.php for the exact set — typically red, green, yellow, blue, cyan, magenta, white, plus semantic helpers like success, error, info, warning):
use Wujunze\CliColor\CliColor;
echo CliColor::green('Build OK') . PHP_EOL;
echo CliColor::red('Deploy failed') . PHP_EOL;
Where to look first:
vendor/wujunze/php-cli-color/src/ — it is small enough to read in one sitting. This is your real documentation; the README is a couple of examples at most.composer.json for the PSR-4 prefix (Wujunze\CliColor\) and the PHP constraint before adopting it.First use case: a throwaway maintenance script or cron job where you want a red line on failure and a green line on success, without pulling Symfony Console into a 40-line script.
// scripts/reindex.php
use Wujunze\CliColor\CliColor;
try {
rebuildIndex();
fwrite(STDOUT, CliColor::green('Index rebuilt') . PHP_EOL);
exit(0);
} catch (Throwable $e) {
fwrite(STDERR, CliColor::red('Failed: ' . $e->getMessage()) . PHP_EOL);
exit(1);
}
Note the explicit STDOUT/STDERR — that distinction is more valuable in practice than the color itself.
Inside Artisan commands, prefer Laravel's own helpers. $this->info(), $this->error(), $this->warn() already handle ANSI negotiation, --no-ansi, and Symfony styling. Use this package only for output Laravel won't give you, or for pre-bootstrap work.
If you do use it in a command, guard it yourself since the package has no notion of TTY:
use Wujunze\CliColor\CliColor;
private function paint(string $text, string $color): string
{
if (! $this->output->isDecorated()) {
return $text;
}
return CliColor::$color($text);
}
// in handle()
$this->line($this->paint('Cache warmed', 'green'));
Wrap it in a small presenter rather than scattering static calls. Because every method returns a string, the natural pattern is a thin formatting layer that composes lines:
final class Console
{
public function __construct(private bool $enabled = true) {}
public function tag(string $level, string $message): string
{
if (! $this->enabled) {
return sprintf('[%s] %s', strtoupper($level), $message);
}
$color = match ($level) {
'error' => 'red',
'warn' => 'yellow',
'ok' => 'green',
default => 'cyan',
};
return sprintf('%s %s', CliColor::$color('[' . strtoupper($level) . ']'), $message);
}
}
Gate $enabled on stream_isatty(STDOUT) (PHP 7.2+) or posix_isatty(), plus a NO_COLOR env check. That single wrapper fixes the vast majority of this package's rough edges in one place.
Compose with sprintf, not concatenation of pre-colored fragments, when you need aligned columns. Escape codes count toward strlen but not toward visual width, so str_pad() must be applied before coloring:
$line = sprintf(
'%-20s %s',
$name, // pad the raw string
CliColor::green($status) // color after padding
);
Use it for status one-liners in CI/deploy scripts, and keep the plain-text fallback meaningful, because those environments are exactly where you'll want the log readable in a file or a web UI:
echo CliColor::yellow('Running migrations...') . PHP_EOL;
Laravel integration tip: call it from php artisan tinker when you want a quick visual marker while poking at a command's output path, or from an event listener that prints before the console output object exists (e.g. during package discovery). Outside those two niches, Laravel's built-ins win.
--no-ansi awareness. Piping to a file, tee, or a CI log capture will embed raw \e[32m sequences. This is the number one reason people rip this package back out. Always gate on stream_isatty() or the NO_COLOR convention.cmd.exe before Windows 10 1511 shows garbage. There is no platform check in the package. If you support legacy Windows shells, you need to detect and disable coloring yourself.\e[0m or \e[2J will break your coloring and, worse, can clear the terminal or forge log lines. Strip \x1b\[[0-9;]*m (or all \x1b) from untrusted text before passing it in.str_pad pattern above.composer.json and verify it actually loads under your PHP 8.x runtime before committing — the risk isn't the code (it's sprintf-level simple), it's that no one will fix an edge case for you.symfony/console's OutputFormatter or Laravel's $this->components before adding another bespoke helper.$this->info() and keep this package quarantined to scripts/.How can I help you explore Laravel packages today?