On this page
Command chains
Sometimes you may want to execute another command straight after the successful completion of another command. As an example, consider the following two command classes:
namespace MyApp\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class FirstCommand extends Command
{
/** @var string */
protected static $defaultName = 'first-command';
protected function configure() : void
{
$this->setName(self::$defaultName);
$this->addOption('name', null, InputOption::VALUE_REQUIRED, 'Module name');
}
protected function execute(InputInterface $input, OutputInterface $output) : int
{
$output->writeln('First command: ' . $input->getOption('name'));
return 0;
}
}
namespace MyApp\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class SecondCommand extends Command
{
/** @var string */
protected static $defaultName = 'second-command';
protected function configure() : void
{
$this->setName(self::$defaultName);
$this->addOption('module', null, InputOption::VALUE_REQUIRED, 'Module name');
}
protected function execute(InputInterface $input, OutputInterface $output) : int
{
$output->writeln('Second command: ' . $input->getOption('module'));
return 0;
}
}
We can expose each to laminas-cli, and also create a chain, whereby when the first command finishes execution, it will then invoke the second:
namespace MyApp\Command;
return [
'laminas-cli' => [
'commands' => [
'first-command' => Command\FirstCommand::class,
'second-command' => Command\SecondCommand::class,
],
'chains' => [
Command\FirstCommand::class => [
Command\SecondCommand::class => ['--name' => '--module'],
],
],
],
];
"chains" configuration options
We discuss chain configuration in more detail below.
Running ./vendor/bin/laminas first-command
will result with:
$ ./vendor/bin/laminas first-command --name=Foo
First command: Foo
Executing second-command. Do you want to continue?
[Y] yes, continue
[s] skip this command,
[n] no, break
> yes, continue
Second command: Foo
Please note that only successful result of the first command will trigger the second command. The final result (exit code) of the chained commands will be the result of the last executed command. If a command in the middle of the chain results in a failure status, execution will halt with that command, and its status will be returned.
Chain configuration
Chain configuration is under the "chains" section of the "laminas-cli" configuration:
<?php
return [
'laminas-cli' => [
'chains' => [ /* . . . */ ],
],
];
The configuration is expected to be an associative array mapping command names you have previously defined in the "commands" section of the "laminas-cli" configuration, and the value is an associative array:
'chains' => [
COMMAND_CLASS_NAME => CHAINED_COMMANDS
],
The chained commands (CHAINED_COMMANDS
) are themselves an associative array,
where the key is the name of a command you have already defined in the
"commands" section of the "laminas-cli" configuration, and the value is an
associative array:
'chains' => [
COMMAND_CLASS_NAME => [
CHAINED_COMMAND_CLASS_NAME => INPUT_MAPPER,
],
],
An input mapper (INPUT_MAPPER
) can be one of two things:
- a string class name of an implemention of
Laminas\Cli\Input\Mapper\InputMapperInterface
- an array specification
Most commonly, you will use an array specification. In this case, items can take two forms:
- a key/value pair, where the key is the option or argument from the previous command, and the value is the option or argument by which to provide the value to the chained command.
- an array, with a single key/value pair of the option or argument name on the chained command, and the value to use with it.
As a visualization:
'chains' => [
COMMAND_CLASS_NAME => [
'argument-on-previous-command' => 'argument-on-this-command',
'--option-on-previous-command' => '--option-on-this-command',
['an-argument-on-this-command' => 'argument value to supply'],
['--an-option-on-this-command' => 'option value to supply'],
],
],
When specifying options, the --
prefix should be used with the option names,
just like you'd invoke them from the command line if you were to call the
command by itself.
Chain command input mapper example
If we return to the original example from the first section of this page:
namespace MyApp\Command;
return [
'laminas-cli' => [
'commands' => [
'first-command' => Command\FirstCommand::class,
'second-command' => Command\SecondCommand::class,
],
'chains' => [
Command\FirstCommand::class => [
Command\SecondCommand::class => ['--name' => '--module'],
],
],
],
];
We have defined two commands, FirstCommand
and SecondCommand
. FirstCommand
defines the option --name
, while SecondCommand
defines the option
--module
. In the above configuration, we indicate that when we call
FirstCommand
, we want to start a command chain that also invokes
SecondCommand
. When it does so, it should take the value provided via the
--name
option and pass that value to the SecondCommand
--module
option. In
effect, that would be similar to calling the following commands in sequence:
$ ./vendor/bin/laminas first-command --name Foo
$ ./vendor/bin/laminas second-command --module Foo
Since FirstCommand
now provides a chain, we can call:
$ ./vendor/bin/laminas first-command --name Foo
and Foo
will be passed for the --module
option when SecondCommand
is
invoked as part of the chain.
InputMapperInterface
As noted in the previous section, you can provide a string class name of a
Laminas\Cli\Input\Mapper\InputMapperInterface
implementation to use in order
to map arguments and options from one command to another. That interface defines
one method:
namespace Laminas\Cli\Input\Mapper;
use Symfony\Component\Console\Input\InputInterface;
interface InputMapperInterface
{
public function __invoke(InputInterface $input): array;
}
The return value should be an associative array mapping arguments and options to
the values they contain, suitable for use with
Symfony\Component\Console\Input\ArrayInput
(see symfony/console documentation
for details);
Example
The initial section of this page
[
'name' => 'module', // adds "module" argument to the next command call with the value of "name" argument from the previous command
'--mode' => '--type', // adds "--type" option to the next command call with the value of "--mode" option from the previous command
['additional-arg' => 'arg-value'], // adds "additional-arg" argument to the next command call with the value "arg-value"
['--additional-opt' => 'opt-value'], // adds "--additional-opt" option to the next command call with the value "opt-value"
],
It is also possible to provide class name (string) which implements Laminas\Cli\Input\Mapper\InputMapperInterface
if you need more customised mapper between input of the previous and next command.