📮

(JS→PHP→WP)WP REST APIでWordPressに投稿する

2024/05/25に公開

概要

JavaScriptで入力されたコンテンツをPHPに送信し、PHPからWordPressに投稿を実行します。
この方法ではWordPressのログイン機能を使用せず、アプリケーションパスワードによる認証を行って投稿を作成します。
つまりアプリケーションパスワードさえわかってしまえば荒らし放題のため、認証情報を秘匿するためにわざわざPHPを間に挟んでいます。
くれぐれもクライアントサイドで認証情報を扱わないようにね。

アプリケーションパスワードの発行

WordPressのユーザー一覧から、「アプリケーションパスワード」を発行します。
(管理者権限以外のユーザーで発行するのが望ましいです。)
WordPress 5.6以降であれば標準機能となっていますが、それ以前の場合はプラグインを使用することで有効化できます。


「アプリケーションパスワードの名前」と、「発行したパスワード」を控えておきます。
トークンは再表示できません。

画像の投稿

まず、phpに投稿したいデータを送る。

JavaScriptのコード
    <input type="file" onchange="addFile" accept="image/png, image/jpeg, image/gif" />
Create.js
let file;

// ファイルの取得
function addFile(e) {
  // 何も選択されなかった場合
  if (e.target === undefined || (e.target.files && e.target.files.length === 0)) {
    return;
  }

  file = e.target.files[0];
  // 画像のプレビューを行う場合
  let _img = document.createElement('img');
  _img.setAttribute("src", URL.createObjectURL(file) );
  _img.setAttribute("alt", file.name);
  document.body.appendChild(_img);
}

// ファイルの送信
async function postFile() {
  if (file) {
    // formDataを作成
    const formData = new FormData();
    formData.append("file", file);

    // 画像を送信
    const mediaResponse = await fetch("/AddMediaAPI.php", {
      method: "POST",
      body: formData,
    });

    const media_data = await mediaResponse.json();

    if (media_data) {
      if (media_data.error) {
        alert("画像アップロードに失敗しました");
        throw new Error(`HTTP error! status: ${JSON.stringify(media_data)}`);
      } else {
        // 投稿を作成するときに使用する画像のID
        console.log(media_data.id);
      }
    }
  }
}

そして、PHPでデータを受け取りcURLでREST APIを叩いてWPに画像を作成する。

PHPのコード
AddMediaAPI.php
<?php
// APIエンドポイントのURL
$url = 'https://sample.com/wp-json/wp/v2/media/';
// 認証コード(Base64)の作成
$token = "Basic " . base64_encode("name:password");

// ファイルの受け取り
$file = $_FILES['file'] ?? null;

if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
    // ファイルがない、またはアップロードエラーがある場合の処理
    echo json_encode(array(
        'error' => 'File upload failed',
        'details' => $file ? $file['error'] : 'No file sent'
    ));
    exit;
}

$request_header = [
  'Authorization: ' . $token,
  'Content-Disposition: attachment; filename="' . $file['name'] . '"',// filenameという名前のファイルをattachment(ダウンロード)する。
];

$cfile = new \CURLFile($file['tmp_name'],$file['type'],$file['name']);// ファイルパス、ファイル拡張子、ファイル名
$data = array('file'=> $cfile);// multipart/form-dataを指定しているので配列にする。

// cURLセッションを初期化
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);// アクセスするURL
curl_setopt($ch, CURLOPT_POST, true);// POST送信する
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);// 送信するデータを指定
curl_setopt($ch, CURLOPT_HTTPHEADER, $request_header);// リクエストヘッダを指定
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);// 送信先のSSL証明書を検証する(開発環境で使用)
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);// 送信結果を返却する

// APIリクエストを実行し、結果を取得
$response = curl_exec($ch);

// cURLエラーのチェック
if (curl_errno($ch)) {
    echo json_encode(array(
        'error' => 'cURL error: ' . curl_error($ch)
    ));
    curl_close($ch);
    exit;
}

// cURLセッションを終了
curl_close($ch);

// 結果をJSON形式で出力
header('Content-Type: application/json');

// 結果をJSON形式でデコード
$response_data = json_decode($response, true);

