2026年9月17日

2026年9月17日

WordPressのプラグイン開発入門:最小構成のボイラープレートを自作して理解する

プラグイン開発は、ヘッダーコメントを書いた PHP ファイル1つから始められます。ただ、有効化・無効化・削除時の後片付け、設定画面のセキュリティ、ファイルの分け方を最初に決めておかないと、機能が増えたときに手が付けられなくなります。この記事では、既存のボイラープレート(ひな形)の選び方を整理したうえで、小さなボイラープレートを自作し、各ファイルが何のためにあるかを説明します。

ボイラープレートを使うか、自作するか

ひな形を一から書く前に、既存の選択肢を知っておくと判断が早くなります(2026年10月現在)。

  • **WP-CLI の wp scaffold plugin**:コマンド1つでプラグインのひな形を生成します。テスト実行用の設定ファイルも作られるため、自動テストを入れたい場合の出発点になります。生成される内容は WP-CLI のバージョンで変わるので、出力を見てから不要なものを消してください。
  • **@wordpress/create-block**:ブロックエディター用のブロックを作るなら、こちらが公式の出発点です。npx @wordpress/create-block でビルド環境ごと用意されます。
  • WordPress Plugin Boilerplate(GitHub で公開されている有名なひな形):クラス分割の考え方を学ぶ教材としては参考になりますが、更新頻度は高くありません。現在の PHP や WordPress の慣習に合わない部分がないか確認してから使ってください。

既存のひな形は多機能なぶん、入門段階では「どのファイルが何をしているか分からないまま」になりがちです。最初の1本は、以下のような最小構成を自作して仕組みをつかみ、規模が大きくなってから既存のひな形やオートロードの導入を検討する、という順番をおすすめします。

開発は本番サイトではなく、ローカル環境かステージング環境で行ってください。致命的なエラーで管理画面に入れなくなった場合でも、FTP やファイルマネージャーで wp-content/plugins/ のフォルダ名を変えれば、そのプラグインは無効になります。

ファイル構成

my-sample-plugin/
├── my-sample-plugin.php      … メインファイル(ヘッダーと読み込み)
├── uninstall.php             … 削除時の後片付け
├── includes/
│   ├── class-msp-plugin.php  … 有効化・無効化・フロント側の処理
│   └── class-msp-admin.php   … 管理画面の設定ページ
├── assets/css/front.css
└── languages/

フォルダ名とメインファイル名、後述のテキストドメインはそろえておくと、翻訳や配布のときに混乱しません。関数名・クラス名・オプション名には msp_ のような固有の接頭辞を付け、他のプラグインとの名前の衝突を防ぎます。PHP の名前空間を使う方法もあります。

メインファイル:ヘッダーと読み込みだけを書く

<?php
/**
 * Plugin Name:       My Sample Plugin
 * Description:       自作プラグインのひな形です。
 * Version:           0.1.0
 * Requires at least: 6.5
 * Requires PHP:      7.4
 * Author:            Your Name
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       my-sample-plugin
 * Domain Path:       /languages
 */

defined( 'ABSPATH' ) || exit;

define( 'MSP_VERSION', '0.1.0' );
define( 'MSP_DIR', plugin_dir_path( __FILE__ ) );
define( 'MSP_URL', plugin_dir_url( __FILE__ ) );

require_once MSP_DIR . 'includes/class-msp-plugin.php';
require_once MSP_DIR . 'includes/class-msp-admin.php';

register_activation_hook( __FILE__, array( 'MSP_Plugin', 'activate' ) );
register_deactivation_hook( __FILE__, array( 'MSP_Plugin', 'deactivate' ) );

add_action( 'plugins_loaded', array( 'MSP_Plugin', 'init' ) );

WordPress はこのヘッダーコメントを読んでプラグイン一覧に表示します。Plugin Name だけでも認識されますが、Requires at least と Requires PHP を書いておくと、条件を満たさない環境での有効化を WordPress が止めてくれます。他のプラグインに依存する場合は、WordPress 6.5 で追加された Requires Plugins ヘッダーで依存先のスラッグを指定できます。

defined( 'ABSPATH' ) || exit; は、ファイルの URL を直接開かれたときに処理を止めるための定型です。includes/ 内のファイルにも同じ1行を入れます。

register_activation_hook() は、メインファイルの中で __FILE__ を渡して呼ぶ必要があります。別ファイルから呼ぶと、渡すパスがずれて動きません。また、有効化フックはプラグインの更新時には実行されません。データベースの構造変更などを更新時に行いたい場合は、保存しておいたバージョン番号と MSP_VERSION を比べて処理する仕組みを別に用意します。

本体クラス:有効化・無効化と読み込み

<?php
defined( 'ABSPATH' ) || exit;

class MSP_Plugin {

	public static function init() {
		if ( is_admin() ) {
			MSP_Admin::init();
		}
		add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
	}

	public static function activate() {
		// 既に値があれば上書きしない
		add_option( 'msp_settings', array( 'message' => '' ) );
	}

