Class

Top > PluginDev > Class
First Edition. 01/16/2006
Last Modified. 01/16/2006
Table of Contents

プラグインクラス化

プラグインは、PukiWiki 配下で稼動するため、本体に影響を及ぼさないために、関数名およびグローバル変数名の命名には、注意が必要となる。これら命名には、プラグイン名をプレフィックスとして付加することを推奨する。

これがPukiwikiプラグインの仕様ですが、全ての関数の先頭にプレフィックスを付けていると関数名が無駄に 長くなり、関数を使う気がなくなります。結果、1つの関数に全て記述してしまうなど、ソースコードが混沌に陥ります。クラス化することで、名前空間を分離し、きれいなコードが書けることを期待します。

関数だけでなく、グローバル変数や、定数など名前空間を共有してしまうものも、プレフィックスを付けなければならず長くなってしまうので、クラスの中に入れ込んで短縮を計るにはどうしたらよいかを案じます。

現在は クラス変数版 を使用しています。

従来方式

まず、前提知識である従来方式です。
参照:dev:プラグイン/開発者向け

<?php
// define() など。名前空間を汚さないように 'PLUGIN_XXX_YYYYY' のように接頭辞が必須 
// プラグインを exist_plugin (require_once) した時に実行されます。
// 1度のPukiWiki実行で1度だけ実行されます。
function plugin_xxx_init()
{ 
// 初期化関数。
// convert, inline, action いずれかのプラグインが呼び出されるとまず最初に実行されます。
// 1度のPukiWiki実行で1度だけ実行されます。
}
function plugin_xxx_convert()
{
// #プラグイン名形式で呼び出されます
}
function plugin_xxx_inline()
{
// &プラグイン名;形式で呼び出されます
}
function plugin_xxx_action()
{
// GET・POSTメソッドでpluginを指定されたときに呼び出されます
}
?>

plugin_xxx_init 内も関数の外も、1度のPukiWiki実行で1度だけ実行されるので、効果は同じと考えてよいでしょう。 ただ関数の外にコードを書くと変数がグローバル変数となってしまうので、define 以外は plugin_xxx_init 関数内にコードを記述するのが筋です。 注:古いバージョンには plugin_xxx_init がプラグイン呼び出し毎に実行されるバグがあります dev:BugTrack2/246

クラス化について考える

基本的には次のようにクラス化できると思います。参考 dev:プラグイン/開発者向け

