🌱 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
You can install Leaf Sprout using the Leaf CLI or Composer:
leaf install sprout
# or with composer
composer require leafs/sprout
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
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);
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!');
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);
});
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);
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