WordPress関数一覧まとめ【Part2】プラグイン開発者向け完全ガイド|設定画面・管理メニュー・メタボックス・権限

WordPress関数大全 WordPress

プラグイン開発は、ひと言でいえば「WordPressのルールに沿って機能を拡張する」仕事です。しかし「動けばOK」で組んでしまうと、後から必ず問題が発生します。

なぜなら、プラグインには以下のような要件があるからです:

  • どのテーマでも正しく動作する必要がある
  • 管理画面から設定を変更できる必要がある
  • 他のプラグインとの競合やWordPressのアップデートに耐える必要がある
  • セキュリティ(nonce検証・権限チェック・入力サニタイズ)が必須

つまりプラグインは長期運用を前提としたソフトウェアなのです。

この記事はWordPress関数一覧まとめの3部構成のPart2です。Part1ではテーマ制作者向けのテンプレタグを、Part3ではAjax/REST APIとセキュリティを解説しています。

  1. 1. プラグイン開発の基本構造|フックとファイル配置
    1. 基本フック関数
      1. add_action( $hook_name, $callback, $priority = 10, $accepted_args = 1 )
      2. add_filter( $hook_name, $callback, $priority = 10, $accepted_args = 1 )
      3. remove_action() / remove_filter()
    2. 「どのフックで何をするか」の設計
  2. 2. 有効化/無効化フック|activation hooks
    1. register_activation_hook( $file, $callback )
    2. register_deactivation_hook( $file, $callback )
  3. 3. 設定値の保存|Options API
    1. get_option( $option, $default = false )
    2. add_option( $option, $value, $deprecated = ”, $autoload = ‘yes’ )
    3. update_option( $option, $value, $autoload = null )
    4. delete_option( $option )
    5. Options API使用時の注意点
  4. 4. 設定画面を正攻法で作る|Settings API
    1. register_setting( $option_group, $option_name, $args )
    2. add_settings_section( $id, $title, $callback, $page )
    3. add_settings_field( $id, $title, $callback, $page, $section, $args )
    4. settings_fields() / do_settings_sections() / submit_button()
  5. 5. 管理メニュー|add_menu_page / add_submenu_page
    1. add_menu_page()
    2. add_submenu_page()
    3. メニュー位置の目安
  6. 6. 投稿編集画面の拡張|メタボックスとカスタムフィールド
    1. add_meta_box()
    2. メタボックスの保存処理
    3. Post Meta関連関数
  7. 7. カスタム投稿タイプとタクソノミー
    1. register_post_type()
    2. register_taxonomy()
    3. rewriteルールのフラッシュ
  8. 8. 画面別のCSS/JS読み込み|管理画面でのenqueue
    1. admin_enqueue_scripts + $hook_suffix
  9. 9. 権限とロール|capability設計
    1. current_user_can( $capability )
    2. よく使う権限(capability)
  10. 10. admin_post_|フォーム送信の受け口
    1. admin_post_{action} / admin_post_nopriv_{action}
  11. 11. WP_List_Table|管理画面の一覧テーブル
  12. 12. 独自アップデート機能|プラグインの自動更新
    1. pre_set_site_transient_update_plugins
  13. 13. $wpdb深掘り|安全なSQL・テーブル設計
    1. $wpdb->prepare() – SQLインジェクション対策の基本
    2. プレースホルダの種類
    3. 独自テーブルの作成
  14. まとめ

1. プラグイン開発の基本構造|フックとファイル配置

WordPressでは、「何かしたい」=「どこかのフックで処理を登録する」という考え方が基本です。プラグインは必ずフックを通じてWordPressの動作に介入します。

プラグインファイルの冒頭には、必ずプラグインヘッダーコメントを記述します。これがないとWordPressはプラグインとして認識しません。

<?php
/**
 * Plugin Name: My Plugin
 * Plugin URI: https://example.com/my-plugin
 * Description: プラグインの説明文をここに記述します。
 * Version: 1.0.0
 * Author: Your Name
 * Author URI: https://example.com
 * License: GPL v2 or later
 * Text Domain: my-plugin
 */

