毎週火曜日、Help me, hackers!に上がったコードを紹介していくコードDJ 第7回。

今、流行の24シーズン1を見ているkomagata a.k.a. DJ(@流行に疎い方)です。

今回紹介するコードはコレ。

任意の期間、ブログが更新されないとメールが飛ぶwordpressのplugin – Help me, hackers!

出題したのは@machida a.k.a. お米嫌い。議論に対していきなりコードで回答を示したのが@terakuma a.k.a. 法務系PHPer。

コード:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
<?php
/*
Plugin Name: Remember The WordPress
Plugin URI: http://github.com/e2esoundcom/Remember-The-WordPress
Description: If you forgot write a new post,this plugin send you E-mail.
Version: 0.2
Author: Yuya Terajima
Author URI: http://www.e2esound.com/
*/


/* When Activate Plugin
==================================================================== */

function rtw_activate() {
    $time = time();
    $terms = 7; //default terms
    $unixtime_per_day = 86400;
    $email = get_bloginfo('admin_email');
    $subject = "Remember the WordPress!!";
    $message = "Blogの更新が滞っているようです。サイトの更新をお願いします。";

    if(!get_option('rtw_initialized') || get_option('rtw_initialized') !== $time) {
        update_option('rtw_initialized',$time);
    }
    if(!get_option('rtw_terms')) {
        update_option('rtw_terms',$terms * $unixtime_per_day);
    }
    if(!get_option('rtw_email')) {
        update_option('rtw_email',$email);
    }
    if(!get_option('rtw_subject')) {
        update_option('rtw_subject',$subject);
    }
    if(!get_option('rtw_message')) {
        update_option('rtw_message',$message);
    }
    wp_schedule_event($time + 86400, 'daily', 'rtw_cron');
}
register_activation_hook(__FILE__, 'rtw_activate');

function get_latest_post_time() {
    global $wpdb;
    $query = "SELECT post_date FROM ".$wpdb->posts."
            WHERE post_status = 'publish'
            ORDER BY `"
.$wpdb->posts."`.`post_date` DESC LIMIT 0,1";
    $query = $wpdb->prepare($query);
    return strtotime($wpdb->get_var($query));
    }

function rtw_sendmail()
{
    $subject  = get_option('rtw_subject');
    $mailbody = get_option('rtw_message');
    $to_email = get_option('rtw_email');
   
    mb_send_mail($to_email,$subject,$mailbody);
}

function rtw_compare_time(){
    $unixtime_per_day = 86400;
    $latest = get_latest_post_time();
    $terms = intval(get_option('rtw_terms')) + intval($latest);
    $time = time();

    if(intval($terms) < intval($time)){
        rtw_sendmail();
    }
}
add_action('rtw_cron', 'rtw_compare_time');

/* When New Post or Edited Post.
==================================================================== */


function rtw_new_posted(){
    update_option('rtw_initialized',time());
    }
add_action('publish_post','rtw_new_posted');

/* When De-Activate Plugin
==================================================================== */


function rtw_deactivate() {
    wp_clear_scheduled_hook('rtw_cron');
    delete_option('rtw_initialized');
    delete_option('rtw_terms');
    delete_option('rtw_message');
    delete_option('rtw_subject');
    delete_option('rtw_email');
}
register_deactivation_hook(__FILE__, 'rtw_deactivate');


/* Admin
==================================================================== */

function rtw_add_admin_menu(){
    add_options_page('Remember The WordPress','Remember The WP','administrator',__FILE__,'rtw_add_admin_page');
}
add_action('admin_menu','rtw_add_admin_menu');

function rtw_add_admin_page(){    
   
    if(isset($_POST['posted']) === FALSE){
        $posted = FALSE;
    }elseif(isset($_POST['posted']) === TRUE){
        $posted = TRUE;
    }

    $unixtime_per_day = 86400;
    if($posted) {
        //Validation
        if(preg_match('/[1-3][0-9]|[1-9]/',intval($_POST['terms']) AND intval($_POST['terms']) <= 30)){
            update_option('rtw_terms',intval($_POST['terms'] * $unixtime_per_day));
            update_option('rtw_email',stripslashes($_POST['email']));
            update_option('rtw_subject',stripslashes($_POST['subject']));
            update_option('rtw_message',stripslashes($_POST['message']));
            $rtw_error = FALSE;
        }else{
            $rtw_error = TRUE;
        }
    }
?>

<?php
    //Admin Page Start
    //Updated Message
    if($posted === TRUE AND $rtw_error === FALSE) : ?>
<div class="updated"><p><strong>設定を保存しました</strong></p></div>
<?php elseif($posted === TRUE AND $rtw_error === TRUE):?>
<div class="error"><p><strong>アラート発生日数は1-30の間の値を入力して下さい。</strong></p></div>
<?php endif; ?>

<div class="wrap">
    <h2>Remember The WordPress Settings</h2>
    <form method="post" action="<?php echo str_replace( '%7E', '~', $_SERVER['REQUEST_URI']); ?>">
        <input type="hidden" name="posted" value="yes">
        <table class="form-table">
            <tr valign="top">
                <th scope="row"><label for="terms">アラート発生日数<label></th>
                <td>
                    <input name="terms" type="text" id="terms" value="<?php echo intval(get_option('rtw_terms') / $unixtime_per_day); ?>" class="regular-text code" /><br />
                    1-30までの数字を入力してください。
                </td>
            </tr>
            <tr valign="top">
                <th scope="row"><label for="email">送信先E-mailアドレス<label></th>
                <td>
                    <input name="email" type="text" id="email" value="<?php echo htmlspecialchars(get_option('rtw_email'),ENT_QUOTES); ?>" class="regular-text code" /><br />
                    アラートメールの送信先E-mailアドレスを入力してください。
                </td>
            </tr>
            <tr valign="top">
                <th scope="row"><label for="terms">メールタイトル<label></th>
                <td>
                    <input name="subject" type="text" id="subject" value="<?php echo htmlspecialchars(get_option('rtw_subject'),ENT_QUOTES); ?>" class="regular-text code" /><br />
                    送信メッセージの「タイトル」を入力してください。
                </td>
            </tr>
            <tr valign="top">
                <th scope="row"><label for="terms">メール本文<label></th>
                <td>
                <textarea name='message' id='message' cols='50' rows='10'><?php echo htmlspecialchars(get_option('rtw_message'),ENT_QUOTES); ?></textarea>
                    <br />
                    送信メッセージの「内容」を入力してください。
                </td>
            </tr>

           
        </table>

        <p class="submit">
            <input type="submit" name="Submit" class="button-primary" value="変更を保存" />
        </p>
    </form>
</div>
<?php } ?>

