0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cronの拡張コードのリファクタリング

0
Posted at

CronExpansion

CronExpansion は JSON で定義したスケジュールを読み込み、
一致した時間にコマンドを実行する 軽量な PHP cron 実行エンジンです。

Linux の cron のようなスケジュール指定を PHPのみで処理できます。

この記事は前回作成したCronの拡張コードのリファクタリングになります.
https://qiita.com/taoka-toshiaki/items/fd12dd1c83132f54dc47


Requirements

  • PHP 7.4+
  • CLI実行環境

Installation

プロジェクトに以下のファイルを配置します。

project/
 ├ CronExpansion.php
 └ crontab.json

Usage

CLIから実行します。

php CronExpansion.php

またはLinux cronに登録します。

* * * * * php /path/to/CronExpansion.php

crontab.json

スケジュールは JSON で定義します。

[
  {
    "m": "*",
    "d": "*",
    "H": "*",
    "i": "*/5",
    "w": [1,1,1,1,1,1,1],
    "command": "php job.php"
  }
]

Schedule Fields

Field Description Range
m month 1–12
d day 1–31
H hour 0–23
i minute 0–59
w weekday flag array 0–6
command command to execute shell

Weekday (w)

曜日は 配列形式のフラグで指定します。

インデックスは DateTime::format('w') に対応します。

Index Day
0 Sunday
1 Monday
2 Tuesday
3 Wednesday
4 Thursday
5 Friday
6 Saturday

Examples

Every day

"w":[1,1,1,1,1,1,1]

Weekdays

"w":[0,1,1,1,1,1,0]

Weekend

"w":[1,0,0,0,0,0,1]

Supported Cron Syntax

以下の cron 式をサポートします。


Any

*

すべての値に一致


Step

*/5

*/10

Range

1-5

Range with Step

1-10/2

Single Value

5

Multiple Values

1,5,10

Mixed Expressions

1,5-10,*/15

Example Schedules

Every 5 minutes

{
  "m":"*",
  "d":"*",
  "H":"*",
  "i":"*/5",
  "w":[1,1,1,1,1,1,1],
  "command":"php job.php"
}

Every weekday at 03:30

{
  "m":"*",
  "d":"*",
  "H":"3",
  "i":"30",
  "w":[0,1,1,1,1,1,0],
  "command":"php report.php"
}

Every Sunday at 02:00

{
  "m":"*",
  "d":"*",
  "H":"2",
  "i":"0",
  "w":[1,0,0,0,0,0,0],
  "command":"php weekly.php"
}

Execution Behavior

コマンドは以下の形式で バックグラウンド実行されます。

command > /dev/null 2>&1 &

意味

処理 内容
stdout 破棄
stderr 破棄
& 非同期実行

How It Works

crontab.json
     ↓
JSON読み込み
     ↓
現在日時取得
     ↓
cron式と比較
     ↓
一致した場合 command 実行

Class Structure

CronExpansion
 ├ __construct()
 ├ run()
 └ cronMatch()

run()

現在時刻とスケジュールを比較し、
一致した場合コマンドを実行します。


cronMatch()

cron式を解析し一致判定を行います。

対応

  • *
  • */5
  • 1-5
  • 1-5/2
  • 1,5,10

License

MIT License


CronExpansion.php
<?php
date_default_timezone_set('Asia/Tokyo');

class CronExpansion
{
    private string $filepath = 'crontab.json';
    private object $cronTabs;

    public function __construct()
    {
        $fileData = file_get_contents($this->filepath);
        $cronTabData = json_decode($fileData);
        $this->cronTabs = !json_last_error() ? (object)$cronTabData : (object)[];
    }

    public function run(): object
    {
        $datetime = new DateTime();
        $dateData = explode(',', $datetime->format('m,d,H,i,w'));

        foreach ($this->cronTabs as $cronTab) {

            $command = null;
            $flg = true;
            $i = 0;

            $cronTabData = get_object_vars($cronTab);

            foreach ($cronTabData as $key => $val) {

                if ($key === 'command') {
                    $command = $val;
                    continue;
                }

                if ($key === 'w') {
                    if (!(int)$val[(int)$dateData[$i]]) {
                        $flg = false;
                        break;
                    }
                } else {
                    if (!$this->cronMatch((int)$dateData[$i], $val)) {
                        $flg = false;
                        break;
                    }
                }

                $i++;
            }

            if ($flg && $command) {
                exec($command . " > /dev/null 2>&1 &");
            }
        }

        return $this;
    }

    private function cronMatch(int $now, string $expr): bool
    {
        $parts = explode(',', $expr);

        foreach ($parts as $part) {

            $part = trim($part);

            // *
            if ($part === '*') {
                return true;
            }

            // */5
            if (preg_match('/^\*\/(\d+)$/', $part, $m)) {
                if ($now % (int)$m[1] === 0) {
                    return true;
                }
            }

            // 1-5
            if (preg_match('/^(\d+)-(\d+)$/', $part, $m)) {
                if ($now >= (int)$m[1] && $now <= (int)$m[2]) {
                    return true;
                }
            }

            // 1-5/2
            if (preg_match('/^(\d+)-(\d+)\/(\d+)$/', $part, $m)) {
                for ($i = $m[1]; $i <= $m[2]; $i += $m[3]) {
                    if ($now === (int)$i) {
                        return true;
                    }
                }
            }

            // single number
            if ((int)$part === $now) {
                return true;
            }
        }

        return false;
    }
}

(new CronExpansion)->run();

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?