🐥

WordPressでタイムアウトになる重い処理をCLI実装する

2024/04/28に公開

WordPressで全記事対象の処理などの重い処理を実行すると、タイムアウトエラーが出ることがある。処理されたレコードを確認して続きから実行するなどの方法があるが、本質的にはバッチ処理なのでSSHできる環境であればバッチ処理として実装すべきである。

具体的には、WordPressにはコマンドラインから実行できるWP-CLIという仕組みが用意されている。

WP-CLIを使うメリット

  • タイムアウトの制限は受けない
  • cronと組み合わせてバッチ処理できる
  • スクリプト内ではWPQueryなどWordPress固有の機能がつかえる

WP-CLIの実行方法

eval-fileでWordPress環境下でPHPファイルを実行できる。

ターミナルでWordPressのインストールディレクトリにcdする場合

カレントディレクトリのWP環境を読み込んで、script.phpを実行する。

cd /path/to/wordpress
wp eval-file /absolute/path/to/script.php

カレントディレクトリがWordPressのインストールディレクトリでない場合:

--pathオプションでWordPressのインストールディレクトリを指定する。

実行内容は変わらないが、cdせずに1行のコマンドで実行できるメリットがある。

wp eval-file /absolute/path/to/script.php --path=/path/to/wordpress

ファイルの配置

WP-CLIの実行にあたり、バッチファイルはWordPress配下にある必要はない。

セキュリティを考慮すると、バッチファイルはインストールディレクトリ外に配置したほうがよさそう。

/var
|-- /www
    |-- /yourdomain
        |-- /public_html  # ウェブアクセス可能
        |-- /scripts      # ウェブアクセス不可能
            |-- batch.php

全記事更新するバッチのサンプル

<?php
/**
 * 全投稿のタイトルを更新するスクリプト
 */
function update_post_titles() {
    $offset = 0;
    $batch_size = 50;

    while (true) {
        // WP_Queryを使用して50件ずつ投稿を取得
        $args = [
            'post_type'      => 'post', // 投稿タイプ
            'posts_per_page' => $batch_size,
            'offset'         => $offset,
            'post_status'    => 'publish' // 公開されている投稿のみ
        ];
        
        $query = new WP_Query($args);

        // 投稿がなければループを抜ける
        if (!$query->have_posts()) {
            break;
        }

        // 各投稿のタイトルを更新
        foreach ($query->posts as $post) {
            // 新しいタイトルの生成(元のタイトルに現在の日時を追加)
            $new_title = $post->post_title . ' - Updated on ' . date('Y-m-d H:i:s');

            // 投稿データの準備
            $update_data = [
                'ID'         => $post->ID,
                'post_title' => $new_title
            ];

            // 投稿の更新
            wp_update_post($update_data);
        }

        // メモリのクリア
        wp_reset_postdata();

        // 次のバッチのためにオフセットを更新
        $offset += $batch_size;
    }

    echo "All posts have been updated.\n";
}

// タイトル更新関数の実行
update_post_titles();

Discussion