// 直接アクセス禁止
defined( 'ABSPATH' ) || exit;

// 初期化処理
add_action( 'init', function() {
  // CPT登録、ショートコード登録などはここで
});

基本フック関数

add_action( $hook_name, $callback, $priority = 10, $accepted_args = 1 )

目的:特定のタイミングで処理を実行する

WordPressは処理の各段階で「アクションフック」と呼ばれるタイミングポイントを提供しています。add_action()を使うことで、そのタイミングに自分の処理を挿入できます。

たとえばinitはWordPressの初期化が完了した直後、wp_enqueue_scriptsはスクリプトを読み込むべきタイミング、save_postは投稿が保存される瞬間に発火します。

第3引数の$priorityは実行順序を制御します。数値が小さいほど早く実行されます。デフォルトは10で、他のプラグインより先に実行したい場合は5や1を、後から実行したい場合は20や100を指定します。

add_filter( $hook_name, $callback, $priority = 10, $accepted_args = 1 )

目的:データを加工して返す

フィルターフックは、ある値がWordPressの処理を通過する際に、その値を変更できるポイントです。add_action()との違いは、必ず値を返す必要がある点です。

たとえばthe_contentフィルターは投稿本文が表示される前に発火し、本文を加工できます。the_titleはタイトル、excerpt_lengthは抜粋の文字数を変更できます。

// 抜粋の長さを変更
add_filter( 'excerpt_length', function( $length ) {
  return 40; // 40語に変更
});

remove_action() / remove_filter()

目的:登録されたフックを解除する

他のプラグインやテーマが登録した処理を無効化したい場合に使用します。解除するには、登録時と同じコールバック関数、同じ優先度を指定する必要があります。

「どのフックで何をするか」の設計

プラグイン開発で迷子にならないコツは、最初に「どのフックで何をするか」を設計することです。主要なフックポイントを把握しておきましょう:

  • plugins_loaded – すべてのプラグインが読み込まれた後
  • init – WordPress初期化完了後(CPT登録などに最適)
  • admin_init – 管理画面の初期化(Settings API登録に使用)
  • admin_menu – 管理メニュー登録
  • wp_enqueue_scripts – フロントのCSS/JS読み込み
  • admin_enqueue_scripts – 管理画面のCSS/JS読み込み

2. 有効化/無効化フック|activation hooks

プラグインが有効化/無効化されたタイミングで実行される処理を登録できます。初期設定の作成、データベーステーブルの作成、クリーンアップ処理などに使用します。

register_activation_hook( $file, $callback )

目的:プラグイン有効化時に1回だけ実行する処理を登録する

プラグインを初めて有効化したとき、または更新後に再有効化したときに実行されます。以下のような処理に使用されることが多いです:

  • 初期オプション値の設定
  • 独自データベーステーブルの作成
  • カスタム投稿タイプ登録後のリライトルールのフラッシュ
  • 必要なディレクトリの作成
register_activation_hook( __FILE__, function() {
  // 初期オプションを設定(既に存在する場合は上書きしない)
  add_option( 'myplugin_version', '1.0.0' );
  add_option( 'myplugin_settings', array(
    'enabled' => true,
    'api_key' => '',
  ) );
  
  // CPTを登録した後にリライトルールをフラッシュ
  myplugin_register_post_types();
  flush_rewrite_rules();
});

register_deactivation_hook( $file, $callback )

目的:プラグイン無効化時に1回だけ実行する処理を登録する

プラグインが無効化されたときに実行されます。一時データのクリア、スケジュールされたcronジョブの解除などに使用します。

重要な注意点:ユーザーデータの削除は無効化時ではなく、アンインストール時に行うのがマナーです。無効化しただけでデータが消えるとユーザーの信頼を損ねます。アンインストール時の処理はuninstall.phpファイル、またはregister_uninstall_hook()で登録します。

