佳木斯湛栽影视文化发展公司

主頁(yè) > 知識(shí)庫(kù) > 淺談Laravel核心解讀之Console內(nèi)核

淺談Laravel核心解讀之Console內(nèi)核

熱門(mén)標(biāo)簽:銀行業(yè)務(wù) 電子圍欄 科大訊飛語(yǔ)音識(shí)別系統(tǒng) 團(tuán)購(gòu)網(wǎng)站 Mysql連接數(shù)設(shè)置 服務(wù)器配置 Linux服務(wù)器 阿里云

Console內(nèi)核

上一篇文章我們介紹了Laravel的HTTP內(nèi)核,詳細(xì)概述了網(wǎng)絡(luò)請(qǐng)求從進(jìn)入應(yīng)用到應(yīng)用處理完請(qǐng)求返回HTTP響應(yīng)整個(gè)生命周期中HTTP內(nèi)核是如何調(diào)動(dòng)Laravel各個(gè)核心組件來(lái)完成任務(wù)的。除了處理HTTP請(qǐng)求一個(gè)健壯的應(yīng)用經(jīng)常還會(huì)需要執(zhí)行計(jì)劃任務(wù)、異步隊(duì)列這些。Laravel為了能讓?xiě)?yīng)用滿(mǎn)足這些場(chǎng)景設(shè)計(jì)了artisan工具,通過(guò)artisan工具定義各種命令來(lái)滿(mǎn)足非HTTP請(qǐng)求的各種場(chǎng)景,artisan命令通過(guò)Laravel的Console內(nèi)核來(lái)完成對(duì)應(yīng)用核心組件的調(diào)度來(lái)完成任務(wù)。 今天我們就來(lái)學(xué)習(xí)一下Laravel Console內(nèi)核的核心代碼。

內(nèi)核綁定

跟HTTP內(nèi)核一樣,在應(yīng)用初始化階有一個(gè)內(nèi)核綁定的過(guò)程,將Console內(nèi)核注冊(cè)到應(yīng)用的服務(wù)容器里去,還是引用上一篇文章引用過(guò)的bootstrap/app.php里的代碼

?php
// 第一部分: 創(chuàng)建應(yīng)用實(shí)例
$app = new Illuminate\Foundation\Application(
  realpath(__DIR__.'/../')
);

// 第二部分: 完成內(nèi)核綁定
$app->singleton(
  Illuminate\Contracts\Http\Kernel::class,
  App\Http\Kernel::class
);
// console內(nèi)核綁定
$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內(nèi)核 \App\Console\Kernel繼承自Illuminate\Foundation\Console, 在Console內(nèi)核中我們可以注冊(cè)artisan命令和定義應(yīng)用里要執(zhí)行的計(jì)劃任務(wù)。

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

在實(shí)例化Console內(nèi)核的時(shí)候,內(nèi)核會(huì)定義應(yīng)用的命令計(jì)劃任務(wù)(shedule方法中定義的計(jì)劃任務(wù))

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

應(yīng)用解析Console內(nèi)核

查看aritisan文件的源碼我們可以看到, 完成Console內(nèi)核綁定的綁定后,接下來(lái)就會(huì)通過(guò)服務(wù)容器解析出console內(nèi)核對(duì)象

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

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

執(zhí)行命令任務(wù)

解析出Console內(nèi)核對(duì)象后,接下來(lái)就要處理來(lái)自命令行的命令請(qǐng)求了, 我們都知道PHP是通過(guò)全局變量$_SERVER['argv']來(lái)接收所有的命令行輸入的, 和命令行里執(zhí)行shell腳本一樣(在shell腳本里可以通過(guò)$0獲取腳本文件名,$1 $2這些依次獲取后面?zhèn)鬟f給shell腳本的參數(shù)選項(xiàng))索引0對(duì)應(yīng)的是腳本文件名,接下來(lái)依次是命令行里傳遞給腳本的所有參數(shù)選項(xiàng),所以在命令行里通過(guò)artisan腳本執(zhí)行的命令,在artisan腳本中$_SERVER['argv']數(shù)組里索引0對(duì)應(yīng)的永遠(yuǎn)是artisan這個(gè)字符串,命令行里后面的參數(shù)會(huì)依次對(duì)應(yīng)到$_SERVER['argv']數(shù)組后續(xù)的元素里。

因?yàn)閍rtisan命令的語(yǔ)法中可以指定命令參數(shù)選項(xiàng)、有的選項(xiàng)還可以指定實(shí)參,為了減少命令行輸入?yún)?shù)解析的復(fù)雜度,Laravel使用了Symfony\Component\Console\Input對(duì)象來(lái)解析命令行里這些參數(shù)選項(xiàng)(shell腳本里其實(shí)也是一樣,會(huì)通過(guò)shell函數(shù)getopts來(lái)解析各種格式的命令行參數(shù)輸入),同樣地Laravel使用了Symfony\Component\Console\Output對(duì)象來(lái)抽象化命令行的標(biāo)準(zhǔn)輸出。

引導(dǎo)應(yīng)用

