WordPressでスケジュール実行してみようと思います。
オーソドックスに、1時間おきにメールする感じでやってみます。
wp-cronを使ってみる
WordPressでスケジュール管理している wp-cron という機能を使って、スケジュール実行してみます。
サーバーには cron という機能でスケジュール実行が出来るのですが、それに「似ている」機能で、WordPress上でスケジュール実行を行うのが wp-cron です。
WordPressへのアクセス(閲覧のアクセス、管理画面へのアクセスなど)があったときに、「次回の実行予定時刻」を過ぎていたら実行してくれます。
アクセスが無いと実行されないので、公開していないWordPressとかだと全然実行されない場合があります。
ちゃんと登録されているか、実行されているかを確認しやすいプラグイン「WP Crontrol」を入れてみました。
全貌が見えないと分かりづらい場合もあるので、何はともあれ今回作ってみたソースです。
「wp-content/plugins」配下に、「cron-test」とディレクトリを作成して、「cron-test.php」とファイルを作成する前提で話を進めます。
(分かる方は、ディレクトリ名やファイル名など任意のものでどうぞ。)
<?php /* Plugin Name: cron-test Description: Wp-Cronのテストです Version: 0.0.1 Author: poporon Author URI: https://popozure.info License: GPLv2 or later */ defined('ABSPATH') || die; class Cron_Test { public function __construct() { if (is_admin()) { register_activation_hook(__FILE__,array($this, 'activation')); register_deactivation_hook(__FILE__,array($this, 'deactivation')); } add_action('action_schedule', array($this, 'schedule_hook')); } public function activation() { wp_schedule_event(time(), 'hourly', 'action_schedule'); } public function deactivation() { wp_clear_scheduled_hook('action_schedule'); } public function schedule_hook() { wp_mail('xxxx@example.com', '現在の時刻は、'.date('H:i:s', current_time('timestamp')).'です。', 'ここに本文を記入します'); } } $cron_test = new cron_test;
(WordPressのビジュアルモードで記事を書いているので、先頭のスペースやタブが削除されてしまうのはご愛嬌…いや、見づらくてスイマセン(^-^;)
それぞれ何をやっているのか書いていきます。
add_actionでイベント定義
そもそも開発中の「Pz-LinkCard」に埋め込む予定なので、クラス化した状態でソースを書いています。
あまりクラス化したソースとか落ちていないので、参考になれば幸いです。
まずは __construct() の一番最後とかで add_action してみることにします。
add_action('action_schedule', array($this, 'schedule_hook'));
「action_schedule」は任意の名前で、「pz_linkcard_update」とか、スケジュールイベントだっていうのが分かり易い名前にすると良いです。
とりあえずこれで、「action_schedule」というフック名のアクションが起きたら、自分のクラス内の「schedule_hook」という関数を呼ぶ、という準備が整いました。
呼ばれる関数を準備
イベントが起きたときに実行される関数を準備します。
public function schedule_hook() { wp_mail('xxxx@example.com', '現在の時刻は、'.date('H:i:s', current_time('timestamp')).'です。', 'ここに本文を記入します'); }
wp_mail() を使ってメールを送ってみます。
関数名は「schedule_hook」にしてみました。任意のものにして良いですが、add_action で定義した関数名 array($this, ‘schedule_hook‘) も同じものにしましょう。
「何かイベントが起きたときに割り込む」のことを「hook」(フック、引っかけるの意)といいます。
第一パラメータは宛先です。「xxxx@example.com」を自分のメールアドレスに書き換えましょう。
第二パラメータはタイトルです。「現在の時刻は、22:00:00です。」といった感じで現在時刻(ここでは current_time() を使って、ブログのローカル時刻を使ってみました。)にしてみました。任意のものに変更するとよいでしょう。
第三パラメータは本文です。「ここに本文を記入します」は任意のものに変更するとよいでしょう。
スケジュールを登録する
スケジュールイベントを登録します。
今回の例では「action_schedule」というフック名のイベントを「hourly」(1時間ごと)に発生させます。
wp_schedule_event(time(), 'hourly', 'action_schedule');
wp_schedule_event でスケジュール登録します。
第一パラメータは最初の実行時間。UNIX時間を指定します。通常は time() を指定します。10秒後に最初の実行をしたい場合、time() + 10 とかにします。current_time() とかのローカル時刻では無いので注意。
第二パラメータは、「hourly」(1時間ごと)、「twicedaily」(1日2回=12時間ごと)、「daily」(1日ごと=24時間ごと)の3種類から指定します。もっと種類があっても良さそうな気がするのですが、これしか情報が無いので、これしかパラメータが無いのだと思います。
第三パラメータは、発生させるイベントの名前(フック名)です。今回は「action_schedule」と名付けました。
第四パラメータは、関数に与える引数(ひきすう、パラメータ)です。array(配列)で指定するようです。今回は無しにしました。
この wp_schedule_event をどこに書くかというと、__construct() に「イベント登録されていなかったら登録する」という形で書くか、
if (!wp_next_scheduled('action_schedule')) { wp_schedule_event( time(), 'hourly', 'action_schedule'); }
アクティベーション時に登録するように書きます。今回はこっちにしてみます。
まずは、アクティベーション時に activation という関数を呼ぶようにします。
register_activation_hook (__FILE__, array($this, 'activation'));
activation 関数を定義して、アクティベーション時に wp_schedule_event() でスケジュール登録します。
public function activation() { wp_schedule_event(time(), 'hourly', 'action_schedule'); }
スケジュールを解除する
プラグインの中でスケジュールを管理するわけでは無いので、プラグインを非アクティベーションするときにスケジュールを解除します。
まずは、非アクティベーション時に関数を呼ぶように。
register_deactivation_hook (__FILE__, array($this, 'deactivation'));
そして、非アクティベーション時に実行される関数の定義です。wp_clear_scheduled_hook() にフック名を記述して解除します。
public function deactivation() { wp_clear_scheduled_hook('action_schedule'); }
解除は scheduled_hook(スケジュールされたフック)と、過去形なので注意。
プラグインを有効化してみる
14行目の register_activation_hook() で登録・アクティベーション時のフック(割り込み)を定義しているので、プラグインを有効化(アクティベーション)すると、activation関数が呼ばれます。
activation関数の中では wp_schedule_event() によって、action_schedule というフック名を1時間ごとに実行するように定義しています。
というわけで、「プラグインを有効化」するとスケジュールが登録されているはずです。
「WP-Crontrol」をインストールしていたら、「ツール」→「Cron Events」で確認してみます。
登録されました。
フック名は被らないように、自分のプラグイン名とかと組み合わせるとよいでしょう。(自分の場合、「pz_linkcard」で使うので「pz_linkcard_update」とかにします。)
初回実行時間を time() としたので、もう実行されています。
メールが届いていました。放っておくと、1時間ごとにメールが届きます。
15行目の register_deactivation_hook() で非アクティベーション時のフックを定義しているので、プラグインを停止(非アクティベーション)すると、deactive関数が呼ばれます。
deactivation関数の中では、wp_clear_scheduled_hook() によって、action_schedule というフック名をクリア(解除)するように定義されています。
というわけで、「プラグインを停止」するとスケジュールが解除されているはずです。
「ツール」→「Cron Events」で確認すると、「action_schedule」の行は無くなっていました。
一回だけ実行したり、もっと細かくコントロールする「くふう」。
一回だけ実行したい場合、「登録→実行されたら解除」とかすると面倒です。
wp_schedule_single_event() を使うことで、一回だけ実行できます。
wp_schedule_single_event( time() + 10, 'action_schedule');
上の例では、10秒後( time()+10 )に、action_schedule というフック名のイベントを発生させることができます。
処理の流れで、(裏で)即時に実行させたいものは、この仕組みを使うと良いでしょう。
あくまでも一回だけスケジュールさせるものなので、たとえば同時に10回アクセスが来たからといって、10回実行されるわけではありません。
でも、数秒以上かかるような処理が、毎秒スケジュールされていくようなことになると、重くなりそうな気がします。(この辺は細かく確認はしていません。)
この「一回実行」を使うことで、「hourly」「twiceday」「daily」では出来ない細かいスケジュールが組めます。
具体的には、スケジュール実行が終わったら、次のスケジュールを組んで終了させます。
public function schedule_hook() { wp_mail('xxxx@example.com', '現在の時刻は、'.date('H:i:s', current_time('timestamp')).'です。', 'ここに本文を記入します'); wp_schedule_single_event(time() + 30, 'action_schedule'); // 次回のスケジュール }
上の例では30秒後に次のスケジュールを組んでいます。「 + 30」を忘れると、終わる度に即時実行されるようにスケジュールしてしまうので注意してください。(「あまりお勧めしません」とも言う。)
DBとかに処理するデータを貯めておいて、スケジュール実行されたときにDBをチェックして「まだ残っていたら、30秒後にもう一度処理をする」みたいな処理が良い気がします。
「30秒後にもう一度処理する」をしなくなったら、また1時間とか12時間ごとにチェックをする動きに戻ったり、そもそも定期のスケジュールは組まないで、通常の処理の中でデータを貯めるときに wp_schedule_single_event() で始める、というのも良いかも知れません。
Pz-LinkCardでの実装
リンクをカード形式で表示する「Pz-LinkCard」でスケジュール実行したくて試しているところです。
具体定期には、ソーシャルカウントを通常の取得処理から分離して、スケジュールだけしておこうと考えています。
こうすることで、ソーシャルカウントはスケジュールで裏で取得しておいてくれます。取得されていなければ表示されないし、取得されていれば表示されるし。
最後の取得から一定時間が経過したものについては、裏で取得してくれるようにできるわけです。
反面、あまりアクセスの無い記事中のリンクもこつこつと更新するようになるので、無駄なアクセスが増えそうな気がするのが悩みどころです。
実のところ、ソーシャルカウントの取得よりもむしろ記事そのものの取得に時間がかかるので、取得全般スケジュールにまわして、取得できていない間は「(取得中)」みたいな表示をさせて、取得できたら表示させるような設定項目を作るのも良いかな、と思っています。
これによって初めてのアクセス時、「表示に数十秒~数分かかる」という事態は避けることが出来ます。
が、「リンクが表示されていない」という状態が記事公開後数十秒~数分続いてしまうので、良し悪しです。
ということで、設定で「遅延取得:有効」とかあると良いのかなぁ。
↓取得中のイメージ
「記事の表示開始から指定秒数過ぎたらそれ以降の取得をしない機能」なんていうのも考えています。
たとえば、「5秒」と指定したら、記事を表示するのに5秒以上かかったら、それ以降のリンクは上のイメージみたいに「(取得中)」と表示される感じです。
リンク先の記事の取得そのものはスケジュールしておくので、次に表示したときにはちゃんと表示されるようなイメージです。
んー、あまり複雑にすると、バグの温床になりそうなのがこわいです(^-^;
では(^-^)o
コメント