leafs / sprout
Fast, lightweight and minimal CLI framework for PHP
Fund package maintenance!
Open Collective
leafsphp
Requires
- php: ^7.4|^8.0
- symfony/process: *
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.64
- leafs/alchemy: ^2.0
- pestphp/pest: *
This package is auto-updated.
Last update: 2025-02-02 22:23:45 UTC
README
🌱 Fast, lightweight and minimal CLI framework for PHP. It provides a type-safe, feature-rich environment to build CLI apps of all sizes, plus a ton of helpers for building amazing experiences for users interacting with your CLI app.
- Not based on any Laravel or Symfony console components
- First-class support for Leaf & Leaf modules
- Comes with built in styling, input handling, output handling, and more
- Support for PHP 7.4 and above
- Easy to use, easy to extend
Installation
You can install Leaf Sprout using the Leaf CLI or Composer:
leaf install sprout
# or with composer
composer require leafs/sprout
Usage
To get started, you need to create a file which will be the entry point for your CLI app. This file should be executable and should have a shebang at the top of the file. Here's an example of a simple CLI app:
#!/usr/bin/env php <?php use Leaf\Sprout\Command; require __DIR__ . "/vendor/autoload.php"; $app = sprout()->createApp([ 'name' => 'My CLI App', 'version' => '1.0.0' ]); $app->command('greet', function (Command $app) { $command->write('Hello, world!'); }); $app->register(\MyCliApp\Commands\GreetCommand::class); $app->run();
You can then run your CLI app using the PHP CLI:
php my-cli-app greet
Writing Commands
You can write your commands in a functional way using the command()
method on the app instance, or you can create a class that extends the Leaf\Sprout\Command
class. Here's an example of a command written as a class:
<?php namespace MyCliApp\Commands; use Leaf\Sprout\Command; class GreetCommand extends Command { protected $signature = 'greet {name}'; protected $description = 'Greet a user'; public function handle() { $this->write("Hello, {$this->argument('name')}!"); } }
You can then register this command with the app instance:
$app->register(\MyCliApp\Commands\GreetCommand::class);
Styling Output
Leaf Sprout comes with a built-in output styling system that allows you to style your output using a simple API adapted from TermWind. Here's an example of how you can style your output:
$app->command('greet', function (Command $command) { $command->write( style()->apply('p-4 bg-green-300 text-white')->to('Hello') . ', world!' ); });
You can also use pre-configured styles like success()
, error()
, warning()
, and info()
:
style()->success('Operation successful'); style()->error('Operation failed'); ... style()->pill('Operation successful'); style()->pill('Operation failed')->apply('bg-red-500 text-white'); ... style()->italic('This is italic text'); style()->bold('This is bold text'); style()->underline('This is underlined text'); style()->dim('This is dim text'); style()->strikethrough('This is strikethrough text');
Most of the styling options are chainable, so you can chain multiple styles together:
style()->bold()->underline()->apply('text-red-500')->to('Hello, world!');
Input Handling
Leaf Sprout comes with a built-in input handling system that allows you to easily get input from the user. The easiest way is to use the prompt()
method:
$name = sprout()->prompt([ 'type' => 'text', // 'select', 'confirm', 'password', 'number', 'text' 'initial' => 'John Doe', 'message' => 'What is your name?', 'validate' => function ($value) { if (empty($value)) { return 'Name cannot be empty'; } return true; } ]); $command->write("Hello, $name!"); // Hello, John Doe!
You can also have multiple prompts in a single command for more complex interactions like setting up a project via your CLI:
$possiblySetValue = $something ?? null; $results = sprout()->prompt([ [ 'type' => 'text', 'name' => 'name', 'message' => 'What is your project name?', 'initial' => 'my-project', 'validate' => function ($value) { if (empty($value)) { return 'Name cannot be empty'; } return true; } ], [ 'type' => $possiblySetValue ? null : 'select', 'name' => 'type', 'message' => 'What type of project do you want?', 'initial' => 0, 'choices' => [ ['title' => 'Web', 'value' => 'web'], ['title' => 'API', 'value' => 'api'], ['title' => 'CLI', 'value' => 'cli'], ], ], [ 'type' => 'confirm', 'name' => 'install', 'message' => 'Do you want to install dependencies?', 'initial' => true, ], ]); $results; // ['name' => 'my-project', 'type' => 'web', 'install' => true]
When the user runs the command, they will be prompted for the project name, type, and whether they want to install dependencies. The results will be stored in the $results
variable. The prompt type can be text
, select
, confirm
, password
, number
, or text
, when set to null
, the prompt will be skipped.
Besides prompts, you can also use the arguments()
and options()
methods to get arguments and options passed to the command:
$app->command('greet', function (Command $command) { $name = $command->argument('name'); $uppercase = $command->option('uppercase'); $greeting = "Hello, $name!"; if ($uppercase) { $greeting = strtoupper($greeting); } $command->write($greeting); });
Output Handling
Process Handling
Sprout comes with a built-in process handling system that allows you to run processes on the system. You can use the process()
method to run a process:
$process = sprout()->createProcess('ls -la'); $process->onError(function ($error) use ($command) { $command->write("An error occurred: $error"); }); $process->run(function ($output) use ($command) { $command->write($output); }); $process->isSuccessful(); // true $process->getExitCode(); // 0
You can also use the run()
method to run a process and get the output:
$output = sprout()->run('ls -la'); $command->write($output);
Composer & Npm Scripts
A lot of console applications allow you to install composer or npm dependencies, and sprout makes it a whole lot easier to do so:
sprout()->composer()->hasDependency('leafs/fs'); // true sprout()->composer()->run('composer-script'); sprout()->composer()->install(); // install all dependencies sprout()->composer()->install('leafs/fs'); // install a package sprout()->composer()->remove('leafs/fs'); // remove a package sprout()->npm()->hasDependency('@leafphp/vite'); // true sprout()->npm()->run('npm-script'); sprout()->npm()->install(); // install all dependencies sprout()->npm()->install('vite'); // install a package sprout()->npm()->remove('vite'); // remove a package
For npm, you can also specify the package manager you want to use:
sprout()->npm('yarn')->install(); // install all dependencies using yarn sprout()->npm('pnpm')->install('vite'); // install a package using pnpm sprout()->npm('bun')->install('vite'); // install a package using bun
All the composer and npm commands log the output to the console in real-time. If you want to change the output, you can pass a callback as the second argument:
sprout()->composer()->install(function ($output) { $command->write($output); }); sprout()->npm()->install('vite @leafphp/vite', function ($output) { $command->write("NPM -> $output"); });
The also return a process instance which you can use to get the output, check if the process was successful, and get the exit code:
$process = sprout()->composer()->install(); $process->isSuccessful(); // true