CLI Command
The bin/php-command
serves as an advanced foundation for developing robust CLI applications.
Leveraging the capabilities of
the Symfony Console component,
this template is designed to accommodate complex business logic and extensive
functionalities. Unlike single-file script, which is intended for
simpler tasks, this template is structured to evolve into a comprehensive CLI
tool.
Symfony Console
The Symfony Console component brings a wealth of features for building CLI applications, making it a go-to choice for developers looking to create robust, scalable command-line tools. One of its main features is the ability to define commands, options, and arguments in a structured manner. This is beneficial for applications that have multiple functionalities, as each can be encapsulated into separate commands.
For instance, in the php-command example, two commands—JokeCommand
and SayHelloCommand—are defined and added to the application. The add method
allows you to register multiple commands easily, creating a modular CLI tool. By
doing this, you enable your application to handle diverse tasks, each accessed
via its own command.
Another advantage is the ability to set a default command
using setDefaultCommand, which is executed when no command is explicitly
specified. This is particularly useful for applications that have a primary
function which is most commonly used.
Beyond commands, Symfony Console also offers robust error handling, input/output abstraction, and helpers for generating styled console output. All these features collectively contribute to creating a professional-grade CLI application.
Example commands
This template includes two example commands: SayHelloCommand
and JokeCommand.
The SayHelloCommand command:
- Called using
app:say-hello. - Provides additional help details for the user.
- Prints
Hello, Symfony console!when run. - Signals successful execution by returning a success status.
This command is a good example of a simple command that can be used as a starting point.
The JokeCommand command:
- Called with the command
app:joke. - Accepts an optional
topicparameter for the joke. - Retrieves a joke from an external API based on the topic.
- Displays the setup and punchline of the joke.
- Shows an error message if it can't retrieve a joke.
- Returns a success status if the joke is displayed, or a failure status otherwise.
This is a more complex command that demonstrates how to handle arguments and options, as well as how to make external API calls.
Both commands are registered in the bin/php-command file and can be tested with
unit tests.
This template provides a robust set of unit tests to get you started.
Command structure
In the CLI application, two key components build the overall structure:
-
Bootstrap file: This is the script that initializes your application. It looks for Composer's autoload file to include all dependencies, then initializes the Symfony Console Application object. You add commands to it, like
JokeCommandandSayHelloCommand, and finally run the application. This acts as the heart of your CLI application, tying together all the commands you've created. -
Command class(es): These are the actual commands your application will execute. Each command class is responsible for a specific action like saying hello or telling a joke. These classes extend Symfony's Command class and are where you define your business logic. Optionally, you may also include unit tests for your commands to verify they work as expected.
These components work together to create a cohesive CLI application, allowing for extensible and maintainable code.
Getting Started
Depending on the complexity of your CLI application, you may chose a simpler
SayHelloCommand or a more complex JokeCommand as a starting point.
Start by renaming the command class and file to match your application.
Next, adjust the configure function to define the command name, description,
and other details. You can also add arguments and options here.
Your core logic will be housed in the execute function. Simply replace the
existing code with your own.
Lastly, you can update unit tests in the tests/phpunit/Command directory.
You can then run your script from the command line
using ./bin/php-command.
Authoring tests
This template includes
a tests/phpunit/Unit/Commands
directory with a sample Unit tests.
The Symfony Console component provides a CommandTester class that allows you
to
test commands in a controlled environment. This is useful for testing commands
that make external API calls or interact with the file system.
The CommandTester class provides methods for simulating user input and
capturing output, allowing you to test the command's behavior without actually
running it.
This template
provides CommandUnitTestCase.php
base class for all command Unit tests. It also includes several traits.
SayHelloCommandTest.php
and
JokeCommandTest.php
are two examples of Unit tests for SayHelloCommand and JokeCommand commands.
Distribution
A CLI application ships as a PHAR file, as a Composer package or as both. The template supports all 3 from 1 layout:
- The application code lives in
src/. - 1 thin launcher,
bin/php-command, loads the Composer autoloader and runs the application. composer.jsonlists the launcher underbin.
The bin/<name> launcher is the layout Composer, Box, PHP-Scoper and Rector
use for their own executables.
As a PHAR
A PHAR is a single executable file that bundles the application and its dependencies. Users need only PHP to run it.
composer build compiles the PHAR into dist/php-command.phar. Git ignores
dist, so the PHAR is never committed. The release workflow uploads it as a
release asset. See PHP Packaging for the Box
configuration.
As a Composer package
Publish the project to Packagist.org, the Composer package registry. Composer
links every bin entry into the vendor/bin directory of the project that
requires the package, so that project runs vendor/bin/php-command.
The launcher looks for the requiring project's autoloader before its own, so it runs both as a dependency and from its own checkout.
As both
Keep the single launcher. Composer links bin/php-command, and Box compiles
the same file into the PHAR, so a change to the launcher reaches both builds.
PHAR through Composer
Some tools, such as PHPStan, commit the built PHAR to their Composer package
and point a small bin/ launcher at it. Composer users then get the compiled
build instead of the source. The template ships the source through Composer:
dist is git-ignored, so the PHAR stays out of the package.