Command Interoperability and Laravel Artisan Applications

December 7, 2016 —John Koster

The typical Artisan command development workflow involves writing commands that interact with different components of the application or external services; the commands themselves are usually isolated in their scope and generally do not depend on other commands or operating system calls. However, it can be incredibly useful to interact with other Artisan commands and operating system features to create more robust commands or to automate different actions throughout the application.

#Executing Commands From Other Artisan Commands

Laravel's Illuminate\Console\Command base class provides useful methods for interoping with other Artisan commands: the call($command, array $arguments = []) and callSilent($command, array $arguments = []) methods. Both methods accept the name of the Artisan command to call (via an argument supplied to the $command parameter) and an optional array of $arguments that should be passed to the Artisan command.

Both commands can be used to call other Artisan commands and return the exit code from the command:

1<?php
2 
3namespace App\Console\Commands;
4 
5use Illuminate\Console\Command;
6 
7class ExampleCommand extends Command
8{
9 
10 // ...
11 
12 public function handle()
13 {
14 // Call the `migrate` command from
15 // within the ExampleCommand.
16 $exitCode = $this->call('migrate');
17 }
18 
19 // ...
20 
21}

The previous code example would execute the migrate Artisan command whenever the ExampleCommand executes. It is also possible to supply arguments and options to the commands that are called via the $arguments parameter. Options and arguments are supplied via an array. The argument and option names should be the key of the array element and the desired value should be the element's value.

The following example would call the migrate command while supplying options and arguments to the command:

1<?php
2 
3namespace App\Console\Commands;
4 
5use Illuminate\Console\Command;
6 
7class ExampleCommand extends Command
8{
9 
10 // ...
11 
12 public function handle()
13 {
14 // Call the `migrate` command from
15 // the ExampleCommand with options.
16 $exitCode = $this->call('migrate', [
17 '--force' => true,
18 '--path' => database_path('tenant_migrations')
19 ]);
20 }
21 
22 // ...
23 
24}

In the previous example, the ExampleCommand is calling the migrate Artisan command with the --force option flag and the --path option with a string value. For options that act as a flag, or do not accept a value, simply supply a truth value of either true or false as the value of the option (the --force option flag is an example of this).

It is important to note that, because of the way that command strings are internally constructed, it is still required to prefix command options with hyphens (--, or - for option shortcuts). Failing to due so will either supply a value to a similarly named argument or cause the command to throw a fatal exception.

Any output that is generated by called Artisan commands will still be displayed to the user executing the parent command when using the call method. To hide any output from other Artisan commands, simply use the callSilent method to execute the command without displaying any output from that command:

1<?php
2 
3namespace App\Console\Commands;
4 
5use Illuminate\Console\Command;
6 
7class ExampleCommand extends Command
8{
9 
10 // ...
11 
12 public function handle()
13 {
14 // Call the `migrate` command from
15 // the ExampleCommand with options.
16 $exitCode = $this->callSilent('migrate', [
17 '--force' => true,
18 '--path' => database_path('tenant_migrations')
19 ]);
20 }
21 
22 // ...
23 
24}

#Calling Commands From Application Code

The call and callSilent commands discussed in the previous section are defined as part of the Artisan Command base class, which is not incredibly useful when commands need to be executed from other locations within the application's code base. Luckily, the console kernel (Illuminate\Foundation\Console\Kernel) exposes a call($command, array $parameters = []) method that can be used to execute commands from arbitrary locations within the application's code base.

The following example demonstrates how to call an Artisan command from a controller by injecting the console kernel:

1<?php
2 
3namespace App\Http\Controllers;
4 
5use App\Console\Kernel;
6 
7class ExampleController extends Controller
8{
9 
10 protected $artisan;
11 
12 public function __construct(Kernel $artisan)
13 {
14 $this->artisan = $artisan;
15 }
16 
17 public function getIndex()
18 {
19 // Call an Artisan command from
20 // inside the controller.
21 $this->artisan->call('migrate', [
22 '--force' => true,
23 '--path' => database_path('tenant_migrations')
24 ]);
25 }
26 
27}

It is also possible to call Artisan commands using the Artisan command:

1<?php
2 
3namespace App\Http\Controllers;
4 
5use Illuminate\Support\Facades\Artisan;
6 
7class ExampleController extends Controller
8{
9 
10 public function getIndex()
11 {
12 // Call an Artisan command from
13 // inside the controller using
14 // the Artisan facade.
15 Artisan::call('migrate', [
16 '--force' => true,
17 '--path' => database_path('tenant_migrations')
18 ]);
19 }
20 
21}

#Operating System Calls

It is also possible to make operating system calls or interact directly with third-party systems using Symfony's Process component. While PHP provides its own features for making operating system calls, the Symfony component is capable of dealing with the nuances and differences between different operating system environments. Because Laravel already depends on the Process component, it does not have to be explicitly loaded using Composer, and is already available for use.

The following code example demonstrates how the Process component works for calling basic operating system features:

1<?php
2 
3use Symfony\Component\Process\Process;
4 
5// Create the process.
6$process = new Process('composer dump-autoload -o');
7 
8// Start the process.
9$process->run();
10 
11// Determine if the process failed.
12if (!$process->isSuccessful()) {
13 // Handle process failure.
14}

#Handling Process Output

When using Symfony's Process component to interact with third-party systems and to make calls to operating system features it may be required to retrieve the output from the process. A simple and effective way to retrieve real-time output from the process is to supply a callback to the process's run method.

The following code example demonstrates how to retrieve real-time output from a Symfony process and display it in the context of a Laravel command:

1<?php
2 
3namespace App\Console\Commands;
4 
5use Illuminate\Console\Command;
6use Symfony\Component\Process\Process;
7 
8class ExampleCommand extends Command
9{
10 
11 // ...
12 
13 public function handle()
14 {
15 // Call the `phpunit` library.
16 $process = new Process('phpunit');
17 
18 // Run the process while streaming
19 // the output to the appropriate
20 // output channels, using the
21 // Laravel command methods.
22 $exitCode = $process->run(function ($type, $buffer) {
23 if (Process::ERR === $type) {
24 // Write process error output to command output.
25 $this->error($buffer);
26 } else {
27 // Write process standard output to command info output.
28 $this->info($buffer);
29 }
30 });
31 }
32 
33 // ...
34 
35}

Some absolutely amazing
people

The following amazing people help support this site and my open source projects ♥️
If you're interesting in supporting my work and want to show up on this list, check out my GitHub Sponsors Profile.