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源码学习里。

也欢迎关注我的公众号 网管叨bi叨 ,最近正在筹备准备分享一些日常工作里学到和总结的技术知识,也会分享一些见闻和学习英语的方法。

Laravel核心解读--Console内核相关推荐

  1. Laravel核心解读--HTTP内核

    Http Kernel Http Kernel是Laravel中用来串联框架的各个核心组件来网络请求的,简单的说只要是通过public/index.php来启动框架的都会用到Http Kernel,而 ...

  2. Laravel核心解读--服务容器(IocContainer)

    Laravel的核心是IocContainer, 文档中称其为"服务容器",服务容器是一个用于管理类依赖和执行依赖注入的强大工具,Laravel中的功能模块比如 Route.Elo ...

  3. Laravel核心解读--完结篇

    过去一年时间写了20多篇文章来探讨了我认为的Larave框架最核心部分的设计思路.代码实现.通过更新文章自己在软件设计.文字表达方面都有所提高,在刚开始决定写Laravel源码分析地文章的时候我地期望 ...

  4. Laravel核心解读--完结篇 1

    过去一年时间写了20多篇文章来探讨了我认为的Larave框架最核心部分的设计思路.代码实现.通过更新文章自己在软件设计.文字表达方面都有所提高,在刚开始决定写Laravel源码分析地文章的时候我地期望 ...

  5. Laravel核心解读--Contracts契约

    Contracts Laravel 的契约是一组定义框架提供的核心服务的接口, 例如我们在介绍用户认证的章节中到的用户看守器契约IllumninateContractsAuthGuard 和用户提供器 ...

  6. Laravel核心解读--控制器

    控制器 控制器能够将相关的请求处理逻辑组成一个单独的类, 通过前面的路由和中间件两个章节我们多次强调Laravel应用的请求在进入应用后首现会通过Http Kernel里定义的基本中间件 protec ...

  7. Laravel核心解读--ENV的加载和读取

    Laravel在启动时会加载项目中的.env文件.对于应用程序运行的环境来说,不同的环境有不同的配置通常是很有用的. 例如,你可能希望在本地使用测试的Mysql数据库而在上线后希望项目能够自动切换到生 ...

  8. Laravel核心解读 -- 用户认证系统(基础介绍)

    用户认证系统(基础介绍) 使用过Laravel的开发者都知道,Laravel自带了一个认证系统来提供基本的用户注册.登录.认证.找回密码,如果Auth系统里提供的基础功能不满足需求还可以很方便的在这些 ...

  9. Laravel核心解读--观察者模式

    观察者模式 Laravel的Event事件系统提供了一个简单的观察者模式实现,能够订阅和监听应用中发生的各种事件,在PHP的标准库(SPL)里甚至提供了三个接口SplSubject, SplObser ...

最新文章

  1. 食出100分:‘粥’的做法4---鱼片瘦肉粥
  2. php修改文件上传大小限制
  3. 剑指offer58-||.左旋转字符串
  4. 如何自己找出SMBDA服务使用的端口号
  5. java入门学习_Java入门学习进阶知识点
  6. c语言用栈编写数制转换程序,数制转换-栈的应用(C++实现)
  7. 每人都有两大炸弹的扎金花2012
  8. Python base64编码解码
  9. 转:Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)
  10. windows安装tensorflow GPU
  11. WinXP利用无线网卡做AP共享上网
  12. 快速排序QuickSort
  13. ffmpeg java 使用教程_Java使用ffmpeg
  14. Otsu算法——最大类间方差法(大津算法)
  15. swing-组件Collapse折叠面板2
  16. AIOT在数字化转型中的机遇和挑战
  17. 现代计算机图形学笔记(六)——布林-冯反射模型、图形管线、纹理映射
  18. matlab倒谱法基音周期,语音学习笔记1------matlab实现自相关函数法基音周期提取...
  19. 金融数据分析与挖掘具体实现方法
  20. Linux中最重要的命令

热门文章

  1. SQL Server Pivot 隐藏group
  2. 转载java中synchronized用法
  3. (转载)查看Oracle字符集及怎样修改字符集
  4. MongoDB索引,性能分析
  5. hive check in checkDiagnosticMessage found error
  6. nginx学习(一):基本安装
  7. Android控件——ListView之Adapter提供数据(其二)
  8. sink的简历(2011-6-20),寻工作一份
  9. 正确设置asp.net网站的404错误页面
  10. 佳能各系列数码相机特点简述