拙作の野球スコアブックモジュールWebScoreRevolutionはV1.31まではシングルモジュールとしてのみ動作し、モジュールの複製には対応しておりませんでした。
しかしこのたび、モジュール複製可能なように実装を見直している最中なので、その過程をメモとしてまとめておきます。
モジュールの複製とは
XOOPSで言う複製可能なモジュールとは、例えばhogehogeというモジュールがあった場合、
XOOPS_ROOT_PATH/modules/hogehoge
の他に
XOOPS_ROOT_PATH/modules/hogehoge2
というような、ユーザーが(ある程度)自由に決めたモジュールディレクトリ名でファイルを置くことができ、両方のモジュールがインストール可能であることを言います。
Duplicatable V3とは
XOOPS Cubeの実装で良く耳にする「Duplicatable V3(略してD3)」も、モジュール複製の実現手法の1つです。
以下のような仕様をもつものとして定義されています。
*必要条件(こうじゃなければD3モジュールとは呼べないもの)
(1) 原則的にX2互換であること
(2) dirnameが[0-9a-zA-Z_-]のキャラクター組み合わせで自由に決められること
(3) ほとんどのロジックがXOOPS_TRUST_PATHの内側にあること
(4) ROOT/modules/下にあるディレクトリをコピーするだけで複数動作すること(そこにソースコード書き換えは一切発生しないこと)
(5) XOOPS_TRUST_PATHの内側(ロジックの記述されたファイル)は複製する必要がないこと
D3については以下も参考になります。
X難 – XOOPS Cubeってむずかしい !? – Duplicatable V3 って何?
僕はこういう基準でモジュール複製を考えました
上記にあるD3の仕様を見るかぎり、D3対応を名乗るにはXOOPS2対応のモジュールでなければなりませんが、あいにく僕が作ったモジュールはXOOPS2対応はしておらず、XCL専用です。
ですので、この時点でどうあがいてもD3の名前を冠することはできません。
また、D3には
- altsysが必要(たぶん)
- XOOPS_TRUST_PATHの設定が必要
という制限があります。
altsysがもともと入っていないXOOPSシステムに、altsysを追加でインストールするのは手間です。
また、XOOPS_TRUST_PATHを設けると、ファイルアップロード先が増えることになるので、インストール時に間違いが起こりやすくなります。
もちろんセキュリティの観点から、XOOPS_TRUST_PATHに置くファイルと、そうではないファイルをきちんと分けてサイト管理する方が望ましいのですが、
public_html以下にしかファイルを置けないレンタルサーバーがある
(つまりXOOPS_TRUST_PATHをセキュアなパス上に置けない)
といった現状を考えてみると、必ずしもXOOPS_TRUST_PATHは万能な方法ではありません。
(もちろん知識も豊富で環境も揃っておられる方はXOOPS_TRUST_PATHを利用するべきですが)
僕としては、なるべくシンプルな手順でモジュールを複製できる方法を探りたかったので、altsysにもXOOPS_TRUST_PATHにも頼らない独自の複製方法を考えることにしました。
複製方法
altsysやXOOPS_TRUST_PATHの概念を使わない複製方法を以下に示します。
テーブル名が重複しないようにする
モジュールを複製する場合、テーブルもモジュール毎に別々に持つ必要がありますので、mysql.sqlやxoops_version.phpで定義している
{prefix}_tablename
を
{prefix}_{dirname}_tablename
とします。こうすることで、モジュールディレクトリ毎に異なったテーブル名を生成し使用することが可能になります。
モジュールにアクセスしていなくてもxoops_version.phpはCallされるので対策する
例えばmodule1モジュールのURLは
http://xxx.com/modules/module1
となりますが、このモジュールディレクトリ以下にアクセスしたときは、必ずmodule1のxoops_version.phpがCallされます。
同様に、module2モジュールのURLは
http://xxx.com/modules/module2
となります。このときはmodule2以下のxoops_version.phpはCallされますが、module1のxoops_version.phpはCallされません。
このようにそれぞれのモジュール下にアクセスするときは、いずれか一方のモジュール下のxoops_version.phpしかCallされませんが、
以下の場合においてはmodule1とmodule2のxoops_version.phpが同時にCallされますので考慮が必要です。
- 管理画面へのアクセス
- サイトTOP画面へのアクセス
同じ内容を持ったxoops_version.phpが複数回Callされると、define定義重複エラーなどになりますので、重複を避けるような名前をつける必要があります。定義名はディレクトリ名を含む文字列にするとよいでしょう。
また、ユーザーがDB禁止文字を含むディレクトリ名をつけないように、モジュールディレクトリ名に制限を設けておきます。
xoops_version.php
$mydirname = basename(dirname(__FILE__)); if( ! preg_match( '/^(\D+)(\d*)$/' , $mydirname , $regs ) ){ echo ( "invalid dirname: " . htmlspecialchars( $mydirname ) ) ; } $constpref = '_MI_' . strtoupper( $mydirname ); $root =& XCube_Root::getSingleton(); $language = $root->mContext->getXoopsConfig('language'); require_once( XOOPS_MODULE_PATH . '/' . $mydirname . '/language/' . $language . '/modinfo.php' );
定義名は、$constprefを使って、
constant($constpref.’_MODULE1_NAME’);
というように定義できます。
その他、$modversion['name']や$modversion['dirname']もモジュール毎に別の名前をセット出来るように工夫する必要があります。
ブロックの複製に対応する
複数のモジュールのブロック表示が行えるように、ブロックで使用する定義、関数名、クラス名は重複を避ける必要があります。
関数名を2度読み込ませないようにするにはfunction_exists(),クラス名ならclass_exists()という関数がありますが、以下のようにphpファイル内の処理全体を、フラグで制御する方法もあります。
特に、blocks/以下に配置するブロック表示のためのphpスクリプトは、function_existsでブロックしてもエラーが出てしまいますので、下記のような再定義防止コードを埋め込むことで解決した方が良いでしょう。
if( !defined('_MODULE1_BLOCK1') ){ define('_MODULE1_BLOCK1', 1); ・・・・処理内容・・・・ }
ブロック用の表示/編集関数であるshow_func()とedit_func()は、引数でdirnameを取得できるように工夫すると良いでしょう。
dirnameが$options[0]から取得できるようにします。
例えば、
{prefix}_newblocksテーブルのoptionsカラムが
n|n
となるブロックの場合、
dirname|n|n
となるように値を保存しておきます。
そうすることでdirnameが$options[0]で取得できるようになり、以降のパラメータはoptions[1]…にて取得できます。