iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 16
1

本篇文章因為有相當大的篇幅在追蹤原始碼,並且把對應的篇幅以連結的方式提供。
請善用「Ctrl+點擊」的方式在新的分頁打開連結

這東西不就是 cronjob 嗎?

先說結論,ㄅ是。

怎麼說呢?

先從官方教學給出的 cronjob 指令說起:

* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1

這指令的意思除了讓 linux 每分鐘執行一次 php artisan schedule:run 之外,
還有把所有訊息(包含錯誤訊息)都丟進黑洞

看起來,單單這行指令是沒辦法讓我們在 Commands/Kernel 中設定的排程準時運作的,
要知道具體如何運作的,必須找出 schedule:run 的原始碼才行。

ScheduleRunCommand

這支檔案就是 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() 裡面是在必瞎咪莽!

開始追程式碼囉

ScheduleRunCommand::dueEvents()

source code

這個函式會先透過一個 array_filter 對所有排程($this->events)做篩選,
而篩選的條件是 $event->isDue($app);TRUE

Event::isDue()

source code

翻譯一下:

  1. 當網站在線上、且已經關閉進入維護狀態則這 Event 不會觸發
  2. 執行 expressionPasses() 檢查該 Event 設定的時間是不是到了

Event::expressionPasses()

source code

這邊會用系統目前的時間到 CronExpression::isDue() 做比較,
但具體的邏輯還是要到 CronExpression 裡面看看。

CronExpression::isDue()

程式碼點這裡

窩....窩放棄....
大家可以跟我一樣只看函式的註解就好QQ

所以?

但總而言之,我們已經知道了不少關於 dueEvents() 的真相:

  1. dueEvents() 會回傳時間到了、應該要執行的排程
  2. dueEvents() 是透過解析 $event->cron()cron expression,並用它來與系統時間比較,才知道是否該執行這個排程了

雖然知道排程檢查做在哪了,但還有兩個問題:

  1. 我們知道在哪裡檢查了,但是是在哪裡設定這個 cron expression 的呢?
  2. filterPass() 到底是在幹嘛的ㄋ?

Event::filtersPass()

source code

裡面迭代了兩個陣列(而且它們裡面都是 callback),看起來都絕非善類
如果回頭追蹤這個 class 有哪邊會用到 $this->filters$this->rejects 的話...

Event::when()、Event::skip()

會追到它們兩位
source code

大家就註解看一看吧~
有需要作額外的限制才需要用到這兩個~

深入 Scheduling\Schedule

呼叫鍊

這個部份要從那串 $schedule->command()->cron() 說起...

Schedule::command($command, $params)

source code
在這個函式裡面做了一些看的我豆頁很痛得東西...,
抱歉這部份我真的沒打算琢磨太深XD

好在傳入的兩個參數也一直到最後一行的 return $this->exec() 才有使用到,
我們就直接去看 exec() 在必瞎咪莽吧!

Schedule::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();

Event::cron()

source code @ Laravel 5.2

抓到了!cron expression 是在這邊儲存的!
收工~


上一篇
Laravel:深入Log
下一篇
Laravel:Inspiring
系列文
花式PHP31

尚未有邦友留言

立即登入留言