The Llama Commander
When I'm in an experimental mood, or working on a proof of concept, I like to code in a scrappy way. Anonymous functions allow me to avoid pausing my train of thought to think of a name for something. However, when I'm using the Symfony Console Component to write command-line utilities, it seems that I have to write a class for each command.
No more, I say!
I've created a tiny library called beryllium/llama
that lets me set the configuration and execution logic of console commands using anonymous functions, right in the constructor. When I found myself using the technique across several projects, I did what anyone would do - I gave it a silly name and threw it on GitHub and Packagist.
I even added unit tests - which, unsurprisingly, immediately revealed a few bugs that needed to be fixed. Yay for tests!
You can try it out in your Silex and/or Symfony Console Component project by running:
$ composer require beryllium/llama
Once it's installed, you can define a llama command like so:
#!/usr/bin/env php
<?php
require __DIR__ . '/vendor/autoload.php';
use Symfony\Component\Console\Application as ConsoleApplication;
use Beryllium\Llama\LlamaCommand;
$console = new ConsoleApplication;
$console->add(new LlamaCommand(
'namespace:commandname',
function ($config) { $config->setDescription('My Command'); },
function ($input, $output) { $output->writeln('Hello, Watson'); }
));
$console->run();
The documentation on how to work with the Symfony Console Component is pretty thorough, so the basic syntax above should be enough to get you started playing with things. In fact, you should be able to save that code as "console" in a project directory, mark it chmod a+x console
, and then run it as:
$ ./console namespace:commandname
If you invoke the console without specifying a command, the help will show your command(s):
$ ./console
Console Tool
Usage:
command [options] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output,
2 for more verbose output and 3 for debug
Available commands:
help Displays help for a command
list Lists commands
namespace
namespace:commandname My Command
I wouldn't recommend writing a large application using this technique. You will quickly get tangled in a giant console bootstrap file hundreds of lines long. But, if you're only going to be having a few short commands that do specific things, this could be a handy tool.
For my own projects, I tend to start out with the unstructured experimental approach. Once the concept is solid or the bootstrap code starts reaching into hundreds-of-lines territory, I add unit and functional tests and begin to refactor my anonymous functions into an actual hierarchy.
Please watch for my upcoming Building Web Apps with Silex 2 book, which will cover a variety of topics, including how to grow a project from a proof of concept to the best-architected application ever written*.
* As the book is not yet finished, anything is possible.
Mentioned in this post:
- Beryllium/Llama on GitHub
- Beryllium/Llama on Packagist
- Symfony Console Component Documentation
- Building Web Apps with Silex 2 (and other useful PHP libraries, too)
Published: October 19, 2015