register_deactivation_hook( __FILE__, function() {
  // スケジュールされたcronジョブを解除
  wp_clear_scheduled_hook( 'myplugin_daily_cleanup' );
  
  // 一時キャッシュをクリア
  delete_transient( 'myplugin_cache' );
});

3. 設定値の保存|Options API

プラグインの設定値など、サイト全体に関わるデータはOptions APIで保存するのが基本です。Options APIはwp_optionsテーブルにキーと値のペアとしてデータを保存します。

get_option( $option, $default = false )

目的:オプション値を取得する

第1引数にオプション名、第2引数にデフォルト値を指定します。オプションが存在しない場合はデフォルト値が返されます。このデフォルト値の指定は非常に重要で、プラグインの初回動作時や、新しい設定項目を追加した際の後方互換性を確保します。

// シンプルな値の取得
$enabled = get_option( 'myplugin_enabled', '0' );

// 配列として保存された設定の取得
$settings = get_option( 'myplugin_settings', array(
  'enabled' => false,
  'api_key' => '',
  'limit'   => 10,
) );

// 個別の値にアクセス
$api_key = $settings['api_key'];

add_option( $option, $value, $deprecated = ”, $autoload = ‘yes’ )

目的:オプションが存在しない場合のみ作成する

すでに同名のオプションが存在する場合は何もしません。プラグインの初期化時に初期値を設定する場合に適しています。既存の値を上書きしたくない場面で使います。

第4引数の$autoloadは、WordPressの起動時にこのオプションをメモリに読み込むかどうかを制御します。頻繁にアクセスするオプションは'yes'(デフォルト)に、たまにしか使わない大きなデータは'no'にするとパフォーマンスが向上します。

// プラグイン有効化時に初期値を設定
register_activation_hook( __FILE__, function() {
  add_option( 'myplugin_settings', array(
    'enabled' => true,
    'limit'   => 10,
  ) );
});

update_option( $option, $value, $autoload = null )

目的:オプション値を更新する(存在しなければ作成)

add_option()との違いは、既存の値があれば上書きする点です。設定画面からの保存処理では通常こちらを使用します。

// 設定を更新
update_option( 'myplugin_settings', array(
  'enabled' => true,
  'api_key' => 'new_api_key_here',
  'limit'   => 20,
) );

delete_option( $option )

目的:オプションを削除する

プラグインのアンインストール時にデータをクリーンアップする際に使用します。

// アンインストール時にすべての設定を削除
delete_option( 'myplugin_settings' );
delete_option( 'myplugin_version' );

Options API使用時の注意点

大量のデータや頻繁に更新されるデータをオプションに保存すると、データベースへの負荷が高くなります。以下のようなケースでは別の方法を検討してください:

  • 一時的なキャッシュデータ → Transients API(後述)を使用
  • 投稿ごとのデータ → Post Meta(カスタムフィールド)を使用
  • ユーザーごとのデータ → User Meta を使用
  • 大量の構造化データ → 独自テーブルを検討

4. 設定画面を正攻法で作る|Settings API

設定画面を作る際、$_POSTを直接受け取ってupdate_option()で保存するコードを書くこともできます。しかし、長期運用を考えるならSettings APIを使うのが堅実です。

Settings APIを使うメリットは以下の通りです:

  • nonce検証が自動で行われる(CSRF対策)
  • サニタイズコールバックで入力値を整形できる
  • エラーメッセージの表示が標準化される
  • WordPressの設定画面と統一されたUIが実現できる
  • 権限チェックが自動で行われる

register_setting( $option_group, $option_name, $args )

目的:設定項目を登録する

Settings APIの核となる関数です。$option_groupは設定グループの識別子、$option_nameは保存されるオプション名です。$args配列で型やサニタイズコールバック、デフォルト値を指定できます。

sanitize_callbackは非常に重要で、ユーザーが入力したすべての値がこのコールバックを通過してからデータベースに保存されます。不正な値の除去、型の変換、期待しない入力の無害化をここで行います。

