2026年5月20日

2026年5月20日

WordPressプラグインでウィジェットを作成する方法(WP_Widgetの継承・登録・WP-CLIでの確認)

WordPress のウィジェットは、サイドバーやフッターなどの「ウィジェットエリア」に置く部品です。プラグインから自作のウィジェットを追加するには、WP_Widget クラスを継承したクラスを作り、widgets_init アクションで register_widget() に渡します。

この記事のコードは、2026年11月時点の WordPress 7.1 と PHP 8.3 で、有効化・ウィジェットの追加・表示まで動作を確かめています。

ウィジェットを使える場面

自作ウィジェットが使えるのは、ウィジェットエリア(サイドバー)を登録しているテーマです。

  • クラシックテーマ(Twenty Twenty-One など): 「外観」→「ウィジェット」から追加します。WordPress 5.8 以降、この画面はブロックで編集する形(ブロックウィジェットエディター)になりましたが、WP_Widget で作ったウィジェットもそのまま追加できます。
  • ブロックテーマ(Twenty Twenty-Five など): ウィジェットエリアが無いため、「外観」に「ウィジェット」が表示されません。サイトエディターで使える部品を作りたい場合は、ウィジェットではなくブロックとして作ります。

ウィジェットのプラグインを作る

wp-content/plugins/my-recent-posts-widget/ フォルダーを作り、その中に my-recent-posts-widget.php として次の内容を保存します。最近の投稿を指定した件数だけ表示するウィジェットです。