class PluginXxx {
  var $...                /* define()していた設定用定数やグローバル変数など */
  function PluginXxx()  { /* plugin_xxx_init()    */ }
  function action()     { /* plugin_xxx_action()  */ }
  function convert()    { /* plugin_xxx_convert() */ }
  function inline()     { /* plugin_xxx_inline()  */ }

この対応によるとコンストラクタ PluginXxx が呼び出されるのは1度だけ、つまり作られるインスタンスは1つだけで、それを使い回すという形になります(plugin_xxx_init() は1度のPukiWiki実行で1度だけ実行されることを思い出してください)。 もしもインスタンス変数を使用したい場合、インスタンス変数を初期化する場面がなくなります。 そこでその問題に対して4つの案を考えました。

(似非)シングルトン版

まずは簡単に基本形に、init() 関数を追加し、各プラグイン呼び出し直前に呼び出し、インスタンスを初期化する方法です。

// シングルトンクラス
class PluginXxx { 
  var $...                /* define()していた設定用定数やグローバル変数など */
  var $...                /* インスタンス変数     */
  function PluginXxx()  { /* plugin_xxx_init()    */ }
  function init()       { /* インスタンスの初期化 */ }
  function action()     { /* plugin_xxx_action()  */ }
  function convert()    { /* plugin_xxx_convert() */ }
  function inline()     { /* plugin_xxx_inline()  */ }

この方式の問題は、初期化が面倒くさいということです。1つ1つのインスタンス変数を再設定しなければいけません (new で新しいインスタンスを作成する場合、コンストラクタに一切記述がなくても初期化されることや、変数宣言部で初期化できたことを考えてください)。 また、グローバル変数として利用されるインスタンス変数なのか、インスタンス変数として利用されるインスタンス変数なのか、区別がつきにくい点も問題です。

却下と言ってよいでしょう。

詳細な実装方法は PluginDev/Class/Idea1 を参照してください。

クラス変数版

少し考え方を変えて、plugin_xxx_init() はもはや無視し、 各プラグイン呼び出し毎に (plugin_xxx_action|convert|inline の先頭で) new する方法です。

class PluginXxx { 
  static $...             /* define()していた設定用定数やグローバル変数など */
  var $...                /* インスタンス変数     */
  function PluginXxx()  { /* インスタンスの初期化 */ }
  function action()     { /* plugin_xxx_action()  */ }
  function convert()    { /* plugin_xxx_convert() */ }
  function inline()     { /* plugin_xxx_inline()  */ }

この方式では、インスタンス変数をプラグイン呼び出し毎に常に初期化するので、 グローバル変数の代わりには、new しても初期化されない static なクラス変数を使用します。 ただし PHP4 ではクラス変数をサポートしていないので、PHP4 にも対応できるようにクラス変数らしきものを使うテクニックを使用します。

結論から言うと、拙作プラグインはこの方針を採用しています。

詳細な実装方法は PluginDev/Class/Idea2 を参照してください。

(似非)名前空間版

そもそもインスタンス変数は使用せずに、クラスを名前空間としての意味だけで使用します。

// シングルトンクラス
class PluginXxx { 
  var $...                /* define()していた定数やその他の変数など */
  function PluginXxx()  { /* plugin_xxx_init()    */ }
  function action()     { /* plugin_xxx_action()  */ }
  function convert()    { /* plugin_xxx_convert() */ }
  function inline()     { /* plugin_xxx_inline()  */ }

最初のシングルトンクラス方式で、インスタンス変数として利用されるインスタンス変数を一切使用しなければ、init() 関数も必要ありません。クラスを名前空間としての意味だけで使用します。 ただし、一応メンバ変数が存在しますので、PluginXxx::関数() のような形ではなく、一旦 new して $obj->関数() とアクセスしなければならないので、(似非)名前空間です。

この方式の問題点はクラス化することの利点が減るということです。 クラス化しない従来方式でのプログラミングとほぼ変わりがありません。 この欠点は、従来方式のコードと最も互換性が高い、という利点と考えることもできるかもしれません。

詳細な実装方法は PluginDev/Class/Idea3 を参照してください。

(似非)シングルトンコピー版

インスタンス変数を汚す前にコピーをとっておく。

// シングルトンクラス
class PluginXxx { 
  var $...                /* define()していた設定用定数やグローバル変数など */
  var $...                /* インスタンス変数     */
  function PluginXxx()  { /* plugin_xxx_init()    */ }
  function action()     { /* plugin_xxx_action()  */ }
  function convert()    { /* plugin_xxx_convert() */ }
  function inline()     { /* plugin_xxx_inline()  */ }

最初の(似非)シングルトン方式で、インスタンス変数を汚す前にインスタンスのコピーを作っておきます(もはや明らかにシングルトンではないですが気にしないでください)。そうすれば init() する必要がありません。この方式の問題点は(似非)シングルトン方式の時と同様に、グローバル変数として利用されるインスタンス変数なのか、インスタンス変数として利用されるインスタンス変数なのか、区別がつきにくくなる点です。しかし、いろいろ考慮すると、これが一番すんなり行きそうです。

詳細な実装方法は PluginDev/Class/Idea4 を参照してください。

比較

ユーザ設定(Plus!)などを考慮すると 現在は(似非)シングルトンコピー版が一番良いのではないかと考えています。 しかし、普通に考えたらクラス変数版が一番理に叶っているデザインだと思います。 やはり問題は PHP4 でクラス変数そのものが使用できないことでしょうか。

ちなみに拙作のプラグインではクラス変数版方式を採用し、できるだけメンバ変数を使用せずに、 (似非)名前空間版のような振る舞いになるようにしています。

というわけで、拙作プラグインのコードを読む場合に参考にしてみてください (^-^