add_action( 'admin_init', function() {
  register_setting( 'myplugin_group', 'myplugin_settings', array(
    'type'              => 'array',
    'sanitize_callback' => function( $input ) {
      // 入力値をサニタイズして返す
      return array(
        'enabled' => isset( $input['enabled'] ) ? '1' : '0',
        'api_key' => sanitize_text_field( $input['api_key'] ?? '' ),
        'limit'   => absint( $input['limit'] ?? 10 ),
      );
    },
    'default' => array(
      'enabled' => '0',
      'api_key' => '',
      'limit'   => 10,
    ),
  ) );
});

add_settings_section( $id, $title, $callback, $page )

目的:設定画面にセクション(区切り)を追加する

設定項目が多い場合、セクションで区切ると見やすくなります。コールバック関数でセクションの説明文を出力できます。

add_action( 'admin_init', function() {
  add_settings_section(
    'myplugin_main_section',      // セクションID
    '基本設定',                    // セクションタイトル
    function() {                  // 説明文を出力するコールバック
      echo '<p>プラグインの基本動作を設定します。</p>';
    },
    'myplugin'                    // ページスラッグ
  );
});

add_settings_field( $id, $title, $callback, $page, $section, $args )

目的:設定フィールドを追加する

個々の設定項目(チェックボックス、テキスト入力など)を登録します。コールバック関数でフォーム要素のHTMLを出力します。

add_action( 'admin_init', function() {
  add_settings_field(
    'myplugin_enabled',                      // フィールドID
    'プラグインを有効化',                      // ラベル
    function() {                             // 入力フォームを出力
      $options = get_option( 'myplugin_settings', array() );
      $checked = ! empty( $options['enabled'] ) ? 'checked' : '';
      echo '<label>';
      echo '<input type="checkbox" name="myplugin_settings[enabled]" ' . $checked . '>';
      echo ' 有効にする';
      echo '</label>';
    },
    'myplugin',                              // ページスラッグ
    'myplugin_main_section'                  // セクションID
  );
  
  add_settings_field(
    'myplugin_api_key',
    'APIキー',
    function() {
      $options = get_option( 'myplugin_settings', array() );
      $value = esc_attr( $options['api_key'] ?? '' );
      echo '<input type="text" name="myplugin_settings[api_key]" value="' . $value . '" class="regular-text">';
      echo '<p class="description">外部サービスのAPIキーを入力してください。</p>';
    },
    'myplugin',
    'myplugin_main_section'
  );
});

settings_fields() / do_settings_sections() / submit_button()

目的:設定フォームを出力する

管理画面の設定ページで、これらの関数を使ってフォームを組み立てます。settings_fields()はnonceフィールドとhiddenフィールドを出力し、do_settings_sections()は登録したセクションとフィールドをすべて出力します。

<div class="wrap">
  <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
  
  <form method="post" action="options.php">
    <?php
      settings_fields( 'myplugin_group' );  // nonceとhidden出力
      do_settings_sections( 'myplugin' );   // セクションとフィールド出力
      submit_button();                      // 送信ボタン出力
    ?>
  </form>
</div>

formのactionoptions.phpであることに注目してください。Settings APIでは、WordPressのoptions.phpが保存処理を引き受けます。nonce検証、権限チェック、サニタイズコールバックの実行をすべて自動で行ってくれます。

5. 管理メニュー|add_menu_page / add_submenu_page

プラグインの設定画面にアクセスするためのメニュー項目を、管理画面の左サイドバーに追加します。

add_menu_page()

目的:管理画面にトップレベルメニューを追加する

独立したメニュー項目を追加します。多機能なプラグインで、専用のメニュー領域が必要な場合に使用します。

add_action( 'admin_menu', function() {
  add_menu_page(
    'My Plugin Settings',        // ページタイトル(<title>に表示)
    'My Plugin',                 // メニュータイトル(サイドバーに表示)
    'manage_options',            // 必要な権限
    'myplugin',                  // メニュースラッグ(URL識別子)
    'myplugin_render_page',      // コンテンツを出力する関数
    'dashicons-admin-generic',   // アイコン(Dashicons名またはURL)
    80                           // メニューの位置
  );
});