<?php
/**
 * Plugin Name: My Recent Posts Widget
 * Description: 最近の投稿を表示するウィジェットのサンプル
 * Version: 1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Myplugin_Recent_Posts_Widget extends WP_Widget {

	public function __construct() {
		parent::__construct(
			'myplugin_recent_posts',        // ID のもとになる名前(id_base)
			'MY: 最近の投稿',                // 管理画面に表示する名前
			array(
				'description'           => '最近の投稿を一覧で表示します',
				'classname'             => 'myplugin-recent-posts',
				'show_instance_in_rest' => true,
			)
		);
	}

	// サイトに表示する内容
	public function widget( $args, $instance ) {
		$title = apply_filters( 'widget_title', $instance['title'] ?? '', $instance, $this->id_base );
		$count = isset( $instance['count'] ) ? absint( $instance['count'] ) : 5;

		$posts = get_posts(
			array(
				'numberposts' => $count,
				'post_status' => 'publish',
			)
		);
		if ( ! $posts ) {
			return;
		}

		echo $args['before_widget'];

		if ( $title ) {
			echo $args['before_title'] . esc_html( $title ) . $args['after_title'];
		}

		echo '<ul class="myplugin-recent-list">';
		foreach ( $posts as $post ) {
			printf(
				'<li><a href="%s">%s</a></li>',
				esc_url( get_permalink( $post ) ),
				esc_html( get_the_title( $post ) )
			);
		}
		echo '</ul>';

		echo $args['after_widget'];
	}

	// 管理画面の設定フォーム
	public function form( $instance ) {
		$title = $instance['title'] ?? '最近の投稿';
		$count = isset( $instance['count'] ) ? absint( $instance['count'] ) : 5;
		?>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">タイトル:</label>
			<input type="text" class="widefat"
				id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
				name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
				value="<?php echo esc_attr( $title ); ?>">
		</p>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'count' ) ); ?>">表示件数:</label>
			<input type="number" class="tiny-text" min="1" max="20"
				id="<?php echo esc_attr( $this->get_field_id( 'count' ) ); ?>"
				name="<?php echo esc_attr( $this->get_field_name( 'count' ) ); ?>"
				value="<?php echo esc_attr( $count ); ?>">
		</p>
		<?php
	}

	// 保存する値の整形
	public function update( $new_instance, $old_instance ) {
		$count = absint( $new_instance['count'] ?? 5 );

		return array(
			'title' => sanitize_text_field( $new_instance['title'] ?? '' ),
			'count' => min( max( $count, 1 ), 20 ),
		);
	}
}

add_action(
	'widgets_init',
	function () {
		register_widget( 'Myplugin_Recent_Posts_Widget' );
	}
);

保存したら、「プラグイン」→「インストール済みプラグイン」で「My Recent Posts Widget」を有効化します。クラシックテーマなら、「外観」→「ウィジェット」でウィジェットエリアに「従来のウィジェット」ブロックを追加し、ウィジェットの一覧から「MY: 最近の投稿」を選びます。

4つのメソッドの役割

メソッド 役割
__construct() ID のもとになる名前(id_base)、表示名、説明などを parent::__construct() に渡す
widget( $args, $instance ) サイトに出力する HTML。$args にはウィジェットエリア側で決めた前後のタグ、$instance には保存された設定が入る
form( $instance ) 管理画面に出す設定フォーム。get_field_id() と get_field_name() で項目の id と name を作る
update( $new_instance, $old_instance ) 保存前に入力値を整形して返す。返した配列がそのまま保存される

引数に型を付けない

WP_Widget の widget()・form()・update() は、引数に型の指定がありません。子クラスで widget( array $args, array $instance ) のように型を付けると、PHP 8 では親クラスのメソッドと互換がないと判断され、プラグインを読み込んだ時点で次のような致命的エラーになります。

PHP Fatal error:  Declaration of Bad_Widget::widget(array $args, array $instance): void must be compatible with WP_Widget::widget($args, $instance)

子クラスの引数は、親クラスと同じく型なしで書いてください。

出力のエスケープ

widget() では、タイトルは esc_html()、URL は esc_url() で出力しています。$args['before_widget'] などはテーマ(ウィジェットエリアの登録側)が用意したHTMLなので、そのまま出力します。

show_instance_in_rest を true にすると、ウィジェットの設定値が REST API(ブロックウィジェットエディターが使う)にそのままの形で出ます。WordPress 標準のウィジェットも true にしています。パスワードなど公開したくない値を設定に持つウィジェットでは true にしないでください。

ウィジェットエリアを追加する

使っているテーマにウィジェットエリアが無い場合や、プラグイン用の場所を増やしたい場合は、register_sidebar() で登録します。ブロックテーマでもこれを登録すると「外観」→「ウィジェット」が表示されますが、テンプレートにウィジェットエリアを出力する仕組みが無いため、サイトには表示されません。

<?php
add_action( 'widgets_init', function () {
	register_sidebar(
		array(
			'id'            => 'myplugin-sidebar',
			'name'          => 'プラグイン用サイドバー',
			'description'   => 'プラグインが追加するウィジェットエリア',
			'before_widget' => '<section id="%1$s" class="widget %2$s">',
			'after_widget'  => '</section>',
			'before_title'  => '<h2 class="widget-title">',
			'after_title'   => '</h2>',
		)
	);
} );

クラシックテーマのテンプレートファイル(sidebar.php など)で、ウィジェットが置かれているときだけ出力します。

<?php if ( is_active_sidebar( 'myplugin-sidebar' ) ) : ?>
	<aside class="myplugin-sidebar">
		<?php dynamic_sidebar( 'myplugin-sidebar' ); ?>
	</aside>
<?php endif; ?>

before_widget の %1$s にはウィジェットのID(例: myplugin_recent_posts-2)、%2$s には classname に指定したクラス名が入ります。

WP-CLIでウィジェットを追加・確認する

WP-CLI の wp widget コマンドで、ウィジェットエリアへの追加・変更・削除ができます。ウィジェットの種類は id_base(この例では myplugin_recent_posts)で指定し、設定の項目は --項目名=値 で渡します。

# ウィジェットエリアの一覧
wp sidebar list --fields=name,id

# 1番目の位置に追加
wp widget add myplugin_recent_posts myplugin-sidebar 1 --title="新着記事" --count=3

# 置かれているウィジェットと設定を確認
wp widget list myplugin-sidebar --fields=name,id,position,options

# 設定を変更(指定しなかった項目は update() の既定値に戻るので、すべて指定する)
wp widget update myplugin_recent_posts-1 --title="最新記事" --count=3

# 削除
wp widget delete myplugin_recent_posts-1

試したところ、wp widget update で --title だけを渡すと、count は update() の既定値の 5 に戻りました。変更するときは、残したい項目も含めてすべて指定してください。

旧来のウィジェット画面に戻す場合

ブロックウィジェットエディターをやめ、WordPress 5.7 までのウィジェット画面に戻すには、use_widgets_block_editor フィルターで false を返します。

<?php
add_filter( 'use_widgets_block_editor', '__return_false' );

公式の「Classic Widgets」プラグインを有効にしても同じ結果になります。

関連記事

お気軽にご相談ください

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