少々長いがへこたれずに頑張ってみていくことにしよう。(自分自身を鼓舞)

WordPressプラグインの作成方法

前提としてこれはWordPressプラグインなのでその大まかな作り方を紹介しよう。

まず、単体のphpファイルか、もしくは同名のディレクトリにプラグイン用のファイルを作る。
(例:foo_plugin/foo_plugin.php)

プラグインの各種情報は決まったコメントの書き方があるのでそれにしたがってファイル内先頭に書く。これがWordPressのプラグイン管理画面に出てくる情報になる。

一番単純なプラグインとしては、プラグインを管理画面から有効にすると、プラグインのphpファイルがWordPressにincludeされる。よって適当な関数をプラグイン内で定義しておき、テーマの中で使うといったことが可能だ。(モチロン名前はぶつかる可能性がある。プラグイン名をprefixにした関数を作るか、プラグイン名のクラスに閉じ込めることが推奨されている。)

これでWordPressの関数や変数、DBをプラグイン内からでも使うことが出来る。また、プラグイン用にactionやfilterといったフックをかける事ができる。この辺は公式ドキュメントに全関数リファレンスやプラグイン用のフックの説明がちゃんとあるのでPHPが分かれば理解するのは容易だ。(Main Page – WordPress Codex 日本語版

擬似cron

今回のコードを理解する上でもう一つ必要になるのが、wp_schedule_event関数を使った擬似cronの仕組みだ。WordPressにはwp_schedule_event関数でcronのように定期スケジュールを登録することが出来る。こういう処理はcron daemonのように常に起動しているプロセスと連携する必要があり、一見単純なPHPウェブアプリだけでは無理に思えるが、Webからのユーザーリクエストのみをトリガーにcronのようなスケジュール実行を可能にしている。

もちろんWebからのリクエスト頼みなのでアクセスがまったく来なければスケジュールした時間には実行されない擬似的な機能なので厳密な処理には向かない。しかし、一般的なサイトであれば大抵毎分1アクセスぐらいはあるので大体このぐらいの頻度でやって欲しいといった処理なら実用上は問題なさそうだ。

参照:Function Reference/wp schedule event « WordPress Codex

こんないい加減な仕組み使うなんてありえねー!とヤルタイプからは言われそうだが、phpのsession削除は同様の仕組みで行われている。(アクセスが来なければ永久にセッションファイルは消えない)
根本的なこの仕組の是非は兎も角、PHPの流儀に従った実装方法ではあると言える。

参照:PHP: 実行時設定 – Manual

Remenber The WordPress

まず、38行目でregister_activation_hook関数で自分自身のファイルのrtw_activate関数をactivation_hookに登録している。activationとはプラグイン管理画面でそのプラグインを「有効」にしたときのことだ。データベーステーブルを独自に持つようなプラグインでは大抵この場所でテーブルをCREATEしている。(WordPressはORMを提供していないので、各種プラグインのこの場所にはMySQLに特化したSQLが大抵使用されている。WordPressはプラグインエコシステムがキモであるため、コレのせいで他DBへの対応が非常に難しくなっている。)

rtw_activate関数の中でwp_schedule_eventを使ってdailyで実行されるようにrtw_cronを登録している。

あとは、40行目のget_latest_post_time関数で最終投稿日を取得して、管理画面で設定できる「何日更新が無かったらメールを送るか?」という値と比較してmb_send_mailでメールを送っている。

殆どの行数は管理画面用なので長く見えるし大変そうだが、管理画面からプラグインの設定が出来るというのがWordPressユーザーに非常に訴える箇所なので重要だ。

議論にコードで答えた事。(これが結構貴重なんだが)WordPress流儀に則った実装。管理画面まで手を抜かない所。等々、@terakuma a.k.a. 法務系PHPerは素晴らしい。それにRemember The MilkをパクったRemember The WordPressというプラグイン名も素敵だ。

WordPressプラグインで困ったことがあったら@terakuma a.k.a. 法務系PHPerに相談してみたらいいかもしれない!

View Comments

この記事のタグ

View Comments to “コードDJ 第7回 – WordPressの擬似cron機構”

  1. machida より:

    ちょっ、お米嫌いってw!
    お米が美味しいところ出身の女の子から嫌われてしまう!

    夜はお米をあんまり食べませんが、昼はよく食べます。
    お米をあまり食べないのは炊飯器がうちにないのが原因。

blog comments powered by Disqus

ブログの新着記事