CLI applications are extremely useful for many, if not most web projects. The Symfony framework even goes so far as to include an extensible CLI console used for everything from running cache cleanup/warmup tasks, to user account management.
Many CLI scripts for web projects consist of just a static .php
file which works fine but grow unweildy over time. Thankfully, the
aforementioned Symfony Console component is released as a decoupled standalone that can be installed and setup easily and provide us
with structure and organization (and some powerful features).
Setup
The Symfony Console component can be found on GitHub at github.com/symfony/console. While installing by hand is doable, I much prefer using Composer to handle my dependencies. If you haven’t used Composer before, I suggest following the Getting Started documentation.
To start things off, we’ll need to setup, or modify, our composer.json
file:
1 2 3 4 5 6 7 8 |
|
And then update our dependencies:
1
|
|
Now that we have Symfony Console and all it’s dependencies downloaded and ready, we’ll need to setup our actual console executable:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Afterwards a simple chmod +x bin/console
will make it executable and you’ll be ready to begin. You’ll notice we placed this in a
file named simply console
in the bin
directory, this is purely a stylistic choice, name it whatever you want. Giving it a test
whirl, you should see the output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Commands
Syfmony Console commands are simply classes you override and inject into the application instance. The skeleton for any given command looks something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
While we have created the class, we haven’t attached it to our console application. To do this we edit our bin/console
file
and add the follwoing code between the new Application(...)
and the $app->run()
:
1 2 3 4 5 6 7 8 |
|
When we run our bin/console
we’ll notice the output is now midly different:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
And we can even use the help command to describe our newly created test command:
1 2 3 4 5 6 7 |
|
Output
Syfmony Console applications direct all their output through the $output
variable provided. What these output objects gives
us is easy ANSI coloring utilizing XML-like tags. For example, a $output->writeln("<info>This will be green</info> This will be white");
will print the ecapsulated text in the “info” style which is green. You can add and override styles to your hearts content, just
peruse the official documentation.
Input
CLI input comes in 2 flavors: Arguments and Options. In essence, Options are flags using the - or – operators and Arguments are your classic space separated values. To put it visually:
1
|
|
Symfony Console requires you to be strict and actively provide your definition in the configure()
method. For example, we can
setup an option and an argument:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Here we have defined a --flag
option which has no value expected (meaning –flag=value would be technically be illegal) and an
array argument (which means an infinite series, e.g. bin/console activities1 activities2
, etc). When we run our help command,
you’ll notice our output has changed to reflect our definition:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Now when our command is actually running we can check whether or not a flag was issued with a simple $input->getOption('flag');
as well as we can get all of our activities with a $input->getArgument('activities');
.
There are many modifiers to be used with both Arguments and Options. For example, you can have required Arguments, Options that require a value to be set, so on and so forth. You can view the official documentation to get an idea of the power available to you.
Integrating With Your Application
The bulk of power you gain from writing CLI apps comes from the ability to share code and resources with your main application.
Symfony Console does not provide an “out of the box” way to integrate with anything, however it is fairly trivial to roll in support on your own.
For example, say you were building a CLI app to complement your Silex web application. You have configured services and database connections you would like to share via the Silex DI container/application object.
A best practice would be to create a base command class that can accept your DI container and have your commands inherit from it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
And update your bin/console
file to include the PHP file responsible for creating and configuring the Application object and pass
said Application object into the commands:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Another path is to contemplate overloading the actualy Symfony Console Application class. Doing this affords you the ability to override the help message as well as any global option definitions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
Which has another added benefit of keeping your bin/console
file simple:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Environment
One thing to be aware of, most web applications these days are aware of environments when it comes to what configurations to use.
The Symfony Console Application object allows you to manually create your $input
and $output
options, which allows us to
perform a neat little trick and gain early access to the option parsing.
If you look at lines 19 and 20 below you can see us checking the --env
option to see if it’s set, if not using a default environment
variable MYAPP_ENV
from the shell or our fallback ‘dev’ environment designation. We then just go ahead and forward our newly
created $input
and $output
objects through our run()
method as shown on line 23.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
As an added bonus, this same concept can be applied to creating your own Output class. The Composer project actively makes use of this to extend in the ability to do overwrites in the console (the technique used for it’s fancy progress indication).