function myplugin_render_page() {
  // 権限チェック(二重チェックとして推奨)
  if ( ! current_user_can( 'manage_options' ) ) {
    return;
  }
  
  echo '<div class="wrap">';
  echo '<h1>' . esc_html( get_admin_page_title() ) . '</h1>';
  // 設定フォームなどを出力
  echo '</div>';
}

add_submenu_page()

目的:既存メニューの下にサブメニューを追加する

設定が複数カテゴリに分かれる場合や、既存のWordPressメニュー(「設定」など)の下に追加したい場合に使用します。

add_action( 'admin_menu', function() {
  // 「設定」メニューの下に追加
  add_submenu_page(
    'options-general.php',       // 親メニューのスラッグ
    'My Plugin Settings',        // ページタイトル
    'My Plugin',                 // メニュータイトル
    'manage_options',            // 必要な権限
    'myplugin-settings',         // メニュースラッグ
    'myplugin_render_settings'   // コンテンツ出力関数
  );
  
  // 自分のトップレベルメニューの下にサブメニューを追加
  add_submenu_page(
    'myplugin',                  // 親メニューのスラッグ
    '詳細設定',                   // ページタイトル
    '詳細設定',                   // メニュータイトル
    'manage_options',
    'myplugin-advanced',
    'myplugin_render_advanced'
  );
});

メニュー位置の目安

add_menu_page()の最後の引数でメニューの表示位置を指定できます:

  • 2 – ダッシュボードの下
  • 20 – 固定ページの下
  • 25 – コメントの下
  • 60 – 「外観」の下
  • 65 – 「プラグイン」の下
  • 70 – 「ユーザー」の下
  • 80 – 「設定」の下

6. 投稿編集画面の拡張|メタボックスとカスタムフィールド

投稿ごとに保存したいデータは、オプションではなくPost Meta(カスタムフィールド)を使用します。投稿編集画面にメタボックスを追加することで、直感的に入力・編集できるUIを提供できます。

add_meta_box()

目的:投稿編集画面にメタボックス(入力エリア)を追加する

メタボックスは投稿編集画面に配置される入力UIです。カスタムフィールドの入力、関連設定の表示など、様々な用途に使用されます。

add_action( 'add_meta_boxes', function() {
  add_meta_box(
    'myplugin_meta_box',        // メタボックスID
    '追加情報',                   // タイトル
    'myplugin_meta_box_html',   // コンテンツ出力関数
    'post',                      // 表示する投稿タイプ(配列で複数指定可)
    'side',                      // 配置位置:'normal', 'side', 'advanced'
    'high'                       // 優先度:'high', 'low', 'default'
  );
});

function myplugin_meta_box_html( $post ) {
  // 現在の値を取得
  $value = get_post_meta( $post->ID, '_myplugin_custom_field', true );
  
  // nonce フィールドを出力(CSRF対策)
  wp_nonce_field( 'myplugin_meta_box', 'myplugin_meta_box_nonce' );
  
  // 入力フォームを出力
  echo '<label for="myplugin_custom_field">カスタム値:</label>';
  echo '<input type="text" id="myplugin_custom_field" ';
  echo 'name="myplugin_custom_field" value="' . esc_attr( $value ) . '" ';
  echo 'style="width:100%;">';
}

メタボックスの保存処理

メタボックスで入力した値を保存するには、save_postアクションフックを使用します。この処理ではセキュリティチェックが非常に重要です。

add_action( 'save_post', function( $post_id ) {
  // 1. 自動保存時は処理しない
  if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
    return;
  }
  
  // 2. nonce検証
  if ( ! isset( $_POST['myplugin_meta_box_nonce'] ) ) {
    return;
  }
  if ( ! wp_verify_nonce( $_POST['myplugin_meta_box_nonce'], 'myplugin_meta_box' ) ) {
    return;
  }
  
  // 3. 権限チェック
  if ( ! current_user_can( 'edit_post', $post_id ) ) {
    return;
  }
  
  // 4. サニタイズして保存
  if ( isset( $_POST['myplugin_custom_field'] ) ) {
    $value = sanitize_text_field( $_POST['myplugin_custom_field'] );
    update_post_meta( $post_id, '_myplugin_custom_field', $value );
  }
});

