Console Wars – PHP CLI Libraries

Share this article

I have always been a big fan of console commands and I try to provide a command line interface (CLI) as much as possible in most of my PHP projects.

In this article, I’ll briefly compare three PHP console command libraries:

Image of terminal screen

Origin Stories

The Symfony console is the oldest and the most popular one, used in many projects (and obviously part of the Symfony framework). With dozens of contributors, it became the first choice for many developers.

Hoa is a modular, extensible and structured set of PHP libraries that includes the Hoa console. It aims to be a bridge between industrial and research worlds, and this makes that project quite interesting.

The Webmozart console is the newest project, wants to be easier, test friendly and add new functionality on top of the Symfony console.

Dependencies, Size, and Complexity

The Symfony console has only suggested dependencies, as opposed to the Hoa console library that depends on some Hoa project libraries. The Webmozart project, too, directly depends on the Symfony console.

The Hoa console has the smallest number of LOC (Logical Lines of Code) ~1397, followed by the Symfony console ~2226 and the Webmozart ~3126 (without dependencies).

In order to have a rough indicator of the complexity of these projects, below is some data from their PHPLOC analysis*:


Description Symfony Hoa Webmozart
Cyclomatic Complexity
Average Complexity per LLOC 0.37 0.36 0.26
Average Complexity per Class 14.73 25.14 8.84
Average Complexity per Method 2.55 3.38 1.99
Dependencies
Global Accesses 3 20 1
Attribute Accesses 807 217 1285
Method Calls 1103 324 1320

*The analysis is performed only in the main source directory, excluding the test folder when present.

Practical example

Description

To have an overview of each library’s functionality and see the code in action, let’s write a business feature to describe a usage example:

Feature: I want to output a message to several people.
	The message should be passed via the `--message` option and should be optional (default="Hello"),
	the message should be followed by two or more names,
	the message should be coloured with `--color=` (default="white") and/or in uppercase with `--up` (default=lowercase).

The final console call should be something like:
somemsg --message='Good Morning' Nicola Bruno --color=green --up
and the output should be:

GOOD MORNING NICOLA AND BRUNO

Implementation

First, we need to define a PHP Message, used in every console implementation, to handle the example.

Below is some pretty straightforward code:

class Message
{
    /**
     * Construct the class and initializes the properties.
     * 
     * @param        $names
     * @param string $message
     * @param bool   $uppercase
     */
    public function __construct($names, $message="Hello", $uppercase=false)
    {
        $this->names = implode(' and ', $names);
        $this->message = $message;
        $this->uppercase = $uppercase;
    }

    /**
     * Generates the output.
     * 
     * @return string
     */
    public function getMessage()
    {
        $output =  $this->message . ' ' . $this->names;
        if ($this->uppercase) {
            $output = strtoupper($output);
        }

        return $output;
    }
}

Symfony console

In order to create a console command in Symfony, it’s necessary to:

  • Create the command class
    – Configure the arguments and options
    – Write the logic
  • Create the application