// ステータスコードの確認
$status = $response_data['data']['status'] ?? null;
if ($status !== 200) {
    echo json_encode(array(
        'error' => 'Image creation failed',
        'details' => $response_data
    ));
    exit;
}

// 成功の場合のレスポンス
echo json_encode($response_data);

本文の投稿・更新

データに投稿IDを含めれば、投稿の更新もできます。

PHPに投稿したいデータを送る

JavaScriptのコード
create.js
// 投稿を作成
async function createPost() {
  const postResponse = await fetch("/AddPostAPI.php", {
    method: "POST",
    body: JSON.stringify({
      title: content_title,
      content: content_value,
      author: 2,// アプリケーションパスワードを持つユーザーのID
      status: "publish",
      featured_media: content_image || null, // サムネイルの画像IDを設定
      categories: [3,4],// カテゴリーのIDを指定
      tags: [3,4],// タグのIDを指定
    }),
  });
  
  const postData = await postResponse.json();
  
  if (postData) {
    if (postData.error) {
      alert("投稿の作成に失敗しました");
      throw new Error(`HTTP error! status: ${JSON.stringify(postData)}`);
    } else {
      console.log(postData);
    }
  }
}

PHPでデータを受け取り、cURLでREST APIを叩いてWPに投稿を作成する。

PHPのコード
AddPostAPI.php
<?php
// APIエンドポイントのURL
$url = 'https://sample.com/wp-json/wp/v2/posts/';
// 認証コード(Base64)の作成
$token = "Basic " . base64_encode("name:password");

$content = json_decode(file_get_contents('php://input'),true);
// $_GETや$_POSTなど、他のデータは取得できなくなる。

if (!$content) {
    // データがない場合の処理
    echo json_encode(array(
        'error' => 'Data upload failed'
    ));
    exit;
}

// サニタイズ処理
function recursive_htmlspecialchars($input) {
  if (is_array($input)) {
    return array_map('recursive_htmlspecialchars', $input);
  } else {
    return htmlspecialchars($input);
  }
}

$title = filter_var($content['title'], FILTER_SANITIZE_STRING);
// $content['content']の中にネストされた配列やオブジェクトがある場合は、json_encodeする
$contents = json_encode(recursive_htmlspecialchars($content['content']));
$author = filter_var($content['author'], FILTER_VALIDATE_INT);
$status = filter_var($content['status'], FILTER_SANITIZE_STRING);
$featured_media = isset($content['featured_media']) ? filter_var($content['featured_media'], FILTER_VALIDATE_INT) : null;
$categories = recursive_htmlspecialchars($content['categories']);
$tags = recursive_htmlspecialchars($content['tags']);

// 投稿ID
$postId = isset($content['id']) ? $content['id'] : null;

// 投稿データの作成
$data = json_encode(array(
  'title' => $title,
  'content' => $contents,
  'author' => $author,
  'status' => $status,
  'featured_media' => $featured_media,
  'categories' => $categories,
  'tags' => $tags,
));

$request_header = [
  'Authorization: ' . $token,
  'Content-Type: application/json'
];

// cURLセッションを初期化
$ch = curl_init();
if(!empty($postId)) {
  // IDがある場合(投稿を更新する場合)
  curl_setopt($ch, CURLOPT_URL, $url . $postId);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
} else {
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_POST, true);
}
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_HTTPHEADER, $request_header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// APIリクエストを実行し、結果を取得
$response = curl_exec($ch);

// cURLエラーのチェック
if (curl_errno($ch)) {
  echo json_encode(array(
      'error' => 'cURL error: ' . curl_error($ch)
  ));
  curl_close($ch);
  exit;
}

// cURLセッションを終了
curl_close($ch);

// 結果をJSON形式で出力
header('Content-Type: application/json');

// 結果をJSON形式でデコード
$response_data = json_decode($response, true);

// ステータスコードの確認
$status = $response_data['data']['status'] ?? null;
if ($status !== 200) {
    echo json_encode(array(
        'error' => 'Image creation failed',
        'details' => $response_data
    ));
    exit;
}

// 成功の場合のレスポンス
echo json_encode($response_data);

Discussion