この「nonce検証 → 権限チェック → サニタイズ → 保存」という流れは、WordPress開発における保存処理の黄金パターンです。必ず守るようにしてください。

Post Meta関連関数

  • get_post_meta( $post_id, $key, $single ) – メタ値を取得
  • update_post_meta( $post_id, $key, $value ) – メタ値を更新(なければ作成)
  • delete_post_meta( $post_id, $key ) – メタ値を削除
  • add_post_meta( $post_id, $key, $value, $unique ) – メタ値を追加

キー名の先頭にアンダースコア(_)を付けると、標準のカスタムフィールドUIに表示されなくなります。プラグイン専用のフィールドには_myplugin_のようなプレフィックスを付けることで、他のプラグインとの衝突も防げます。

7. カスタム投稿タイプとタクソノミー

独自のコンテンツタイプが必要な場合、カスタム投稿タイプ(CPT)を登録します。テーマではなくプラグインで登録することで、テーマを変更してもデータ構造が維持されます。

register_post_type()

目的:カスタム投稿タイプを登録する

add_action( 'init', function() {
  register_post_type( 'book', array(
    'label'           => '書籍',
    'labels'          => array(
      'name'          => '書籍',
      'singular_name' => '書籍',
      'add_new'       => '新規追加',
      'add_new_item'  => '新規書籍を追加',
      'edit_item'     => '書籍を編集',
    ),
    'public'          => true,
    'has_archive'     => true,
    'supports'        => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
    'show_in_rest'    => true, // ブロックエディタとREST API対応
    'menu_icon'       => 'dashicons-book',
  ) );
});

register_taxonomy()

目的:カスタムタクソノミーを登録する

add_action( 'init', function() {
  register_taxonomy( 'genre', 'book', array(
    'label'             => 'ジャンル',
    'hierarchical'      => true, // trueでカテゴリ形式、falseでタグ形式
    'show_in_rest'      => true,
    'show_admin_column' => true, // 一覧にカラム表示
  ) );
});

rewriteルールのフラッシュ

CPTを登録するとパーマリンク構造が変わりますが、リライトルールのキャッシュがあるため、そのままでは404エラーになることがあります。プラグイン有効化時にflush_rewrite_rules()を実行してください。

register_activation_hook( __FILE__, function() {
  // 先にCPTを登録
  myplugin_register_post_types();
  
  // その後でリライトルールをフラッシュ
  flush_rewrite_rules();
});

注意:flush_rewrite_rules()は重い処理なので、毎リクエストで実行してはいけません。有効化時の1回だけにしてください。

8. 画面別のCSS/JS読み込み|管理画面でのenqueue

管理画面用のCSS/JSは、すべてのページで読み込むのではなく、必要なページだけで読み込むのがベストプラクティスです。

admin_enqueue_scripts + $hook_suffix

目的:特定の管理画面ページでのみCSS/JSを読み込む

admin_enqueue_scriptsアクションのコールバックには、現在の管理画面ページを示す$hook_suffixが渡されます。これを使って条件分岐することで、不要なページへの読み込みを防げます。

add_action( 'admin_enqueue_scripts', function( $hook_suffix ) {
  // 自分のプラグイン設定ページでのみ読み込む
  if ( $hook_suffix !== 'toplevel_page_myplugin' ) {
    return;
  }
  
  wp_enqueue_style(
    'myplugin-admin',
    plugin_dir_url( __FILE__ ) . 'assets/css/admin.css',
    array(),
    '1.0.0'
  );
  
  wp_enqueue_script(
    'myplugin-admin',
    plugin_dir_url( __FILE__ ) . 'assets/js/admin.js',
    array( 'jquery' ),
    '1.0.0',
    true
  );
});