Create The command

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class MessageCommand extends Command
{
    /**
     * Configures the argument and options
     */
    protected function configure()
    {
        $this
            ->setName('demo:msg')
            ->setDescription('Simple message delivery')
            ->addArgument('names', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'Who do you want to message?')
            ->addOption('message', null, InputOption::VALUE_REQUIRED, 'Set the message', 'Hello')
            ->addOption('up', null, InputOption::VALUE_NONE, 'Set the output in uppercase')
            ->addOption('color', null, InputOption::VALUE_REQUIRED, 'Which colors do you like?', 'white')
        ;
    }

    /**
     * Executes the logic and creates the output.
     * 
     * @param InputInterface $input
     * @param OutputInterface $output
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $names = $input->getArgument('names');
        $message = $input->getOption('message');
        $uppercase = $input->getOption('up');
        $color = $input->getOption('color');

        $message = new Message($names, $message, $uppercase);
        $messageString = $message->getMessage();
        $coloredMsg = '<fg='.$color.'>'.$messageString.'</fg='.$color.'>';

        $output->writeln($coloredMsg);
    }
}

The configure method is used to set up the arguments and options for the command.

The addArgument method can receive the following parameters:
addArgument($name, $mode, $description, $default)


type name description
string $name The argument name
int $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
string $description A description text
mixed $default The default value (for InputArgument::OPTIONAL mode only)

The addOption can receive the following parameters:
addArgument($name, $shortcut, $mode, $description, $default)


type name description
string $name The option name
string $shortcut The shortcut (can be null)
int $mode The option mode: One of the InputOption::VALUE_* constants
string $description A description text
mixed $default The default value (must be null for InputOption::VALUE_REQUIRED or InputOption::VALUE_NONE)

There are three options available to color the output:

  • Use a preset tag (es: $output->writeln('<info>foo</info>'); for green output)
  • Define a style using the OutputFormatterStyle class
  • Set the color inside the tag name es:
// red text on a cyan background
 $output->writeln('<fg=red;bg=cyan>foo</fg=red;bg=cyan>');

The available foreground and background colors are: black, red, green, yellow, blue, magenta, cyan and white. The available options are: bold, underscore, blink, reverse and conceal.

More information in the official Symfony documentation.

Note: by default, the Windows command console doesn’t support output coloring. You’ll need (and should install) Git tools or another more advanced command console.

Create The application

After the configuration and the execution, we’re almost done. The last step is creating a PHP file to run the command.

//file myconsole.php

require __DIR__.'/vendor/autoload.php';

use MessageCommand;
use Symfony\Component\Console\Application;

$application = new Application();
$application->add(new MessageCommand());
$application->run();

Console call example:

php myconsole.php demo:msg Nicola Bruno --message='Good Morning' --color=blue --up

The Symfony console also automatically provides the output helper with the --help argument.

Hoa console

The Hoa console follows a less structured approach to configuring the console command.

That process consists of the following steps:

  • Parse the command
  • Get options and input
  • Execute the logic

Parse the command

/**
 * $argv contains an array of all the arguments passed to the script,
 * the first argument is always the name of the PHP file.
 * the Hoa Parser->parse method accept a string in input, so it's necessary to convert the $argv array in a string without the first argument as below.
 */
unset($argv[0]);
$command = implode(' ', $argv);

$parser = new Hoa\Console\Parser();
$parser->parse($command);

//options definition
//['longname', TYPE, 'shortname']
$options = new Hoa\Console\GetOption(
    [
        ['up', Hoa\Console\GetOption::NO_ARGUMENT, 'u'],
        ['message', Hoa\Console\GetOption::REQUIRED_ARGUMENT, 'm'],
        ['color', Hoa\Console\GetOption::OPTIONAL_ARGUMENT, 'c']
    ],
    $parser
);

Get options and inputs

//definition of default values
$uppercase = false;
$message = "Hello";
$color = "white";

$names = $parser->getInputs();

//The following while with the switch will assign the values to the variables.
while (false !== $shortName = $options->getOption($value)) {
    switch ($shortName) {
        case 'u':
            $uppercase = true;
            break;
        case 'm':
            $message = $value;
            break;
        case 'c':
            $color = $value;
            break;
    }
}

Execute the logic

$message = new Message($names, $message, $uppercase);
$messageString = $message->getMessage();

Hoa\Console\Cursor::colorize('fg('.$color.')');
echo $messageString;
Hoa\Console\Cursor::colorize('fg(white)'); //reset the cursor to default white

For coloring the output, it’s possible to change the Cursor color.

The Hoa console supports a wide range of colors.
The color can be set by name (black, red, green, yellow…), by number (from 0 to 256 representing the 264 color palette) or by hexadecimal code #rrggbb, example:
Hoa\Console\Cursor::colorize('fg(yellow) bg(#932e2e) underlined');

The basic usage in the example doesn’t provide an automatic helper output, and is not strongly OOP oriented but extending the Hoa\Console\Dispatcher\Kit (requires hoa/dispatcher) could add more flexibility (more information in the official documentation)

The command can be called with:

php message.php  -u --message=Hello --color=green Nicola Bruno

One of the strongpoints of the Hoa console is that it provides additional API classes to manipulate important elements, supporting the different terminal profiles:

  • The Cursor (move, clear, show, colorize, …)
  • The Mouse (listening to mouse action)
  • The Window (setSize, scroll, minimize, …)
  • The terminal line with Readline (history, autocompletion, etc)

Webmozart console

The Webmozart console command creation workflow consists of:

  • configuring the argument and options
  • writing the logic
  • creating the application

Webmozart’s console follows an approach similar to the Symfony Console, but with a clear separation between the configuration and the logical execution.

Configuration

use Webmozart\Console\Api\Args\Format\Argument;
use Webmozart\Console\Api\Args\Format\Option;
use Webmozart\Console\Config\DefaultApplicationConfig;

/**
 * Configuration of arguments and options
 */
class MsgApplicationConfig extends DefaultApplicationConfig
{
    protected function configure()
    {
        parent::configure();

        $this
            ->setName('msg')
            ->setVersion('0.1')
            ->beginCommand('msg')
                ->setDescription('Show a nice message')
                ->setHandler(new MsgCommandHandler())
                ->addArgument('names', Argument::MULTI_VALUED | Argument::REQUIRED, 'Who do you want to message?')
                ->addOption('message', null, Option::OPTIONAL_VALUE, 'Set the message', 'Hello')
                ->addOption('up', null, Option::NO_VALUE, 'Set the output in uppercase')
                ->addOption('color', null, Option::REQUIRED_VALUE, 'Which colors do you like?', 'white')
            ->end()
        ;
    }
}

Logic

use Webmozart\Console\Api\Args\Args;
use Webmozart\Console\Api\IO\IO;

/**
 * Handling the command logic
 */
class MsgCommandHandler
{
    public function handle(Args $args, IO $io)
    {
        //gets the argument and option	
        $names = $args->getArgument('names');
        $message = $args->getOption('message');
        $uppercase = $args->getOption('up');
        $color = $args->getOption('color');
        
        $message = new Message($names, $message, $uppercase);
        $messageString = $message->getMessage();
        
        $coloredMsg = '<fg='.$color.'>'.$messageString.'</fg='.$color.'>';
        
        $io->writeLine($coloredMsg);
    }
}

The strong separation of configuration and logic allows more flexibility for easy testing and for a project that will grow with additional commands.

Other advantages of the Webmozart’s console are:

  • sub commands support:

    php mycommand.php msg send --arg1 --arg2
    	 php mycommand.php msg receive --someoptions=somevalue
  • support for manpage documentation (like with “git help remote”)

  • adapters for the Symfony console (to use Symfony’s classes like ProgressBar)

Creating The application

The application file to run the command is similar to the Symfony one:

require 'vendor/autoload.php';
use Webmozart\Console\ConsoleApplication;

$cli = new ConsoleApplication(new MsgApplicationConfig());
$cli->run();

Console call:

php myconsole.php msg --message='Good Morning' Nicola Bruno --color=blue --up

Final Thoughts

Each console covered above provides different functionalities for different use types and user preferences.

  • The Symfony console is well tested, robust, with good documentation and features to solve most of the average use cases.
  • The Hoa console is more industry oriented, perfect for manipulating the terminal environment (mouse, cursor, window, and so on).
  • The Webmozart console is new (there will be a stable release soon) but it’s very useful for handling projects that tend to grow to large sizes.

Do you use any of them regularly? If so, which one? Why? What would you say the pros and cons of each are? Do you have other contenders to suggest? Let us know!

Frequently Asked Questions (FAQs) about PHP CLI Libraries

What are the key differences between PHP CLI and other command-line interfaces?

PHP CLI, or Command Line Interface, is a method of interacting with a computer program where the user issues commands to the program in the form of successive lines of text. Unlike other command-line interfaces, PHP CLI is specifically designed for PHP scripts. It’s not dependent on the server environment, which means it can run in any system that has PHP installed. It also has no time limits for script execution, making it ideal for long-running scripts.

How can I install PHP CLI libraries?

PHP CLI libraries can be installed using Composer, a tool for dependency management in PHP. You can specify the libraries you need in a composer.json file, and Composer will handle the downloading and installation. You can also manually download the libraries from their respective repositories and include them in your project.

What are some popular PHP CLI libraries and their features?

Some popular PHP CLI libraries include Symfony Console, Hoa Console, and CLImate. Symfony Console provides a simple API for creating command-line commands, while Hoa Console offers a rich layer of abstraction for the terminal. CLImate allows you to output colored text, special formats, and even animations.

How can I create my own PHP CLI application?

Creating a PHP CLI application involves writing a PHP script that can be run from the command line. You’ll need to start with a shebang line to specify the PHP interpreter, and then you can write your PHP code as usual. You’ll also need to set the correct permissions on the file to make it executable.

Can I use PHP CLI libraries with web-based PHP applications?

Yes, PHP CLI libraries can be used with web-based PHP applications. However, keep in mind that CLI scripts run in a different environment than web scripts. They have different configuration settings and do not have access to web-specific PHP features.

How can I handle input and output in PHP CLI?

PHP CLI provides several ways to handle input and output. You can use the standard input, output, and error streams provided by PHP, or you can use the features provided by your CLI library. For example, Symfony Console provides input and output objects that you can use to interact with the user.

What are the benefits of using PHP CLI libraries?

PHP CLI libraries provide a number of benefits. They abstract away the complexities of dealing with the command line, allowing you to focus on your application logic. They also provide helpful features like color output, input validation, and progress bars.

Can I use PHP CLI libraries in non-CLI PHP scripts?

While it’s technically possible to use PHP CLI libraries in non-CLI PHP scripts, it’s generally not recommended. These libraries are designed to work with the command line and may not function correctly in a web environment.

How can I debug PHP CLI scripts?

Debugging PHP CLI scripts is similar to debugging web-based PHP scripts. You can use echo statements, var_dump, or print_r to output variable values. You can also use a PHP debugger like Xdebug.

Are there any limitations or drawbacks to using PHP CLI libraries?

One potential drawback of using PHP CLI libraries is that they add an extra layer of complexity to your code. If you’re not familiar with the library, it can also be a learning curve to get up to speed. Additionally, not all features of CLI libraries may be available on all systems, particularly on Windows.

Nicola PietroluongoNicola Pietroluongo
View Author

Nicola Pietroluongo is a software engineer with many years of experience, open source enthusiast, now creating and contributing to awesome PHP web projects.

BrunoScliconsolehoaOOPHPPHPSymfony Console
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week