スポンサーリンク

WP-CLIを使って記事を自動投稿する(失敗談)

WP-CLIというコマンドラインからWordPressを操作することができるすごいツールがあります。
これを利用してWebAPIのリクエスト結果を元に記事を自動生成するプログラムを書いたところ失敗してしまったのでメモ。
WP-CLI自体のインストールは以下の記事を参考にしました。
参考:WordPress WP-CLIインストール (windows編)


やりたかったことは以下のような処理

  1. WebAPIでjsonのレスポンスを受け取る
  2. レスポンスデータを元に記事の本文データをファイルに作成する
  3. wp post create honbun.txtで記事を作成する

実際は以下のような長い処理…

  1. WebAPIでjsonのレスポンスを受け取る
  2. wp post createで空記事を作成する
    1. 実行時のメッセージを正規表現でパースしてpost_idを抽出する(^q^
  3. 画像をwp media importでWordPressに取り込む
    1. 実行時のメッセージを正規表現でパースしてpost_idを抽出する(^q^
  4. レスポンスデータを元に記事の本文データをファイルに作成する
    1. 画像のファイルパスは3-1で抽出したpost_idを元にwp post getで取得
  5. wp post updateで本文を更新する
    1. –post_titleでタイトルに日本語を含めるとエラーになってしまう(^q^
  6. どうしようもないのでWordPress本体直下のwp-load.phpをインクルードし、wp_update_postコマンドから直で更新する

wpコマンドをexecで呼び出す際カレントがwordpress本体のディレクトリである必要があるのでcdコマンドと連結して以下のような感じになりました。コマンドを連結する文字はWindows環境だと&が1つですが、Linux環境だと2つになるので定数で定義しておいたものを使用しています。

// 空記事を作成する
unset($output, $return_var);
exec("cd {$_(WP_DIR)} {$_(EXEC_AND)} wp post create --post_type=\"post\" --post_title=\"{$item['item_id']}\" --post_status=draft ", $output, $return_var);
if (empty($output) || empty($output[0]) || $return_var != 0) {
  throw new Exception('wp post error.');
}
$post_id = extract_wp_post_id($output[0]);
if (is_null($post_id) || empty($post_id) || !is_numeric($post_id)) {
  throw new Exception('wp post_id error.');
}

2-1、3-1の抽出用のプログラムは以下のような感じです。

/**
 * wp post createコマンドを実行したときのレスポンスから投稿IDを抽出する
 *
 * 例)wp post create --post_type=\"post\" --post_title="test title" --post_status=draft
 * → Success: Created post 65.
 *
 * @param string $strings
 * @return unknown|NULL
 */
function extract_wp_post_id($strings = '') {
  $pattern = "/^Success: Created post (\d+)\.$/u";
  if (preg_match($pattern, $strings, $matches)) {
    // サイズ2の配列のとき正常
    // 例
    // $matches[0] = Success: Created post 65.
    // $matches[1] = 65
    if (!empty($matches) && count($matches) == 2 && is_numeric($matches[1])) {
      return $matches[1];
    }
  }
  return null;
}

/**
 * wp media importコマンドを実行したときのレスポンスから画像IDを抽出する
 *
 * 例1)アイキャッチ画像を登録するときのコマンド
 * wp media import "C:\Users\hoge\Pictures\Sample Pictures\Desert.jpg" --post_id=33 --featured_image
 * → Imported file 'C:\Users\hoge\Pictures\Sample Pictures\Desert.jpg' as attachment ID 64 and attached to post 33 as featured image.
 *    Success: Imported 1 of 1 images.
 *
 * 例2)普通の画像登録コマンド
 * wp media import "C:\Users\hoge\Pictures\Sample Pictures\Desert.jpg" --post_id=33
 * → Imported file 'C:\Users\hoge\Pictures\Sample Pictures\Desert.jpg' as attachment ID 63 and attached to post 33.
 *    Success: Imported 1 of 1 items.
 *
 * @param string $strings
 * @return unknown|NULL
 */
function extract_wp_image_id($strings = '') {
  $pattern = "/^Imported file '(.*)' as attachment ID (\d+) and attached to post (\d+).+$/u";
  if (preg_match($pattern, $strings, $matches)) {
    // サイズ4の配列のとき正常
    // 例)
    // matches[0] = Imported file 'C:Users hogePicturesSample PicturesDesert.jpg' as attachment ID 63 and attached to post 33.
    // matches[1] = C:Users hogePicturesSample PicturesDesert.jpg
    // matches[2] = 63
    // matches[3] = 33
    if (!empty($matches) && count($matches) == 4 && is_numeric($matches[2])) {
      return $matches[2];
    }
  }
  return null;
}

4-1のwp post getはオプションを指定しないとMySQLをコマンドラインで実行したときのような表示で返ってきてしまうので以下のようなプログラムになりました。

unset($output, $return_var);
exec("cd {$_(WP_DIR)} {$_(EXEC_AND)} wp post get {$image_id} --fields=guid --format=json", $output, $return_var);
if (empty($output) || empty($output[0]) || $return_var != 0) {
  throw new Exception('wp post get error. image cant read.');
}
$image_url = @json_decode($output[0], true)['guid'];

5-1については–debugオプションを付加して実行した際のログの中で日本語が消えてることを確認しました。以下のログの2行目になります。実際は日本語のタイトルが指定してありましたが、デバッグログの中で消えてしまっていました。
wp-cli.pharを展開してソース少し追ってみたりもしましたが諦めました(´;ω;`)

Debug (bootstrap): No project config found (0.221s)
Debug (bootstrap): argv: c:/wp-cli/wp-cli.phar post create --post_type=post --post_title= --post_status=draft --debug (0.221s)
Debug (bootstrap): ABSPATH defined: E:/workspace/@@@@@@@@@@/ (0.222s)
Debug (bootstrap): Begin WordPress load (0.225s)
Debug (bootstrap): wp-config.php path: E:\workspace\@@@@@@@@@@\wp-config.php (0.225s)
Debug (bootstrap): Loaded WordPress (0.567s)
Debug (hooks): Processing hook "before_run_command" with 1 callbacks (0.568s)
Debug (hooks): On hook "before_run_command": WP_CLI\Bootstrap\RegisterDeferredCommands->add_deferred_commands() (0.569s)
Debug (bootstrap): Running command: post create (0.569s)
Error: データベースに投稿を追加できませんでした

6の更新処理はwp-load.phpを読み込んだ状態で以下のように書きました。

$update_data = [
    'ID' => $post_id,
    'post_title' => $item['title'],
];
$update_post_id = wp_update_post($update_data, true);
if (is_wp_error( $update_post_id )) {
  $errors = $update_post_id->get_error_messages();
  foreach ($errors as $error) {
    echo $error;
  }
}

上記のような長い処理を以って目的としたものは一応作れました。が、これだったら最初からwp-load.phpを読み込んだバッチ処理の開発を行った方が綺麗に作れたような気がしないでもない。そっちはそっちでWordPressのドキュメントで関数についてたくさん調べないといけなそう。。
phpのプログラムからexecで呼び出すという発想の時点でWP-CLIを使っている意味が薄れてしまっていたのかなーと考えたりしています;;

タイトルとURLをコピーしました