On Console kernel of Laravel core interpretation

  • 2021-11-10 09:02:19
  • OfStack

Console kernel

In the last article, we introduced HTTP kernel of Laravel, and summarized in detail how HTTP kernel mobilized various core components of Laravel to complete tasks in the whole life cycle of network request from entering application to application processing request and returning HTTP response. In addition to handling HTTP requests, a robust application often needs to perform scheduled tasks, asynchronous queues, etc. Laravel designed artisan tool to meet these scenarios, defined various commands to meet various scenarios of non-HTTP requests through artisan tool, and artisan command completed the scheduling of application core components through Console kernel of Laravel to complete tasks. Today, we will learn the core code of Laravel Console kernel.

Kernel binding

Like the HTTP kernel 1, there is a kernel binding process in the application initialization stage. Register the Console kernel in the service container of the application, or refer to the code in bootstrap/app. php cited in the previous article


<?php
//  No. 1 1 Part:   Create an application instance 
$app = new Illuminate\Foundation\Application(
  realpath(__DIR__.'/../')
);

//  No. 1 2 Part:   Complete kernel binding 
$app->singleton(
  Illuminate\Contracts\Http\Kernel::class,
  App\Http\Kernel::class
);
// console Kernel binding 
$app->singleton(
  Illuminate\Contracts\Console\Kernel::class,
  App\Console\Kernel::class
);

$app->singleton(
  Illuminate\Contracts\Debug\ExceptionHandler::class,
  App\Exceptions\Handler::class
);

return $app;

Console kernel \App\Console\Kernel Inherit from Illuminate\Foundation\Console, In the Console kernel, we can register artisan commands and define scheduled tasks to be performed in the application.


/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
  // $schedule->command('inspire')
  //     ->hourly();
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
  $this->load(__DIR__.'/Commands');
  require base_path('routes/console.php');
}

When the Console kernel is instantiated, the kernel defines the command scheduling task for the application (the scheduling task defined in the shedule method)


public function __construct(Application $app, Dispatcher $events)
{
  if (! defined('ARTISAN_BINARY')) {
    define('ARTISAN_BINARY', 'artisan');
  }

  $this->app = $app;
  $this->events = $events;

  $this->app->booted(function () {
    $this->defineConsoleSchedule();
  });
}

Application parsing Console kernel

View aritisan We can see the source code of the file. After the binding of Console kernel binding is completed, the console kernel object will be parsed through the service container


$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);

$status = $kernel->handle(
  $input = new Symfony\Component\Console\Input\ArgvInput,
  new Symfony\Component\Console\Output\ConsoleOutput
);

Execute command task

After parsing the Console kernel object, The next step is to process the command request from the command line, We all know that PHP receives all command-line input through the global variable $_ SERVER ['argv'], Same as executing shell script 1 on the command line (in shell script, you can get the script file name through $0, and $1 $2, which are the parameter options passed to shell script in turn). Index 0 corresponds to the script file name. Then, all the parameter options passed to the script from the command line, so the command executed through the artisan script from the command line, the index 0 in the $_ SERVER ['argv'] array in the artisan script will always correspond to the string artisan, and the following parameters in the command line will correspond to the subsequent elements of the $_ SERVER ['argv'] array in turn.

Because command parameter options can be specified in the syntax of the artisan command, and some options can also specify arguments, In order to reduce the complexity of parsing command line input parameters, Laravel uses Symfony\ Component\ Console\ Input objects to parse these parameter options in the command line (shell script is actually one sample, and command line parameter input in various formats will be parsed through shell function getopts). Similarly, Symfony\ Component\ Console\ Output objects are used to abstract the standard output of the command line.

Guided application

In the handle method of the Console kernel, we can see that the bootstrapper program reference application 1 is used before the HTTP kernel processes the request. Before starting to process the command task, there will also be this 1 step operation of booting the application

Its parent class "IlluminateFoundationConsoleKernel" defines an array of bootstrap programs with the property name "bootstrappers" inside:


protected $bootstrappers = [
  \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
  \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
  \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
  \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
  \Illuminate\Foundation\Bootstrap\SetRequestForConsole::class,
  \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
  \Illuminate\Foundation\Bootstrap\BootProviders::class,
];

The boot program included in the array is basically the same as the boot program defined in the HTTP kernel, which is the environment variable, configuration file loading, registration exception handler, setting Console request, registration service container in the application, Facade and startup service. Among them, setting Console request is only one boot program different from HTTP kernel.

Execute a command

Execute commands are executed through Console Application, which inherits from the Symfony\ Component\ Console\ Application class of the Symfony framework, and execute commands through the corresponding run method.


name Illuminate\Foundation\Console;
class Kernel implements KernelContract
{
  public function handle($input, $output = null)
  {
    try {
      $this->bootstrap();

      return $this->getArtisan()->run($input, $output);
    } catch (Exception $e) {
      $this->reportException($e);

      $this->renderException($output, $e);

      return 1;
    } catch (Throwable $e) {
      $e = new FatalThrowableError($e);

      $this->reportException($e);

      $this->renderException($output, $e);

      return 1;
    }
  }
}

namespace Symfony\Component\Console;
class Application
{
  // Execute a command 
  public function run(InputInterface $input = null, OutputInterface $output = null)
  {
    ......
    try {
      $exitCode = $this->doRun($input, $output);
    } catch {
      ......
    }
    ......
    return $exitCode;
  }
  
  public function doRun(InputInterface $input, OutputInterface $output)
  {
    // Parse out the command name 
    $name = $this->getCommandName($input);
    
    // Analytic access parameter 
    if (!$name) {
      $name = $this->defaultCommand;
      $definition = $this->getDefinition();
      $definition->setArguments(array_merge(
        $definition->getArguments(),
        array(
          'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name),
        )
      ));
    }
    ......
    try {
      // Find out the command class (namespace, class name, etc.) by command name 
      $command = $this->find($name);
    }
    ......
    // Run command class 
    $exitCode = $this->doRunCommand($command, $input, $output);
    
    return $exitCode;
  }
  
  protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
  {
    ......
    // That executes the command class run Method to process tasks 
    $exitCode = $command->run($input, $output);
    ......
    
    return $exitcode;
  }
}

There are three main steps when executing the command:

Parse the command name and parameter options through command line input. Find the namespace and class name of the command class by command name. Execute the run method of the command class to complete the task processing and return the status code.

And command-line script specification 1, if the execution of the command task program successfully will return 0, throw an exception to exit will return 1.

Also, after opening the command class, we can see that there is no run method. We write the processing logic in the handle method. If you look at the code carefully, you will find that the run method is defined in the parent class, and the handle method defined in the subclass will be called in the run method meeting to complete the task processing. SOLID principle of object-oriented programming is strictly followed.

End application

After the program returns the status code after executing the command, it will directly output the status code through the exit ($status) function in artisan and end the PHP process. Next, the shell process will judge whether the script command is successfully executed according to whether the returned status code is 0.

Here, the program process started through the command line ends here. The Console kernel, like the HTTP kernel 1, is also responsible for scheduling throughout its lifecycle, However, the Http kernel finally lands the request in the Controller program, while the Console kernel lands the command line request in various command class programs defined in Laravel, and then we can write other programs in the command class to freely use various components in Laravel and services registered in the service container.

This article has been included in the series of articles Laravel source code learning, welcome to visit and read.


Related articles: