前回は「Wordpress プラグインの動作原理」について書きました。
Wordpress プラグインの作り方、2回目の今日は「プラグインをクラスにまとめる」です。
プラグインを作る際、Global な空間に関数を増やすのは得策ではありません。
Namespace も PHP 6 5.3 からですし、ここはひとつクラスを作ってそこに全部まとめてしまいましょう。
抽象クラス
プラグインの動きのうち決まりきっている機能を抽象クラスに実装します。
抽象クラスといっても実装する際に直接このクラスにメソッドを追加しても OK です。
と言うか僕は大体そうしています。
僕がいつも使う抽象クラスはこんな感じ。
class abstractClass { private static /** * プラグイン名 */ $pluginName = 'This plugin name.', /** * クラス名 */ $className, /** * オプションの規定値 */ $defaultOptions = array( 'options1' => 1, 'options2' => 2, 'options3' => 3 ); /** * 初期処理 * @return void */ public function init(){ self::$className = get_class(); self::translation(); add_action( 'admin_menu', array( self::$className, 'addOptionsPage' ) ); } /** * 翻訳データ読み込み * @return void */ private function translation(){ load_textdomain( self::$className, dirname(__FILE__).DIRECTORY_SEPARATOR.get_locale().'.mo'); } /** * オプションの保存と読み込み * @return array オプションの値 */ private function postAndGetOptions(){ $options = get_option( self::$className ); if( !current_user_can( 'manage_options' ) ) return $options; foreach( self::$defaultOptions as $name => $default ){ if( isset( $_POST[$name] ) ) $options[$name] = $_POST[$name]; if( !isset( $options[$name] ) ) $options[$name] = $default; } update_option( self::$className, $options ); return $options; } /** * 設定ページのリンクを設定タブに出力 * @return void */ public function addOptionsPage(){ if ( !function_exists( 'add_options_page' ) ) return; add_options_page( self::$pluginName, __( self::$pluginName, self::$className ), 8, __FILE__, array( self::$className, 'displayOptionsPage' ) ); } /** * 設定ページの出力 * @return void */ public function displayOptionsPage(){ $options = self::postAndGetOptions(); $labels = array( 'header' => __( 'This plugin name.', self::$className ), 'options1' => __( 'options1', self::$className ), 'options2' => __( 'options2', self::$className ), 'options3' => __( 'options3', self::$className ), 'SUBMIT' => __( 'Save as changes', self::$className ) ); echo <<<_HTML_ <div class="wrap"> <h2>{$labels['header']}</h2> <form action="{$_SERVER["REQUEST_URI"]}" method="post"> <table class="form-table" border="0"> <tbody> <tr> <th>{$labels['options1']}</th> <td><input name="options1" type="text" value="{$options['options1']}" /></td> </tr> <tr> <th>{$labels['options2']}</th> <td><input name="options2" type="text" value="{$options['options2']}" /></td> </tr> <tr> <th>{$labels['options3']}</th> <td><input name="options3" type="text" value="{$options['options3']}" /></td> </tr> </tbody> </table> <p class="submit"><input name="SUBMIT" type="submit" value="{$labels['SUBMIT']}" /></p> </form> </div> _HTML_; } } abstractClass::init();
メソッドの解説
抽象クラスに実装されているメソッドはそれぞれこんな役割を担っています。
- abstractClass::init()
- 初期化処理。自分のクラス名をキャッシュしたり、プラグインの翻訳データを読み込んだりします。
また、トリガにフックする手続きもここで行っておくのが無難です。
このメソッドはクラスが書かれているファイルで呼び出しておきます。 - abstractClass::translation()
- プラグインの翻訳データを読み込みます。
翻訳については次回。 - abstractClass::postAndGetOptions()
- abstractClass::$className をオプション名としてオプションを読み込みます。
読み込んだオプションのキー名で POST された値があればオプションの値を上書きします。
上書きされたオプションを再度チェックし、値がない場合は abstractClass::$defaultOptions に定義されている配列のキーを元にオプションの値を変更します。
オプションを保存しつつ返します。
つまり、プラグインを始めて有効にした際にはデータベースにオプションの値が保存されていないため、自動的に abstractClass::$defaultOptions の値が使われる事になります。
またこのメソッドは呼び出される状況が広範に渡る事が予測されますので、オプションを保存できるのは「manage_options」という権限を持ったユーザのみに限られます。
manage_options は設定ページで任意の設定が行える権限です。(後述:1) - abstractClass::addOptionsPage()
- 設定ページにプラグインの設定ページへのリンクを追加します。
プラグインの設定ページへのリンクをクリックすると abstractClass::displayOptionsPage() が呼び出されます。 - abstractClass::displayOptionsPage()
- プラグインの設定ページに HTML を出力します。
フォームであることがほとんどです。
$labels はフォームに入力する項目の項目名や表題などで、これはフォームに直接書いてしまっても構いません。
僕はヒアドキュメントを使っているので変数に格納しています。
これだけです。
プラグインの動作のうち、ほぼ全てのプラグインに必要なのは以下の三つで、abstractClass クラスでは全てが実装された状態です。
- オプションの値を保存・読込する機能
- 設定ページの出力
- 翻訳データの読込
この静的なクラスを使ってプラグインを実装すれば、Global 空間は汚れないし、静的なクラスなので変数のもち回しを気にする必要もありません。
プラグインがプラグインだと認識されるためには
プラグインはただ単に plugins ディレクトリにアップロードしてもプラグインだとは認識してくれません。
プラグインファイルの冒頭に下記のコメントを書く事によって、初めてプラグインとして認識されます。
/*
Plugin Name: プラグイン名
Plugin URI: http://spais.jp/プラグインを公開している URI
Description: プラグインの説明
Version: プラグインのバージョン
Author: プラグインの製作者
Author URI: http://spais.jp/プラグイン製作者の URI
*/プラグインが認識されたらプラグインを有効にしてみましょう。
管理画面の設定ページに abstractClass プラグインの設定ページへのリンクが表示されるようになったはずです。
プラグインが現在有効かどうかは Wordpress が管理しています。逆を返せばテーマに干渉するようなプラグインである場合は、プラグインが有効であるかどうかを method_exists() などで確認する必要があります。そうでないとプラグインが無効な状況では Fatal error が発生してしまいます。
さて、次回からは実際にプラグインを作る過程を踏まえつつ、管理画面にプラグイン独自の設定ページを追加する詳細を追ってみたいと思います。
以上、現場から生中継でお送りしました。
後述:1
Wordpress の認証機構は「権限」という形で提供されています。
Wordpress を利用できるユーザにはそれぞれ”権限”と言う要素があり、権限には「これをやってもいい」「あれはやってはいけない」と任意の条件を複数設定する事が出来ます。
例えば、編集者はプラグインを有効にする事は出来ないが、自分以外のユーザが書いた記事を削除する権限を持っています。
投稿者は基本的に、記事を書くための権限しか与えられていません。
また、今回の記事で書いたソースでは、current_user_can() という関数で名称のついた権限を評価していますが、権限の持つ力を数値で表す方法もあります。キャパビリティという名前の通り、どこまで許されるのかと言う本質的な評価を行います。
ただ、数値で表すのは多少乱暴でもあるので、僕的には任意の名称で厳密な役割を評価させるべきだと思います。
入り口は細い方がハンドリングしやすいと言う話。
レベル7までの行動が許されている。と言われるよりは、「食べ物を口に入れながら眠ってはいけない」など(人にとって)明確な判断をする方がより普遍的だと思うわけです。非公開ならそれもありかもしれませんが。
ちょっと横道にそれましたが件のメソッドでは、関所のようなものを設置しておく事が可能です。
関所があるおかげで、別のメソッドにする必要はないけどそのままだとよろしくないケースにおいて、権限と言う明確な基準でアクセスしている対象を評価できるため、Wordpress の認証システムを割りと簡単にプラグインに組み込む事が可能です。
結構色々とあるので詳細は端折りますが、Role Manager というプラグインで権限と権限の条件を管理できるようになります。
Wordpress をサービスとして展開する場合、こういった仕組みが必要となってきます。きますと言うか、こういう機能が提供されているので使った方が楽。
割と有機的な機能なので、Bブログの編集者だったらAブログの投稿者になれるなど、SNS 的な仕組みも実装できます。プラグインとしてブログごとにインストールし、中央にサーバを持つ必要のない SNS が作れるわけです。ネットワークを俯瞰できる要素を持たないのでこれはこれでたどっていく楽しみがあるかもしれません。ブログの URL をユーザとして識別して、識別するため Wordpress に”フレンド”などと言った概念で登録(申請)できるプラグイン、と言う形で提供するのが楽しそうですね。
- 新しい: Wordpress プラグインの作り方(3)
- 古い: ArtRage2が面白い
コメント:7
- halt 2008 年 7 月 30 日
超重箱の隅ですがnamespaceは5.3からです。クロージャとかnamespaceとかつめこみすぎですね。
- admin 2008 年 7 月 31 日
>halt さん
>超重箱の隅ですがnamespaceは5.3からです。
ご指摘ありがとうございます。すいません適当に書いて。>クロージャとかnamespaceとかつめこみすぎですね。
重箱だけに。クロージャも namespace も僕は超期待していますよ!
あと今年こそ PHP 勉強会に参加したい!ここだけの話、6のコードネームが5.3らしいですよ。
* *
* うそです +
n ∧_∧ n
+ (ヨ(* ´∀`)E)
Y Y *- NaggySpice 2008 年 8 月 29 日
はじめまして、WordPressとPHPを調べ始めたところで参考にさせていただいてます。(MTのプラグインが難しくて)
さっそくプラグインして試してみたのですが、オプションの保存が・・・の”options1″は”options3″ですよね。(作り方4も)
プラグインの作り方が流れで解説されていて非常に参考になります。
- NaggySpice 2008 年 8 月 29 日
をを、コードをコピペしてしまって消えてました。
displayOptionsPage() のoption3用のinput name=”options1″の部分です。
失礼しました- admin 2008 年 8 月 29 日
>NaggySpice さん
はじめまして!読んでいただきありがとうございます!> オプションの保存が・・・
> の”options1″は”options3″ですよね。(作り方4も)おお!ご指摘ありがとうございます!!
早速修正しておきます。> プラグインの作り方が流れで解説されていて非常に参考になります。
びゃああああ><
ありがとうございます。とっても励みになります。- もぎゃ 2009 年 1 月 16 日
“wordpress プラグイン クラス”でググってたどり着きました。具体的なサンプルがあって助かります。
add_optionはArrayも保存できてしまうのですね!なんだか中断しちゃっているみたいですが、続き楽しみにしております~。
- nic 2009 年 1 月 22 日
>もぎゃさん
はじめまして!読んでいただきありがとうございます!
> add_optionはArrayも保存できてしまうのですね!
そうです!そうですと言うより一つのプラグインでいくつものオプションを作ってしまうことはコンクリフト的な視点からも余り良くはない事ですので、基本的にはプラグインのオプションは配列で一つのオプションとして保存しておくのがモアベターです。
> なんだか中断しちゃっているみたいですが、続き楽しみにしております~。
2つくらいストックがあったのですが推敲してる間に 2.7 がリリースされてしまってオプション周りの仕様が若干変更した影響もあって未だ遂行中でございます。今しばらくお待ち下さい。
ここだけの話、次はオプション周りがテーマとなっております。
トラックバック:5
- この記事のトラックバック URL
- http://spais.jp/php/wordpress-%e3%83%97%e3%83%a9%e3%82%b0%e3%82%a4%e3%83%b3%e3%81%ae%e4%bd%9c%e3%82%8a%e6%96%b92/2008-07-30/trackback
- トラックバックの送信元リスト
- Wordpress プラグインの作り方(2) - SPaiS より
- pingback - SPaiS - Wordpress プラグインの作り方(3) より 2008 年 8 月 1 日
[...] 前回は実際にプラグインとして最低限必要であろう機能を実装した抽象クラスを扱いましたが、今回は Wordpress のとても重要な機能の一つである”翻訳”について一から作る場合の方法 [...]
- pingback - SPaiS - Wordpress プラグインの作り方(1) より 2008 年 8 月 16 日
[...] pingback from SPaiS – Wordpress プラグインの作り方(2) 08-07-30 (水) 10:29 [...]
- pingback - SPaiS - Wordpress プラグインの作り方(4) より 2008 年 8 月 27 日
[...] 今回はWordpress プラグインの作り方(2)で作ったプラグイン用の抽象クラスを使います。使いますと言うかすでに実装されています。 中身は同じですが、便宜上以下にプラグイン用の抽象 [...]
- pingback - やじゅろぐ » WordPressプラグインに挑戦してみたい より 2008 年 10 月 21 日
[...] SPaiS – Wordpress プラグインの作り方(2) [...]
- pingback - WordPressノウハウ~走り書き~ - typista より 2008 年 11 月 20 日
[...] ◆プラグインの翻訳方法など http://spais.jp/php/wordpress-%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B92/2008-07-30 http://spais.jp/php/wordpress-%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%AE%E [...]