$hook_suffixの値は、toplevel_page_{menu_slug}{parent_slug}_page_{menu_slug}のような形式になります。不明な場合はvar_dump( $hook_suffix )で確認してください。

9. 権限とロール|capability設計

WordPressでは、ユーザーの権限は「ロール」と「ケイパビリティ(capability)」で管理されます。プラグインの設定画面や機能へのアクセスを制御するには、適切な権限チェックが必要です。

current_user_can( $capability )

目的:現在のユーザーが特定の権限を持っているかチェックする

管理画面の表示時、保存処理時、Ajax処理時など、重要な処理の前では必ず権限チェックを行います。

// 設定画面のレンダリング
function myplugin_render_page() {
  if ( ! current_user_can( 'manage_options' ) ) {
    wp_die( 'この操作を行う権限がありません。' );
  }
  // 設定画面を出力
}

よく使う権限(capability)

  • manage_options – サイト設定の管理(管理者向け)
  • edit_posts – 投稿の編集(編集者以上)
  • edit_post – 特定の投稿の編集(第2引数で投稿IDを指定)
  • upload_files – ファイルのアップロード
  • edit_users – ユーザーの編集

「誰がこの機能を使えるべきか?」を最初に決めておくと、UIもコードも一貫性が保てます。

10. admin_post_|フォーム送信の受け口

Settings APIでは実現しにくい複雑な処理(ファイルのダウンロード、一括処理、ウィザード形式など)には、admin_post_{action}フックが便利です。

admin_post_{action} / admin_post_nopriv_{action}

目的:フォーム送信(POST)を受け取る専用エンドポイントを作成する

// フォーム側
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
  <?php wp_nonce_field( 'myplugin_export', 'myplugin_nonce' ); ?>
  <input type="hidden" name="action" value="myplugin_export">
  <button type="submit" class="button button-primary">エクスポート</button>
</form>
// 受け口
add_action( 'admin_post_myplugin_export', function() {
  // 権限チェック
  if ( ! current_user_can( 'manage_options' ) ) {
    wp_die( 'Permission denied' );
  }
  
  // nonce検証
  check_admin_referer( 'myplugin_export', 'myplugin_nonce' );
  
  // 処理を実行(例:CSV生成など)
  // ...
  
  // 処理完了後はリダイレクト(二重送信防止)
  wp_safe_redirect( add_query_arg(
    array( 'page' => 'myplugin', 'action' => 'exported' ),
    admin_url( 'admin.php' )
  ) );
  exit;
});

11. WP_List_Table|管理画面の一覧テーブル

管理画面で「投稿一覧」のようなテーブルUIを作りたい場合、WP_List_Tableクラスを継承して使用します。

注意:WP_List_TableはWordPress内部クラスであり、公式にはパブリックAPIとして保証されていません。しかし実務では広く使用されており、WordPressの管理画面と統一されたUIを提供できるため、現実的な選択肢として認知されています。

if ( ! class_exists( 'WP_List_Table' ) ) {
  require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}

class MyPlugin_List_Table extends WP_List_Table {
  
  public function get_columns() {
    return array(
      'cb'    => '<input type="checkbox" />',
      'title' => 'タイトル',
      'date'  => '日付',
    );
  }
  
  protected function column_cb( $item ) {
    return sprintf(
      '<input type="checkbox" name="ids[]" value="%d" />',
      absint( $item['id'] )
    );
  }
  
  protected function column_title( $item ) {
    $actions = array(
      'edit'   => sprintf( '<a href="%s">編集</a>', esc_url( $item['edit_url'] ) ),
      'delete' => sprintf( '<a href="%s">削除</a>', esc_url( $item['delete_url'] ) ),
    );
    return sprintf( '%s %s', esc_html( $item['title'] ), $this->row_actions( $actions ) );
  }
  
  protected function column_default( $item, $column_name ) {
    return esc_html( $item[ $column_name ] ?? '' );
  }
  
