WordPressのウィジェットタイプのプラグインの作り方を調べてみました
ウィジェットの作り方の記事は、検索すればたくさん見つかりますが、一番参考になるのは WordPress のデフォルトウィジェットです
WordPress の /wp-includes フォルダ下の default-widgets.php に、13個のウイジェットのソースコードがあるのでこれが参考になります
- Pages widget class
- Links widget class
- Search widget class
- Archives widget class
- Meta widget class
- Calendar widget class
- Text widget class
- Categories widget class
- Recent_Posts widget class
- Recent_Comments widget class
- RSS widget class
- Tag cloud widget class
- Navigation Menu widget class
テンプレートタグ/the widget – WordPress Codex 日本語版
デフォルトのテキストウィジェットクラスを調べる
この中のテキストウィジェットを参考にウィジェットの作り方を調べていきます
まずは、デフォルトのテキストウィジェットのソースコードを見てみます
class WP_Widget_Text extends WP_Widget { function __construct() { $widget_ops = array('classname' => 'widget_text', 'description' => __('Arbitrary text or HTML')); $control_ops = array('width' => 400, 'height' => 350); parent::__construct('text', __('Text'), $widget_ops, $control_ops); } function widget( $args, $instance ) { extract($args); $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base ); $text = apply_filters( 'widget_text', empty( $instance['text'] ) ? '' : $instance['text'], $instance ); echo $before_widget; if ( !empty( $title ) ) { echo $before_title . $title . $after_title; } ?> <div class="textwidget"><?php echo !empty( $instance['filter'] ) ? wpautop( $text ) : $text; ?></div> <?php echo $after_widget; } function update( $new_instance, $old_instance ) { $instance = $old_instance; $instance['title'] = strip_tags($new_instance['title']); if ( current_user_can('unfiltered_html') ) $instance['text'] = $new_instance['text']; else $instance['text'] = stripslashes( wp_filter_post_kses( addslashes($new_instance['text']) ) ); // wp_filter_post_kses() expects slashed $instance['filter'] = isset($new_instance['filter']); return $instance; } function form( $instance ) { $instance = wp_parse_args( (array) $instance, array( 'title' => '', 'text' => '' ) ); $title = strip_tags($instance['title']); $text = esc_textarea($instance['text']); ?> <p><label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:'); ?></label> <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>" /></p> <textarea class="widefat" rows="16" cols="20" id="<?php echo $this->get_field_id('text'); ?>" name="<?php echo $this->get_field_name('text'); ?>"><?php echo $text; ?></textarea> <p><input id="<?php echo $this->get_field_id('filter'); ?>" name="<?php echo $this->get_field_name('filter'); ?>" type="checkbox" <?php checked(isset($instance['filter']) ? $instance['filter'] : 0); ?> /> <label for="<?php echo $this->get_field_id('filter'); ?>"><?php _e('Automatically add paragraphs'); ?></label></p> <?php } }
思ったより短いソースコードでなんとかなりそうな気がします (^_^;)
ウィジェットクラスには、コンストラクタと3つの関数が定義されています
- __construct
- widget($args, $instance)
- update( $new_instance, $old_instance )
- form( $instance )
__construct コンストラクタ ウイジェット情報の登録
ウィジェット情報を親クラスのコンストラクタを使い登録する
ウイジェット名 | 管理画面に表示する名前 |
ウイジェットオプション | クラス名 管理画面に表示する説明文 |
コントロールオプション | ウィジェットフォームの表示幅 – width: 250px 以上 – height: リザーブ |
widget($args, $instance) ウイジェットのコンテンツ出力(echo)
$args には、呼び出された dynamic_sidebar の情報、ウイジェットの情報、前後の付加する div情報等が設定されています。extract関数で渡された $args 連想配列を変数名と値に展開しています
$instance には、title や text 等のコンテンツ出力データが設定されています。
また、Title と text には、出力をカスタマイズ出来るようにフィルターフックを定義してあり、このフィルターフックに add_filter でカスタマイズするための関数をフック出来るようにしてあります
apply_filters()に関しては、下記サイトで分かりやすく紹介されています
WordPress プラグインをカスタマイズする場合は、apply_filters() を探せ …
テキストウィジェットには、pタグの自動追加する機能が設けられており、filter というチェックボックスがチャックされていれば wpautop($text ) でpタグを自動挿入したテキストを出力します
いくつかの条件判断等がありますが、簡単にいえば echo文で決まった順番でブラウザへ出力しているだけです
- echo $before_widget;
- echo $before_title;
- echo $title;
- echo $after_title;
- echo $text;
- echo $after_widget;
update($new_instance, $old_instance) 設定値確認と保存処理
アップデートでは、インスタンスの内容確認やサニタイズ(セキュリティ対策)を行い
正常時 | 新しいインスタンスを返します(自動的に保存更新されます) |
エラー時 | false を返します(インスタンスは、保存/更新されません) |
ウィジェットは複数使用できるように設計されているので、作成する側で特になにもしなくとも、WordPressが自動的にウィジェット毎に設定を保存管理してくれます
テキストウィジェットでは、セキュリティ対策のためにサニタイズを行います。strip_tags関数で文字列 から NUL バイトと HTML および PHP タグを取り除いています。また、current_user_can(‘unfiltered_html’) でフィルターなしHTMLの権限があるか調べ、権限がなければ、wp_filter_post_kses関数で、HTMLタグを無効化しています
form( $instance ) 設定値の入力フォーム
管理画面でウイジェットのフォームにHTMLを使用してオプション設定やコンテンツ等を入力します
フォームで表示するデータは、引数 $instance で渡されます
wp_parse_args($instance, $default) 関数を使用すると、値のない項目に対しては指定した $default で初期値を補ってくれます
ウイジェットには、識別するための一意な属性(id, name) が付けられています。その属性を get_field_id, get_field_name 関数で取得して、HTML属性の id, name で使用します
マイテキストウィジェットクラスを作成してみる
テキストウィジェットクラスを調べてわかった事を整理します
ウィジェットは、WP_Widgetというクラスを継承したクラスとして作成します
子クラスでは、最小限必要な関数をオーバーライドするだけでよく、かなりの部分は親クラス側で自動的に処理してくれるので作りやすくなっています
ここでは、テキストウィジェットのタイトル部を非表示とするチェックボックスを付けてみたいと思います
テキストウィジェットクラスを元に post6text_widget クラスを作成します
__construct
ウィジェット名、クラス名、説明文を修正します
//コンストラクタ ウイジェット情報の登録 function __construct() { $name = __('Post6 Text' ); $widget_ops = array('classname' => 'post6text_widget', 'description' => __('Post6 Text Widget Description')); $control_ops = array('width' => 400, 'height' => 350); parent::__construct( false, $name, $widget_ops, $control_ops ); }
widget($args, $instance)
フィルターフックは、とりあえず削除して、タイトル表示処理部へ非表示用の条件判定を加えます
//ウイジェットのコンテンツ出力(echo) public function widget($args, $instance) { //連想配列 $args を変数名($before_widget, $before_title, $after_title, $after_widget, ...)に展開 extract($args); $title = $instance['title']; $hide = $instance['hidetitle']; $filter= $instance['filter']; $text = $instance['text']; echo $before_widget; if ( !empty( $title ) && empty( $hide )) { echo $before_title . $title . $after_title; } ?> <div class="textwidget"><?php echo !empty( $filter ) ? wpautop( $text ) : $text; ?></div> <?php echo $after_widget; }
update( $new_instance, $old_instance )
タイトル非表示用のチェックボックス値の保存処理を加えます
//アップデート 設定値確認と保存処理 // OK: Return 新しいインスタンス(インスタンスは自動的に保存更新) // NG: Return false (インスタンスは保存/更新されません) public function update($new_instance, $old_instance) { $instance = $old_instance; $instance['title'] = strip_tags($new_instance['title']); $instance['hidetitle'] = isset($new_instance['hidetitle']); if ( current_user_can('unfiltered_html') ) $instance['text'] = $new_instance['text']; else $instance['text'] = stripslashes( wp_filter_post_kses( addslashes($new_instance['text']) ) ); // wp_filter_post_kses() expects slashed $instance['filter'] = isset($new_instance['filter']); return $instance; }
form( $instance )
タイトル非表示チェックボックスの処理を加えます
//設定値の入力フォーム public function form($instance) { //値がない項目に対して引数で指定した初期値を設定 $default = array( 'title' => '', 'text' => '' , 'filter'=> FALSE, 'hidetitle'=> FALSE); $instance = wp_parse_args( (array) $instance, $default); //タイトルデータ $tl[val] = strip_tags($instance['title']); $tl[id] = $this->get_field_id('title'); $tl[name] = $this->get_field_name('title'); //テキストデータ $tx[val] = esc_textarea($instance['text']); $tx[id] = $this->get_field_id('text'); $tx[name] = $this->get_field_name('text'); //チェックボックスデータ $ht[val] = $instance['hidetitle']; $ht[id] = $this->get_field_id('hidetitle'); $ht[name] = $this->get_field_name('hidetitle'); $ft[val] = $instance['filter']; $ft[id] = $this->get_field_id('filter'); $ft[name] = $this->get_field_name('filter'); ?> <p> <label for="<?php echo $tl[id]; ?>"><?php _e('Title:'); ?></label> <input id="<?php echo $ht[id]; ?>" name="<?php echo $ht[name]; ?>" type="checkbox" <?php checked(isset($ht[val]) ? $ht[val] : 0); ?> /> <label for="<?php echo $ht[id]; ?>"><?php _e('Hide'); ?></label> <input class="widefat" id="<?php echo $tl[id]; ?>" name="<?php echo $tl[name]; ?>" type="text" value="<?php echo esc_attr($tl[val]); ?>" /> </p> <textarea class="widefat" rows="16" cols="20" id="<?php echo $tx[id]; ?>" name="<?php echo $tx[name]; ?>"><?php echo $tx[val]; ?></textarea> <p> <input id="<?php echo $ft[id]; ?>" name="<?php echo $ft[name]; ?>" type="checkbox" <?php checked(isset($ft[val]) ? $ft[val] : 0); ?> /> <label for="<?php echo $ft[id]; ?>"><?php _e('Automatically add paragraphs'); ?></label> </p> <?php }
チェックボックスを1個加えただけなのでほとんどオリジナルと変わりません (^^)
実行させるには登録が必要
クラスを作っただけでは実行出来ませんので、このクラスをインスタンス化(実体化)する必要があります
インスタンス化は、専用の register_widget関数を使用します
作成したウイジェットを複数利用する場合でも、引数にクラス名を指定して1回だけ呼び出します
register_widget("post6text_widget");
この関数内で、new演算子によりインスタンス化してコンストラクタが呼び出されますが、同じウイジェットを複数使用していてもコンストラクタが呼び出されるのは1回のみです。詳細は分かりませんが、複数個分のウイジェットをインスタンス化しているわけではなく、そう見えるようにWordPressがウイジェット毎に管理してくれていると理解しています (^_^;)
動作確認には、既にある自作プラグインを使いました
- プラグインファイルの先頭で post6text_widget.php ファイルを読み込ませます
- プラグインのコンストラクタ内から add_action関数で ‘widgets_init’ にフック
- フックさせた関数内で register_widget("post6text_widget") 実行
Widget タイププラグインのおおよその作り方は分かりました
今度はもう少し実用性のあるウィジェットに挑戦したいと思います