wordpress CLIとcronを用いてプラグインの関数を定期自動実行させる
技術スタック PHPwordpress

wordpress CLIとcronを用いてプラグインの関数を定期自動実行させる

2020.11.26

こんにちはjunです。「別CMSで作成された4万件分の大量投稿をwordpressに引越しする」の記事であったように4万件のデータをマルチサイト構成で移行したことがありました。そこで新しい課題として、親サイトでは登録した他のサイト全ての投稿をページングで一覧で表示するという仕様にぶち当たりました。

基本的にwordpressはget_posts()を使えば簡単にページングも兼ね備えた一覧ページを作れるのですが、マルチサイト構成の場合は上手くいかずDBから上手く引っ張る必要がありました。しかしマルチサイトだけでも75サイトあり、そこから4万件分の投稿をとる必要があります。表示のたびにそんな高コストの処理はできないので、予め必要なデータをまとめたキャッシュテーブルを作ることで解決しました。

ただしキャッシュテーブルの弱点は更新が必要なことです。手動の更新は実装できましたが、1時間に1回自動で実行するなどの実装が必要となります。すぐに思いついた方法としてはcronを用いてキャッシュ作成の関数を叩くことです。

wordpressにcronやCLIでできないかな〜?と調べていたら公式でwp CLIというものが使えると聞き、見様見真似で実装ができました。wordpressで重い処理の定期実行などを考えている方はぜひ参考にしてみてください。

wp cli をインストール

公式サイトにあるようにcliを使用するにはコマンドラインを通じてインストールする必要があります。

curlなどで pharファイルを読み込む

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar

ドキュメントルート あたりで行ってみましょう。wp-cli.pharというファイルが出現します。これだけでも

php wp-cli.phar --info

という風にして実行することができます。wp ~~ と実行するためにはもう一工夫必要です。

実行権限をあたえ、移動

以下のようにして実行権限を付与し、wpというコマンドで打てるようにします。

chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp

はい。これでOKです。wp --infoを打ってみると

$ wp --info
OS:     Linux 4.19.76-linuxkit #1 SMP Tue May 26 11:42:35 UTC 2020 x86_64
Shell:  /bin/sh
PHP binary:     /usr/local/bin/php
PHP version:    7.4.12
php.ini used:
WP-CLI root dir:        phar://wp-cli.phar/vendor/wp-cli/wp-cli
WP-CLI vendor dir:      phar://wp-cli.phar/vendor
WP_CLI phar path:       /var/www/html
WP-CLI packages dir:
WP-CLI global config:
WP-CLI project config:
WP-CLI version: 2.4.0

こんな感じに情報が出てくればきちんと機能しています。

TIPS rootでやると怒られる

rootユーザーでコマンドを実行しようとすると以下のように怒られます。

Error: YIKES! It looks like you're running this as root. You probably meant to run this as the user that your WordPress installation exists under.

If you REALLY mean to run this as root, we won't stop you, but just bear in mind that any code on this site will then have full control of your server, making it quite DANGEROUS.

If you'd like to continue as root, please run this again, adding this flag:  --allow-root

If you'd like to run it as the user that this site is under, you can run the following to become the respective user:

    sudo -u USER -i -- wp <command>

ざっくり和訳すると

「ええっ!wpコマンドをrootユーザーで実行しようとしてない!?rootユーザーはサーバーもいろいろ弄れるから危ないよ。まあ、あなたがどうしてもrootユーザーで実行したいなら止めないけど。その時は –allow-root オプションを添えてね」

こんな感じです。rootユーザーはサーバーの最高権限を持っているので使うのは危ないです。さらにwpコマンドを通じてwordpressの内容を容易に破壊することも可能なので、rootでやるのはおすすめしないと警告されます。

回避する場合は警告の通り –allow-root オプションを添えるか、ユーザーを変更して実行します。実務サーバーではアプリケーションをrootで運用することはないので特に問題ないとは思います。

(自作)プラグインの関数にフックさせる

それではCLIがインストールされた所でプラグインの関数にフックさせてみましょう。方法は沢山ありますが簡単なのは自作のコマンドを追加してしますことです。CLIをインストールして、wpコマンドを実行するとWP_CLIクラスが実行時に使用可能になります。

WP_CLIクラスにはこちらのリファランスのように静的メソッドがいくつか用意されています。その中にWP_CLI::add_command()をコマンドを追加できるメソッドがあります。第一引数にコマンド名、第二引数にコールバック関数名または関数を指定します。

plugins/test/test.php
<?php
/*
Plugin Name: test
*/

if(!defined('ABSPATH')) {
    die('You are not allowed to call this page directly.');
}

function cmm_test(){
    echo 'test!';
}

if ( class_exists( 'WP_CLI' ) ) {
    WP_CLI::add_command( 'foo', 'cmm_test' );
}

例えば上記のようにした場合は以下のように実行されます。

$ wp foo
test!

なので同じようにして関数を定義して、フックさせます。

<?php
/*
Plugin Name: test
*/

if(!defined('ABSPATH')) {
    die('You are not allowed to call this page directly.');
}

function create_cache_table(){
    .... // めちゃくちゃ時間のかかる処理など
}

if ( class_exists( 'WP_CLI' ) ) {
    WP_CLI::add_command( 'create_cache', 'create_cache_table' );
}

ちなみにif ( class_exists( 'WP_CLI' ) ) がないとUndefined type 'WP_CLI'というエラーが発生して怒られます。ドキュメントルート 配下のwordpressフォルダにはWP_CLIクラスのソースがなく、コマンドを実行(wp-cli.pharを実行)するときにクラスが提供されるそうです。一応これが標準らしい。

コマンドが追加できたのであとはcronを設定してwpコマンドを設定してあげます。

cronを設定

rootでないユーザーで操作しているものとします。

$ crontab -e
PATH = /usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin;
0 * * * * cd /var/www/html && wp create_cache >> /dev/null 2>&1

このcronは1時間ごと(00分)に実行されます。cd /var/www/html でドキュメントルート へ移動して先ほどのコマンドを打って、思い処理の関数を実行しています。ちなみに上の PATH = .... がないと

/usr/bin/env: 'php': No such file or directory

というようにエラーが出てしまい、指定のcron処理が行われません。これ結構ハマったのですが、cron実行時のPATHの設定が/bin,/usr/binだけしか通っていないので、PHPやwpが存在する /usr/local/bin配下を見ておらずPHPが実行できないのが原因です。

なので予め普段使用しているPATHを出しておいて、crontab上で再定義しています。上記のようにすればあとは指定の時間が来れば

  1. cronでコマンドを実行
  2. コマンドに結びついた関数を呼び出す
  3. 処理が実行
  4. ログがあればそれに吐き出す

と言った自動運用が可能になります。

「>> /dev/null 2>&1」 これは「エラー出力を標準出力とし、/dev/nullに書き出す」という意味です。

「?」となった方にもう簡単説明します。>> で実行したコマンドの結果などを出力できます。エラーであればエラー文が得られます。そして /dev/null はunixのスペシャルファイルで、空のファイルです。空のファイルを指定して結果を捨てることができます。「無に書き出す」みたいなことをしています。2>&1 はエラー出力を標準出力にします。という意味です。

つまり>> /dev/null 2>&1はエラー出力であっても結果内容を残さず捨てるという意味です。テストぐらいであれば問題ないのですが、実務でやると何か起きたときに原因が分からないので以下のようにするといいです。

PATH = /usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin;
0 * * * * cd /var/www/html && wp create_cache >> /var/log/cron.log 2>&1

こうするときちんとログファイルにエラーが残ります。

Copyright © 2021 jun. All rights reserved.