  public function prepare_items() {
    $per_page = 20;
    $page = max( 1, absint( $_GET['paged'] ?? 1 ) );
    
    // データを取得(独自のデータ取得関数を呼び出す)
    $data = myplugin_get_items( $page, $per_page );
    $total = myplugin_count_items();
    
    $this->items = $data;
    $this->set_pagination_args( array(
      'total_items' => $total,
      'per_page'    => $per_page,
      'total_pages' => ceil( $total / $per_page ),
    ) );
  }
}

12. 独自アップデート機能|プラグインの自動更新

WordPress.org以外で配布するプラグインに更新機能を実装したい場合、更新情報のトランジェントをフィルタリングします。

pre_set_site_transient_update_plugins

目的:プラグイン更新チェック結果に独自情報を追加する

add_filter( 'pre_set_site_transient_update_plugins', function( $transient ) {
  if ( empty( $transient->checked ) ) {
    return $transient;
  }
  
  $plugin_file = 'myplugin/myplugin.php';
  $current = $transient->checked[ $plugin_file ] ?? null;
  
  if ( ! $current ) {
    return $transient;
  }
  
  // 自前サーバーから最新バージョン情報を取得
  $response = wp_remote_get( 'https://example.com/api/update-check.json' );
  if ( is_wp_error( $response ) ) {
    return $transient;
  }
  
  $info = json_decode( wp_remote_retrieve_body( $response ), true );
  
  // 新しいバージョンがあれば更新情報を追加
  if ( $info && version_compare( $info['version'], $current, '>' ) ) {
    $transient->response[ $plugin_file ] = (object) array(
      'slug'        => 'myplugin',
      'plugin'      => $plugin_file,
      'new_version' => $info['version'],
      'package'     => $info['download_url'],
      'tested'      => $info['tested'] ?? '',
    );
  }
  
  return $transient;
});

13. $wpdb深掘り|安全なSQL・テーブル設計

Options APIやMeta APIで対応できない場合(大量データ、高速検索、複雑なクエリ)、独自テーブルと$wpdbを使用します。

$wpdb->prepare() – SQLインジェクション対策の基本

ユーザー入力を含むSQLは、必ずprepare()を使用してください。プレースホルダを使うことで、SQLインジェクション攻撃を防ぎます。

global $wpdb;

$sql = $wpdb->prepare(
  "SELECT * FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = %s",
  $post_id,
  'my_key'
);

$results = $wpdb->get_results( $sql, ARRAY_A );

プレースホルダの種類

  • %d – 整数(decimal)
  • %s – 文字列(string)
  • %f – 浮動小数点数(float)

独自テーブルの作成

プラグイン有効化時にdbDelta()を使用してテーブルを作成します。文字コードと照合順序を既存テーブルと揃えることが重要です。

register_activation_hook( __FILE__, function() {
  global $wpdb;
  
  $table = $wpdb->prefix . 'myplugin_items';
  $charset_collate = $wpdb->get_charset_collate();
  
  $sql = "CREATE TABLE {$table} (
    id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
    user_id BIGINT(20) UNSIGNED NOT NULL,
    title VARCHAR(255) NOT NULL,
    created_at DATETIME NOT NULL,
    PRIMARY KEY  (id),
    KEY user_id (user_id)
  ) {$charset_collate};";
  
  require_once ABSPATH . 'wp-admin/includes/upgrade.php';
  dbDelta( $sql );
});

まとめ

プラグイン開発において、設定画面の作り方は非常に重要です。この記事で解説した内容を振り返ると:

  • フック設計:「どのフックで何をするか」を最初に決める
  • Options API:サイト全体の設定値を保存する基本的な方法
  • Settings API:nonce・サニタイズ・権限を自動で処理してくれる正攻法
  • メタボックス:投稿ごとのデータはPost Metaで管理
  • セキュリティ:権限チェック → nonce検証 → サニタイズ の黄金パターン

次回のPart3では、Ajax/REST API、外部API連携、キャッシュ戦略、セキュリティの詳細について解説します。

コメント

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