PukiWikiプラグイン開発から学ぶPHP #3

# 前回
https://qiita.com/PitaQ/items/66869cd0799a52115c63

# 本題
今回は現在時刻をJSで更新して表示するプラグインをつくります。

ファイル名は「datetime.inc.php」とします。

## 設計
`&datetime([表示形式],[UNIX時間]);`

- &datetime(`y-m-d h:i:s`); という形で呼び出すと「2024-12-17 16:27:32」という風に表示
- フォーマットの指定がされていないときはデフォルトで「`y-m-d h:i:s`」
- UNIX時間が表示されていないときはデフォルトで現在時刻に (ぶっちゃけ必要ではないかも)
- UNIX時間に -1 を指定することで現在時刻となる (表示形式のみ指定したらいい話なので勝手に内部で-1になる)

## 開発

```datetime.inc.php
<?php
function plugin_datetime_inline() {
    // 引数を取得
    $args = func_get_args();

    // フォーマットとUNIX時間を取得、三項演算子で存在しない場合はデフォルトにしておく
    // (-1 は現在時刻を使用するためのフラグ的なもの)
    $format = !empty($args[0]) ? htmlsc($args[0]) : 'y-m-d h:i:s';
    $unixtime = !empty($args[1]) ? htmlsc($args[1]) : "-1";

    // UNIX時間が指定されていればその時間を使用
    // date() でフォーマットを適用して表示
    $date = get_date($format, $unixtime == "-1" ? time() : $unixtime);
    
    return <<<EOD
        <span class="plugin_datetime">
            <span>{$date}</span>
            <input type="hidden" class="plugin_datetime_property" value="{&quot;format&quot;:&quot;{$format}&quot;,&quot;unixtime&quot;:{$unixtime}}" />
        </span>
EOD;
}

function plugin_datetime_init() {
    global $head_tags;
    
    $head_tags[] = <<<EOD
    <script>
    function plugin_datetime_update() {
        document.querySelectorAll(".plugin_datetime").forEach((tag) => {
            let prop = JSON.parse(tag.querySelector(".plugin_datetime_property").value);
        
            let format = prop.format;
            let unixtime = prop.unixtime;

            if (unixtime != "-1") return;
        
            let date = new Date();

            let y = date.getFullYear();
            let m = ('0' + (date.getMonth() + 1)).slice(-2);
            let d = ('0' + date.getDate()).slice(-2);
            let h = ('0' + date.getHours()).slice(-2);
            let i = ('0' + date.getMinutes()).slice(-2);
            let s = ('0' + date.getSeconds()).slice(-2);
            
            // フォーマットを適用する
            let formatted = format.replace(/y/g, y).replace(/m/g, m).replace(/d/g, d).replace(/h/g, h).replace(/i/g, i).replace(/s/g, s);

            // 時刻の書き換え
            tag.querySelector("span").innerHTML = formatted;
        });
    }
    
    setInterval(plugin_datetime_update, 1000); // 1秒ごとに更新 1s = 1000ms
    </script>
EOD;
}
```

### plugin_datetime_inline() 関数
&datetime([表示形式],[UNIX時間]);で呼び出される

- 1番目の引数を表示形式、2番目の引数をUNIX時間として取得
  - PukiWikiプラグインでは一般的に可変長引数を扱うため、func_get_args() で引数を取得する
- 表示形式の引数がない場合は 'y-m-d h:i:s' を、UNIX時間の引数がない場合は現在時刻を使う

- get_date() はPukiWiki内部のfunc.phpで定義されている関数で、PHP標準関数と違ってPukiWikiの設定に沿って日付を取得できる
- htmlsc() もPukiWiki内部の関数で、HTMLエスケープを行う、これしないとXSSされる可能性がある
- 返り値はHTML、spanで囲んで表示形式に従って日付を表示するという仕組み
  - spanの中に、UNIX時間と表示形式をjson形式でinput[type=hidden]で持たせる
    - JavaScriptで1秒ごとに更新するために使う

### plugin_datetime_init() 関数
プラグインが呼び出された(plugin_datetime_inline() が呼び出された)ときに呼び出される
ブラウザ上で継続的に時刻を更新するためにJavaScriptをheadタグに追加する

- グローバル変数 $head_tags にJavaScriptを追加
  - JSで1秒ごとに plugin_datetime_update() を呼び出して、ページ内の datetime クラスを持つタグの時刻を更新する
  - JSON.parse() で input[type=hidden] のvalueに入ってるjsonを読み込める
  - setInterval() で1秒ごとに plugin_datetime_update() を呼び出す

# 最後に
PukiWiki側で定義されている $html_tags というグローバル変数を使うことで、headタグにタグを追加することができます。
使いこなせば、JavaScriptを使った機能を追加することも可能なので便利です。