资讯专栏INFORMATION COLUMN

Laravel核心解读--Console内核

Barry_Ng / 1272人阅读

摘要:其中设置请求是唯一区别于内核的一个引导程序。和命令行脚本的规范一样,如果执行命令任务程序成功会返回抛出异常退出则返回。严格遵循了面向对象程序设计的原则。

Console内核

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

内核绑定

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

singleton(
    IlluminateContractsHttpKernel::class,
    AppHttpKernel::class
);
// console内核绑定
$app->singleton(
    IlluminateContractsConsoleKernel::class,
    AppConsoleKernel::class
);

$app->singleton(
    IlluminateContractsDebugExceptionHandler::class,
    AppExceptionsHandler::class
);

return $app;

Console内核 AppConsoleKernel继承自IlluminateFoundationConsole, 在Console内核中我们可以注册artisan命令和定义应用里要执行的计划任务。

/**
* Define the application"s command schedule.
*
* @param  IlluminateConsoleSchedulingSchedule  $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(IlluminateContractsConsoleKernel::class);

$status = $kernel->handle(
    $input = new SymfonyComponentConsoleInputArgvInput,
    new SymfonyComponentConsoleOutputConsoleOutput
);
执行命令任务

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

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

引导应用

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

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

protected $bootstrappers = [
    IlluminateFoundationBootstrapLoadEnvironmentVariables::class,
    IlluminateFoundationBootstrapLoadConfiguration::class,
    IlluminateFoundationBootstrapHandleExceptions::class,
    IlluminateFoundationBootstrapRegisterFacades::class,
    IlluminateFoundationBootstrapSetRequestForConsole::class,
    IlluminateFoundationBootstrapRegisterProviders::class,
    IlluminateFoundationBootstrapBootProviders::class,
];

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

执行命令

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

name IlluminateFoundationConsole;
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 SymfonyComponentConsole;
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叨 ,最近正在筹备准备分享一些日常工作里学到和总结的技术知识,也会分享一些见闻和学习英语的方法。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/29725.html

相关文章

  • Laravel核心解读--HTTP内核

    摘要:终止程序终止中间件内核的方法会调用中间件的方法,调用完成后从请求进来到返回响应整个应用程序的生命周期就结束了。 Http Kernel Http Kernel是Laravel中用来串联框架的各个核心组件来网络请求的,简单的说只要是通过public/index.php来启动框架的都会用到Http Kernel,而另外的类似通过artisan命令、计划任务、队列启动框架进行处理的都会用到C...

    chenjiang3 评论0 收藏0
  • Laravel核心解读--完结篇

    摘要:过去一年时间写了多篇文章来探讨了我认为的框架最核心部分的设计思路代码实现。为了大家阅读方便,我把这些源码学习的文章汇总到这里。数据库算法和数据结构这些都是编程的内功,只有内功深厚了才能解决遇到的复杂问题。 过去一年时间写了20多篇文章来探讨了我认为的Larave框架最核心部分的设计思路、代码实现。通过更新文章自己在软件设计、文字表达方面都有所提高,在刚开始决定写Laravel源码分析地...

    laoLiueizo 评论0 收藏0
  • Laravel核心解读--Contracts契约

    摘要:的契约是一组定义框架提供的核心服务的接口,例如我们在介绍用户认证的章节中到的用户看守器契约和用户提供器契约以及框架自带的模型所实现的契约。接口与团队开发当你的团队在开发大型应用时,不同的部分有着不同的开发速度。 Contracts Laravel 的契约是一组定义框架提供的核心服务的接口, 例如我们在介绍用户认证的章节中到的用户看守器契约IllumninateContractsAuth...

    Prasanta 评论0 收藏0
  • PHPer面试指南-Laravel

    摘要:简述的生命周期采用了单一入口模式,应用的所有请求入口都是文件。分发请求一旦应用完成引导和所有服务提供者都注册完成,将会移交给路由进行分发。此外,由于对动态方法的独特用法,也使测试起来非常容易。 本书的 GitHub 地址:https://github.com/todayqq/PH... Laravel 作为现在最流行的 PHP 框架,其中的知识较多,所以单独拿出来写一篇。 简述 La...

    alaege 评论0 收藏0
  • Laravel核心解读 -- 用户认证系统(基础介绍)

    摘要:系统的核心是由的认证组件的看守器和提供器组成。使用的认证系统,几乎所有东西都已经为你配置好了。其配置文件位于,其中包含了用于调整认证服务行为的注释清晰的选项配置。 用户认证系统(基础介绍) 使用过Laravel的开发者都知道,Laravel自带了一个认证系统来提供基本的用户注册、登录、认证、找回密码,如果Auth系统里提供的基础功能不满足需求还可以很方便的在这些基础功能上进行扩展。这篇...

    RebeccaZhong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<