Laravel核心解读--Console内核

Console内核

上一篇文章我们介绍了Laravel的HTTP内核,详细概述了网络请求从进入应用到应用处理完请求返回HTTP响应整个生命周期中HTTP内核是如何调动Laravel各个核心组件来完成任务的。除了处理HTTP请求一个健壮的应用经常还会需要执行计划任务、异步队列这些。Laravel为了能让应用满足这些场景设计了artisan工具,通过artisan工具定义各种命令来满足非HTTP请求的各种场景,artisan命令通过Laravel的Console内核来完成对应用核心组件的调度来完成任务。 今天我们就来学习一下Laravel Console内核的核心代码。

内核绑定

跟HTTP内核一样,在应用初始化阶有一个内核绑定的过程,将Console内核注册到应用的服务容器里去,还是引用上一篇文章引用过的bootstrap/app.php里的代码


<?php
// 第一部分: 创建应用实例
$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

// 第二部分: 完成内核绑定
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);
// console内核绑定
$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内核 \App\Console\Kernel继承自Illuminate\Foundation\Console, 在Console内核中我们可以注册artisan命令和定义应用里要执行的计划任务。


/**
* 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');
}

在实例化Console内核的时候,内核会定义应用的命令计划任务(shedule方法中定义的计划任务)


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();
    });
}

应用解析Console内核

查看aritisan文件的源码我们可以看到, 完成Console内核绑定的绑定后,接下来就会通过服务容器解析出console内核对象


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

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

执行命令任务

解析出Console内核对象后,接下来就要处理来自命令行的命令请求了, 我们都知道PHP是通过全局变量$_SERVER[‘argv‘]来接收所有的命令行输入的, 和命令行里执行shell脚本一样(在shell脚本里可以通过$0获取脚本文件名,$1 $2这些依次获取后面传递给shell脚本的参数选项)索引0对应的是脚本文件名,接下来依次是命令行里传递给脚本的所有参数选项,所以在命令行里通过artisan脚本执行的命令,在artisan脚本中$_SERVER[‘argv‘]数组里索引0对应的永远是artisan这个字符串,命令行里后面的参数会依次对应到$_SERVER[‘argv‘]数组后续的元素里。

因为artisan命令的语法中可以指定命令参数选项、有的选项还可以指定实参,为了减少命令行输入参数解析的复杂度,Laravel使用了Symfony\Component\Console\Input对象来解析命令行里这些参数选项(shell脚本里其实也是一样,会通过shell函数getopts来解析各种格式的命令行参数输入),同样地Laravel使用了Symfony\Component\Console\Output对象来抽象化命令行的标准输出。

引导应用

在Console内核的handle方法里我们可以看到和HTTP内核处理请求前使用bootstrapper程序引用应用一样在开始处理命令任务之前也会有引导应用这一步操作

其父类 「IlluminateFoundationConsoleKernel」 内部定义了属性名为 「bootstrappers」 的 引导程序 数组:


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,
];

数组中包括的引导程序基本上和HTTP内核中定义的引导程序一样, 都是应用在初始化阶段要进行的环境变量、配置文件加载、注册异常处理器、设置Console请求、注册应用中的服务容器、Facade和启动服务。其中设置Console请求是唯一区别于HTTP内核的一个引导程序。

执行命令

执行命令是通过Console Application来执行的,它继承自Symfony框架的Symfony\Component\Console\Application类, 通过对应的run方法来执行命令。


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
{
    //执行命令
    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)
    {
       //解析出命令名称
       $name = $this->getCommandName($input);

       //解析出入参
       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 {
            //通过命令名称查找出命令类(命名空间、类名等)
            $command = $this->find($name);
        }
        ......
        //运行命令类
        $exitCode = $this->doRunCommand($command, $input, $output);

        return $exitCode;
    }

    protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
    {
        ......
        //执行命令类的run方法来处理任务
        $exitCode = $command->run($input, $output);
        ......

        return $exitcode;
    }
}

执行命令时主要有三步操作:

  • 通过命令行输入解析出命令名称和参数选项。
  • 通过命令名称查找命令类的命名空间和类名。
  • 执行命令类的run方法来完成任务处理并返回状态码。

和命令行脚本的规范一样,如果执行命令任务程序成功会返回0, 抛出异常退出则返回1。

还有就是打开命令类后我们可以看到并没有run方法,我们把处理逻辑都写在了handle方法中,仔细查看代码会发现run方法定义在父类中,在run方法会中会调用子类中定义的handle方法来完成任务处理。 严格遵循了面向对象程序设计的SOLID 原则。

结束应用

执行完命令程序返回状态码后, 在artisan中会直接通过exit($status)函数输出状态码并结束PHP进程,接下来shell进程会根据返回的状态码是否为0来判断脚本命令是否执行成功。

到这里通过命令行开启的程序进程到这里就结束了,跟HTTP内核一样Console内核在整个生命周期中也是负责调度,只不过Http内核最终将请求落地到了Controller程序中而Console内核则是将命令行请求落地到了Laravel中定义的各种命令类程序中,然后在命令类里面我们就可以写其他程序一样自由地使用Laravel中的各个组件和注册到服务容器里的服务了。

本文已经收录在系列文章Laravel源码学习里,欢迎访问阅读。

原文地址:https://segmentfault.com/a/1190000017218756

原文地址:https://www.cnblogs.com/lalalagq/p/10110922.html

时间: 2024-10-04 08:50:09

Laravel核心解读--Console内核的相关文章

Laravel核心解读--HTTP内核

Http Kernel Http Kernel是Laravel中用来串联框架的各个核心组件来网络请求的,简单的说只要是通过public/index.php来启动框架的都会用到Http Kernel,而另外的类似通过artisan命令.计划任务.队列启动框架进行处理的都会用到Console Kernel, 今天我们先梳理一下Http Kernel做的事情. 内核绑定 既然Http Kernel是Laravel中用来串联框架的各个部分处理网络请求的,我们来看一下内核是怎么加载到Laravel中应用实

Laravel 核心--Facades 门面

Laravel 核心--Facades 门面 伊Summer 关注 0.1 2017.08.12 19:07* 字数 2017 阅读 1089评论 0喜欢 5 介绍 Facades 为应用的 IoC 服务容器 的类提供了一个静态的接口.Laravel 里面自带了一些 Facades,如Cache等.Laravel 的门面作为服务容器中底层类的“静态代理”,相比于传统静态方法,在维护时能够提供更加易于测试.更加灵活.简明优雅的语法. 解释 在 Laravel 应用这个上下文里面,一个 Facade

Redis核心解读(转)

原文:Redis核心解读 Redis是知名的键值数据库,它广泛用于缓存系统.关于Redis的信息已经不用我多介绍了.这个系统的Redis文章主要从另外一个角度关注,Redis作为一个开源项目,短短2W行代码包含了一个健壮的服务器端软件的必需,我们从Redis中可以学习C语言项目的编程风格.范式,学习类Unix下的系统编程,还有对于一个常驻服务的健壮性考虑等等. 对于一个C语言的初学者来说,学习一个类似Redis这样不大不小的项目是非常好的选择.Redis既没有Nginx深入性能细节的晦涩编码方式

js学习总结----核心解读

下面是对jquery源码的简单解读: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script> //window.jQuery = window.$ = jQuery(jQuery就是那个闭包中的私

laravel 核心类Kernel

vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php.是laravel处理网络请求的最核心类,在app容器准备好了之后,就会调用本类,之后所有的处理都在此类中. 初始化 1. 调用router,因为router已经中app中注册,所以,直接make就行. 2. 添加middleware,首先添加如下默认的middleware: $middlewarePriority = [                \Illum

小白也能看懂的 Laravel 核心概念讲解

自动依赖注入 什么是依赖注入,用大白话将通过类型提示的方式向函数传递参数. 实例 1 首先,定义一个类: /routes/web.php class Bar {} 假如我们在其他地方要使用到 Bar 提供的功能(服务),怎么办,直接传入参数即可: /routes/web.php Route::get('bar', function(Bar $bar) { dd($bar); }); 访问 /bar,显示 $bar 的实例: Bar {#272} 也就是说,我们不需要先对其进行实例!如果学过 PH

laravel框架的学习笔记(一) 运行逻辑

1.laravel应用的入口文件都是public/index.php文件,所有请求都会被web服务器导向这个文件.这里是加载框架其它部分的起点. index.php文件载入composer生成的自动加载设置,然后从bootstrap/app.php脚本获取Laravel应用实例,Laravel的第一个动作就是创建服务容器实例. 2.HTTP/Console内核 接下来,请求被发送到HTTP内核或者Console内核,这取决于进入应用的请求类型.这两个内核是所有请求要经过的中央处理器,现在,就让我

PHPer面试指南-laravel 篇

简述 Laravel 的生命周期 Laravel 采用了单一入口模式,应用的所有请求入口都是 public/index.php 文件. 注册类文件自动加载器 : Laravel通过 composer 进行依赖管理,无需开发者手动导入各种类文件,而由自动加载器自行导入. 创建服务容器:从 bootstrap/app.php 文件中取得 Laravel 应用实例 $app (服务容器) 创建 HTTP / Console 内核:传入的请求会被发送给 HTTP 内核或者 console 内核进行处理

linux 内核升级2 转

linux内核升级 一.Linux内核概览 Linux是一个一体化内核(monolithic kernel)系统. 设备驱动程序可以完全访问硬件. Linux内的设备驱动程序可以方便地以模块化(modularize)的形式设置,并在系统运行期间可直接装载或卸载. 1. linux内核 linux操作系统是一个用来和硬件打交道并为用户程序提供一个有限服务集的低级支撑软件. 一个计算机系统是一个硬件和软件的共生体,它们互相依赖,不可分割. 计算机的硬件,含有外围设备.处理器.内存.硬盘和其他的电子设