在Console內(nèi)核的handle方法里我們可以看到和HTTP內(nèi)核處理請(qǐng)求前使用bootstrapper程序引用應(yīng)用一樣在開(kāi)始處理命令任務(wù)之前也會(huì)有引導(dǎo)應(yīng)用這一步操作

其父類(lèi) 「IlluminateFoundationConsoleKernel」 內(nèi)部定義了屬性名為 「bootstrappers」 的 引導(dǎo)程序 數(shù)組:

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

數(shù)組中包括的引導(dǎo)程序基本上和HTTP內(nèi)核中定義的引導(dǎo)程序一樣, 都是應(yīng)用在初始化階段要進(jìn)行的環(huán)境變量、配置文件加載、注冊(cè)異常處理器、設(shè)置Console請(qǐng)求、注冊(cè)應(yīng)用中的服務(wù)容器、Facade和啟動(dòng)服務(wù)。其中設(shè)置Console請(qǐng)求是唯一區(qū)別于HTTP內(nèi)核的一個(gè)引導(dǎo)程序。

執(zhí)行命令

執(zhí)行命令是通過(guò)Console Application來(lái)執(zhí)行的,它繼承自Symfony框架的Symfony\Component\Console\Application類(lèi), 通過(guò)對(duì)應(yīng)的run方法來(lái)執(zhí)行命令。

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
{
  //執(zhí)行命令
  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)
  {
    //解析出命令名稱(chēng)
    $name = $this->getCommandName($input);
    
    //解析出入?yún)?
    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 {
      //通過(guò)命令名稱(chēng)查找出命令類(lèi)(命名空間、類(lèi)名等)
      $command = $this->find($name);
    }
    ......
    //運(yùn)行命令類(lèi)
    $exitCode = $this->doRunCommand($command, $input, $output);
    
    return $exitCode;
  }
  
  protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
  {
    ......
    //執(zhí)行命令類(lèi)的run方法來(lái)處理任務(wù)
    $exitCode = $command->run($input, $output);
    ......
    
    return $exitcode;
  }
}

執(zhí)行命令時(shí)主要有三步操作:

  • 通過(guò)命令行輸入解析出命令名稱(chēng)和參數(shù)選項(xiàng)。
  • 通過(guò)命令名稱(chēng)查找命令類(lèi)的命名空間和類(lèi)名。
  • 執(zhí)行命令類(lèi)的run方法來(lái)完成任務(wù)處理并返回狀態(tài)碼。

和命令行腳本的規(guī)范一樣,如果執(zhí)行命令任務(wù)程序成功會(huì)返回0, 拋出異常退出則返回1。

還有就是打開(kāi)命令類(lèi)后我們可以看到并沒(méi)有run方法,我們把處理邏輯都寫(xiě)在了handle方法中,仔細(xì)查看代碼會(huì)發(fā)現(xiàn)run方法定義在父類(lèi)中,在run方法會(huì)中會(huì)調(diào)用子類(lèi)中定義的handle方法來(lái)完成任務(wù)處理。 嚴(yán)格遵循了面向?qū)ο蟪绦蛟O(shè)計(jì)的SOLID 原則。

結(jié)束應(yīng)用

執(zhí)行完命令程序返回狀態(tài)碼后, 在artisan中會(huì)直接通過(guò)exit($status)函數(shù)輸出狀態(tài)碼并結(jié)束PHP進(jìn)程,接下來(lái)shell進(jìn)程會(huì)根據(jù)返回的狀態(tài)碼是否為0來(lái)判斷腳本命令是否執(zhí)行成功。

到這里通過(guò)命令行開(kāi)啟的程序進(jìn)程到這里就結(jié)束了,跟HTTP內(nèi)核一樣Console內(nèi)核在整個(gè)生命周期中也是負(fù)責(zé)調(diào)度,只不過(guò)Http內(nèi)核最終將請(qǐng)求落地到了Controller程序中而Console內(nèi)核則是將命令行請(qǐng)求落地到了Laravel中定義的各種命令類(lèi)程序中,然后在命令類(lèi)里面我們就可以寫(xiě)其他程序一樣自由地使用Laravel中的各個(gè)組件和注冊(cè)到服務(wù)容器里的服務(wù)了。

本文已經(jīng)收錄在系列文章Laravel源碼學(xué)習(xí)里,歡迎訪(fǎng)問(wèn)閱讀。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

標(biāo)簽:衢州 蚌埠 江蘇 棗莊 廣元 萍鄉(xiāng) 衡水 大理

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《淺談Laravel核心解讀之Console內(nèi)核》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話(huà)咨詢(xún)

    • 400-1100-266
    云林县| 安达市| 罗田县| 霍城县| 澜沧| 江安县| 南江县| 蕲春县| 徐水县| 岐山县| 平阴县| 祥云县| 天台县| 达州市| 呼玛县| 昌吉市| 余庆县| 密云县| 保德县| 七台河市| 宁阳县| 安顺市| 仁寿县| 龙川县| 钦州市| 广水市| 鄢陵县| 土默特右旗| 许昌市| 乌兰察布市| 伊春市| 青海省| 永州市| 本溪市| 凤山县| 武乡县| 威远县| 尉犁县| 梅河口市| 栾川县| 中山市|