	public static function deactivate() {
		// 予約したcronイベントがあれば解除する(データは消さない)
		wp_clear_scheduled_hook( 'msp_daily_event' );
	}

	public static function enqueue_assets() {
		wp_enqueue_style( 'msp-front', MSP_URL . 'assets/css/front.css', array(), MSP_VERSION );
	}
}

無効化と削除は役割が違います。無効化は一時的に止めるだけなので、予約処理の解除などにとどめ、設定は残します。設定やテーブルを消すのは削除(アンインストール)のときです。ここを混同すると、「一度止めただけで設定が消えた」という不満につながります。

CSS や JavaScript は <head> に直接タグを書かず、wp_enqueue_style() / wp_enqueue_script() で読み込みます。第4引数にバージョンを渡しておくと、更新時にブラウザのキャッシュが切り替わります。

設定画面:Settings API で安全に保存する

<?php
defined( 'ABSPATH' ) || exit;

class MSP_Admin {

	public static function init() {
		add_action( 'admin_menu', array( __CLASS__, 'add_menu' ) );
		add_action( 'admin_init', array( __CLASS__, 'register_settings' ) );
	}

	public static function add_menu() {
		add_options_page( 'My Sample Plugin', 'My Sample Plugin', 'manage_options', 'my-sample-plugin', array( __CLASS__, 'render_page' ) );
	}

	public static function register_settings() {
		register_setting(
			'msp_settings_group',
			'msp_settings',
			array(
				'type'              => 'array',
				'sanitize_callback' => array( __CLASS__, 'sanitize' ),
				'default'           => array( 'message' => '' ),
			)
		);
		add_settings_section( 'msp_main', '基本設定', '__return_false', 'my-sample-plugin' );
		add_settings_field( 'msp_message', '表示メッセージ', array( __CLASS__, 'field_message' ), 'my-sample-plugin', 'msp_main' );
	}

	public static function sanitize( $input ) {
		return array(
			'message' => isset( $input['message'] ) ? sanitize_text_field( $input['message'] ) : '',
		);
	}

	public static function field_message() {
		$options = get_option( 'msp_settings', array() );
		$value   = isset( $options['message'] ) ? $options['message'] : '';
		printf( '<input type="text" name="msp_settings[message]" value="%s" class="regular-text">', esc_attr( $value ) );
	}

	public static function render_page() {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		?>
		<div class="wrap">
			<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
			<form action="options.php" method="post">
				<?php
				settings_fields( 'msp_settings_group' );
				do_settings_sections( 'my-sample-plugin' );
				submit_button();
				?>
			</form>
		</div>
		<?php
	}
}

自前で $_POST を受け取って update_option() する設定画面も書けますが、nonce の検証や権限確認を自分で漏れなく書く必要があります。Settings API を使うと、settings_fields() が nonce を含む隠しフィールドを出力し、保存先の options.php 側で nonce と権限の確認が行われます。開発者側の責任として残るのは次の3点です。

  • 入力のサニタイズ:sanitize_callback で型と中身を整えます。テキストなら sanitize_text_field()、数値なら absint()、URL なら esc_url_raw() が基本です。
  • 出力のエスケープ:画面に出す値は esc_attr()・esc_html() で囲みます。
  • ページを開く権限の確認:current_user_can() で、メニュー登録時と同じ権限を確かめます。

uninstall.php:削除時の後片付け

<?php
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
	exit;
}

delete_option( 'msp_settings' );

管理画面からプラグインを削除したときに実行されます。WP_UNINSTALL_PLUGIN の確認は、このファイルを URL で直接開かれても何も消さないための安全装置です。独自テーブルや投稿メタを作った場合もここで消しますが、利用者が後で再インストールする可能性を考え、何を消すかは README などに明記しておくと親切です。マルチサイトでは各サイトのオプションが別々に保存されるため、対応する場合はサイトごとに削除する処理が要ります。

翻訳の読み込みで注意すること

WordPress.org で配布するプラグインは、翻訳ファイルが自動で読み込まれる仕組みがあります。同梱の翻訳を使う場合は load_plugin_textdomain() を使いますが、WordPress 6.7 以降は翻訳を早すぎるタイミングで読み込むと開発時に通知が出るようになりました。__() などの翻訳関数は init 以降で呼ぶようにしておくと安全です。細かな挙動はバージョンで変わることがあるため、開発者向けのリリースノートで確認してください。

まとめ

  • 最初の1本は最小構成を自作して仕組みを理解し、規模が大きくなったら wp scaffold plugin や既存のひな形を検討する。ブロック開発なら @wordpress/create-block。
  • メインファイルはヘッダーと読み込みだけにし、ABSPATH チェックと固有の接頭辞で安全性と衝突回避を確保する。
  • 有効化フックは更新時に動かない。無効化ではデータを残し、消すのは uninstall.php で。
  • 設定画面は Settings API を使い、サニタイズ・エスケープ・権限確認を自分の責任として書く。
  • 開発はローカルかステージングで行い、困ったときはプラグインのフォルダ名を変えて無効化できることを覚えておく。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