摘要:其中設(shè)置請求是唯一區(qū)別于內(nèi)核的一個引導(dǎo)程序。和命令行腳本的規(guī)范一樣,如果執(zhí)行命令任務(wù)程序成功會返回拋出異常退出則返回。嚴(yán)格遵循了面向?qū)ο蟪绦蛟O(shè)計的原則。
Console內(nèi)核
上一篇文章我們介紹了Laravel的HTTP內(nèi)核,詳細(xì)概述了網(wǎng)絡(luò)請求從進(jìn)入應(yīng)用到應(yīng)用處理完請求返回HTTP響應(yīng)整個生命周期中HTTP內(nèi)核是如何調(diào)動Laravel各個核心組件來完成任務(wù)的。除了處理HTTP請求一個健壯的應(yīng)用經(jīng)常還會需要執(zhí)行計劃任務(wù)、異步隊列這些。Laravel為了能讓應(yīng)用滿足這些場景設(shè)計了artisan工具,通過artisan工具定義各種命令來滿足非HTTP請求的各種場景,artisan命令通過Laravel的Console內(nèi)核來完成對應(yīng)用核心組件的調(diào)度來完成任務(wù)。 今天我們就來學(xué)習(xí)一下Laravel Console內(nèi)核的核心代碼。
內(nèi)核綁定跟HTTP內(nèi)核一樣,在應(yīng)用初始化階有一個內(nèi)核綁定的過程,將Console內(nèi)核注冊到應(yīng)用的服務(wù)容器里去,還是引用上一篇文章引用過的bootstrap/app.php里的代碼
singleton( IlluminateContractsHttpKernel::class, AppHttpKernel::class ); // console內(nèi)核綁定 $app->singleton( IlluminateContractsConsoleKernel::class, AppConsoleKernel::class ); $app->singleton( IlluminateContractsDebugExceptionHandler::class, AppExceptionsHandler::class ); return $app;
Console內(nèi)核 AppConsoleKernel繼承自IlluminateFoundationConsole, 在Console內(nèi)核中我們可以注冊artisan命令和定義應(yīng)用里要執(zhí)行的計劃任務(wù)。
/** * 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內(nèi)核的時候,內(nèi)核會定義應(yīng)用的命令計劃任務(wù)(shedule方法中定義的計劃任務(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)核綁定的綁定后,接下來就會通過服務(wù)容器解析出console內(nèi)核對象
$kernel = $app->make(IlluminateContractsConsoleKernel::class); $status = $kernel->handle( $input = new SymfonyComponentConsoleInputArgvInput, new SymfonyComponentConsoleOutputConsoleOutput );執(zhí)行命令任務(wù)
解析出Console內(nèi)核對象后,接下來就要處理來自命令行的命令請求了, 我們都知道PHP是通過全局變量$_SERVER["argv"]來接收所有的命令行輸入的, 和命令行里執(zhí)行shell腳本一樣(在shell腳本里可以通過$0獲取腳本文件名,$1 $2這些依次獲取后面?zhèn)鬟f給shell腳本的參數(shù)選項)索引0對應(yīng)的是腳本文件名,接下來依次是命令行里傳遞給腳本的所有參數(shù)選項,所以在命令行里通過artisan腳本執(zhí)行的命令,在artisan腳本中$_SERVER["argv"]數(shù)組里索引0對應(yīng)的永遠(yuǎn)是artisan這個字符串,命令行里后面的參數(shù)會依次對應(yīng)到$_SERVER["argv"]數(shù)組后續(xù)的元素里。
因為artisan命令的語法中可以指定命令參數(shù)選項、有的選項還可以指定實參,為了減少命令行輸入?yún)?shù)解析的復(fù)雜度,Laravel使用了SymfonyComponentConsoleInput對象來解析命令行里這些參數(shù)選項(shell腳本里其實也是一樣,會通過shell函數(shù)getopts來解析各種格式的命令行參數(shù)輸入),同樣地Laravel使用了SymfonyComponentConsoleOutput對象來抽象化命令行的標(biāo)準(zhǔn)輸出。
引導(dǎo)應(yīng)用在Console內(nèi)核的handle方法里我們可以看到和HTTP內(nèi)核處理請求前使用bootstrapper程序引用應(yīng)用一樣在開始處理命令任務(wù)之前也會有引導(dǎo)應(yīng)用這一步操作
其父類 「IlluminateFoundationConsoleKernel」 內(nèi)部定義了屬性名為 「bootstrappers」 的 引導(dǎo)程序 數(shù)組:
protected $bootstrappers = [ IlluminateFoundationBootstrapLoadEnvironmentVariables::class, IlluminateFoundationBootstrapLoadConfiguration::class, IlluminateFoundationBootstrapHandleExceptions::class, IlluminateFoundationBootstrapRegisterFacades::class, IlluminateFoundationBootstrapSetRequestForConsole::class, IlluminateFoundationBootstrapRegisterProviders::class, IlluminateFoundationBootstrapBootProviders::class, ];
數(shù)組中包括的引導(dǎo)程序基本上和HTTP內(nèi)核中定義的引導(dǎo)程序一樣, 都是應(yīng)用在初始化階段要進(jìn)行的環(huán)境變量、配置文件加載、注冊異常處理器、設(shè)置Console請求、注冊應(yīng)用中的服務(wù)容器、Facade和啟動服務(wù)。其中設(shè)置Console請求是唯一區(qū)別于HTTP內(nèi)核的一個引導(dǎo)程序。
執(zhí)行命令執(zhí)行命令是通過Console Application來執(zhí)行的,它繼承自Symfony框架的SymfonyComponentConsoleApplication類, 通過對應(yīng)的run方法來執(zhí)行命令。
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 { //執(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) { //解析出命令名稱 $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 { //通過命令名稱查找出命令類(命名空間、類名等) $command = $this->find($name); } ...... //運行命令類 $exitCode = $this->doRunCommand($command, $input, $output); return $exitCode; } protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) { ...... //執(zhí)行命令類的run方法來處理任務(wù) $exitCode = $command->run($input, $output); ...... return $exitcode; } }
執(zhí)行命令時主要有三步操作:
通過命令行輸入解析出命令名稱和參數(shù)選項。
通過命令名稱查找命令類的命名空間和類名。
執(zhí)行命令類的run方法來完成任務(wù)處理并返回狀態(tài)碼。
和命令行腳本的規(guī)范一樣,如果執(zhí)行命令任務(wù)程序成功會返回0, 拋出異常退出則返回1。
還有就是打開命令類后我們可以看到并沒有run方法,我們把處理邏輯都寫在了handle方法中,仔細(xì)查看代碼會發(fā)現(xiàn)run方法定義在父類中,在run方法會中會調(diào)用子類中定義的handle方法來完成任務(wù)處理。 嚴(yán)格遵循了面向?qū)ο蟪绦蛟O(shè)計的SOLID 原則。
結(jié)束應(yīng)用執(zhí)行完命令程序返回狀態(tài)碼后, 在artisan中會直接通過exit($status)函數(shù)輸出狀態(tài)碼并結(jié)束PHP進(jìn)程,接下來shell進(jìn)程會根據(jù)返回的狀態(tài)碼是否為0來判斷腳本命令是否執(zhí)行成功。
到這里通過命令行開啟的程序進(jìn)程到這里就結(jié)束了,跟HTTP內(nèi)核一樣Console內(nèi)核在整個生命周期中也是負(fù)責(zé)調(diào)度,只不過Http內(nèi)核最終將請求落地到了Controller程序中而Console內(nèi)核則是將命令行請求落地到了Laravel中定義的各種命令類程序中,然后在命令類里面我們就可以寫其他程序一樣自由地使用Laravel中的各個組件和注冊到服務(wù)容器里的服務(wù)了。
本文已經(jīng)收錄在系列文章Laravel源碼學(xué)習(xí)里。
也歡迎關(guān)注我的公眾號 網(wǎng)管叨bi叨 ,最近正在籌備準(zhǔn)備分享一些日常工作里學(xué)到和總結(jié)的技術(shù)知識,也會分享一些見聞和學(xué)習(xí)英語的方法。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/29725.html
摘要:終止程序終止中間件內(nèi)核的方法會調(diào)用中間件的方法,調(diào)用完成后從請求進(jìn)來到返回響應(yīng)整個應(yīng)用程序的生命周期就結(jié)束了。 Http Kernel Http Kernel是Laravel中用來串聯(lián)框架的各個核心組件來網(wǎng)絡(luò)請求的,簡單的說只要是通過public/index.php來啟動框架的都會用到Http Kernel,而另外的類似通過artisan命令、計劃任務(wù)、隊列啟動框架進(jìn)行處理的都會用到C...
摘要:過去一年時間寫了多篇文章來探討了我認(rèn)為的框架最核心部分的設(shè)計思路代碼實現(xiàn)。為了大家閱讀方便,我把這些源碼學(xué)習(xí)的文章匯總到這里。數(shù)據(jù)庫算法和數(shù)據(jù)結(jié)構(gòu)這些都是編程的內(nèi)功,只有內(nèi)功深厚了才能解決遇到的復(fù)雜問題。 過去一年時間寫了20多篇文章來探討了我認(rèn)為的Larave框架最核心部分的設(shè)計思路、代碼實現(xiàn)。通過更新文章自己在軟件設(shè)計、文字表達(dá)方面都有所提高,在剛開始決定寫Laravel源碼分析地...
摘要:的契約是一組定義框架提供的核心服務(wù)的接口,例如我們在介紹用戶認(rèn)證的章節(jié)中到的用戶看守器契約和用戶提供器契約以及框架自帶的模型所實現(xiàn)的契約。接口與團隊開發(fā)當(dāng)你的團隊在開發(fā)大型應(yīng)用時,不同的部分有著不同的開發(fā)速度。 Contracts Laravel 的契約是一組定義框架提供的核心服務(wù)的接口, 例如我們在介紹用戶認(rèn)證的章節(jié)中到的用戶看守器契約IllumninateContractsAuth...
摘要:簡述的生命周期采用了單一入口模式,應(yīng)用的所有請求入口都是文件。分發(fā)請求一旦應(yīng)用完成引導(dǎo)和所有服務(wù)提供者都注冊完成,將會移交給路由進(jìn)行分發(fā)。此外,由于對動態(tài)方法的獨特用法,也使測試起來非常容易。 本書的 GitHub 地址:https://github.com/todayqq/PH... Laravel 作為現(xiàn)在最流行的 PHP 框架,其中的知識較多,所以單獨拿出來寫一篇。 簡述 La...
摘要:系統(tǒng)的核心是由的認(rèn)證組件的看守器和提供器組成。使用的認(rèn)證系統(tǒng),幾乎所有東西都已經(jīng)為你配置好了。其配置文件位于,其中包含了用于調(diào)整認(rèn)證服務(wù)行為的注釋清晰的選項配置。 用戶認(rèn)證系統(tǒng)(基礎(chǔ)介紹) 使用過Laravel的開發(fā)者都知道,Laravel自帶了一個認(rèn)證系統(tǒng)來提供基本的用戶注冊、登錄、認(rèn)證、找回密碼,如果Auth系統(tǒng)里提供的基礎(chǔ)功能不滿足需求還可以很方便的在這些基礎(chǔ)功能上進(jìn)行擴展。這篇...
閱讀 1309·2021-11-15 11:37
閱讀 2564·2021-09-22 10:56
閱讀 3391·2021-09-06 15:11
閱讀 801·2021-08-31 09:45
閱讀 2897·2021-07-28 11:16
閱讀 1806·2019-08-30 15:44
閱讀 477·2019-08-30 13:22
閱讀 3344·2019-08-30 13:18