本篇文章因為有相當大的篇幅在追蹤原始碼,並且把對應的篇幅以連結的方式提供。
請善用「Ctrl+點擊」的方式在新的分頁打開連結
先說結論,ㄅ是。
先從官方教學給出的 cronjob 指令說起:
* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1
這指令的意思除了讓 linux 每分鐘執行一次 php artisan schedule:run
之外,
還有把所有訊息(包含錯誤訊息)都丟進黑洞。
看起來,單單這行指令是沒辦法讓我們在 Commands/Kernel
中設定的排程準時運作的,
要知道具體如何運作的,必須找出 schedule:run 的原始碼才行。
這支檔案就是 schedule:run 的原始碼,
在它其中的 fire() 就是執行這個指令的時候會做的動作,讓我們來猜猜它在幹嘛:
<?php
class ScheduleRunCommand extends Command
{
public function fire()
{
// 抓出一些排程
$events = $this->schedule->dueEvents($this->laravel);
// 應該是用來紀錄已經執行完的排程的
$eventsRan = 0;
// 迭代剛剛抓出來的排程
foreach ($events as $event) {
// 看看這排程有沒有通過檢查
if (! $event->filtersPass($this->laravel)) {
// 沒有通過就跳過
continue;
}
// 印一行字到終端機上說「欸我正在幫你跑一個排程喔」
$this->line('<info>Running scheduled command:</info> '.$event->getSummaryForDisplay());
// 真的給它跑下去了
$event->run($this->laravel);
// 剛剛的標記要加 1
++$eventsRan;
}
// 如果沒抓到排程、或有抓到一些但沒執行半個,就印這行到終端機上
if (count($events) === 0 || $eventsRan === 0) {
$this->info('No scheduled commands are ready to run.');
}
}
}
根據上面的盲狙,大概可以知道具體在做排程的就是這段程式碼了。
但具體的細節還要看 dueEvents()
跟 filterPass()
裡面是在必瞎咪莽!
這個函式會先透過一個 array_filter 對所有排程($this->events)做篩選,
而篩選的條件是 $event->isDue($app);
為 TRUE
。
翻譯一下:
這邊會用系統目前的時間到 CronExpression::isDue() 做比較,
但具體的邏輯還是要到 CronExpression 裡面看看。
窩....窩放棄....
大家可以跟我一樣只看函式的註解就好QQ
但總而言之,我們已經知道了不少關於 dueEvents() 的真相:
$event->cron()
的 cron expression,並用它來與系統時間比較,才知道是否該執行這個排程了雖然知道排程檢查做在哪了,但還有兩個問題:
裡面迭代了兩個陣列(而且它們裡面都是 callback),看起來都絕非善類
如果回頭追蹤這個 class 有哪邊會用到 $this->filters
跟 $this->rejects
的話...
會追到它們兩位
source code
大家就註解看一看吧~
有需要作額外的限制才需要用到這兩個~
這個部份要從那串 $schedule->command()->cron()
說起...
source code
在這個函式裡面做了一些看的我豆頁很痛得東西...,
抱歉這部份我真的沒打算琢磨太深XD
好在傳入的兩個參數也一直到最後一行的 return $this->exec() 才有使用到,
我們就直接去看 exec() 在必瞎咪莽吧!
<?php
// 這邊看起來是把 $parameters 編譯成 String,並串接在 $command 之後
if (count($parameters)) {
$command .= ' '.$this->compileParameters($parameters);
}
// 把串接過的 $command 拿去 new Event
// 並把 new 出來的 $event 塞進 $this->events 陣列
// 並把 new 出來的 $event 再傳回去
$this->events[] = $event = new Event($command);
return $event;
所以最初的呼叫鍊:
<?php
$schedule->command()->cron();
可以解讀成
<?php
$event = $schedule->command();
$event->cron();
抓到了!cron expression 是在這邊儲存的!
收工~