In a current customer project, I needed the ability to run commands from the CLI.

Symfony provides a console component which is, among others, used by zendframework/zend-expressive-tooling. The documentation got me off to a quick start and I got my first Hello command running within minutes.

I wanted to provide a re-usable package, where the implementation can provide the definition of a command list.

Since I am not using the package within a Symfony application, I needed somehow access to the Service Container of the Expressive application. I quick search on the internet did not show up any useful results and I was left to use my own brain.

My starting point was the public/index.php provided by Expressive. From there it was just a small step of adjustments and I could access the configuration by simply loading the service container via $container = require 'config/container.php';.

The next task was to use a factory service for commands that have dependencies. Since I already have the service container, I can simply get the command from it.

The end-result looks like so:

#!/usr/bin/env php
<?php

declare(strict_types = 1);

namespace Arueckauer\Console;

use PackageVersions\Versions;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;

require 'vendor/autoload.php';

/** @var ContainerInterface $container */
$container = require 'config/container.php';

$commandsConfig = require 'config/autoload/commands.global.php';
$commandList = [];
foreach ($commandsConfig[CommandList::class] as $command) {
    if ($command instanceof Command) {
        $commandList[] = $command;
    } else {
        $commandList[] = $container->get($command);
    }
}

$application = new Application('Console');
$application->addCommands($commandList);
$application->run();

The configuration of commands is placed in a configuration file like config/autoload/commands.global.php.

<?php

declare(strict_types = 1);

use Command\Hello;
use Command\NeedSomeDependencies;
use Elstr\Console\CommandList;

return [
    CommandList::class => [
        new Hello(),
        NeedSomeDependencies::class,
    ],
];

A day after I completed my implementation, two users mentioned an interesting post by Geert Eltink (aka @xtreamwayz) from 2016 zend-expressive console cli commands and the zfcampus/zf-console library.

Conversation on Slack

(I blacked out the usernames, since I do not have their permission to use them.)

Update 2019-10-18: In an additional post, I show two more real-world examples.