MODxのスニペット開発のガイドライン

MODxのスニペット開発において、「Creating Snippets」の内容は事実上のガイドラインとなるものです。気をつけておきたい点がいくかあったため、読んだついでに翻訳しておきます。

スニペットというよりは、PHPでのコーディングに関する基本的な内容だと思いますが、スニペット開発を通してPHPへの理解を深めようという人は、このガイドラインを尊重することによって、ミスやバグの混入の防止、可読性の向上につながるコードが書きやすくなるでしょう。結果として、MODxのスニペット開発もより容易なものとなるはずです(なので、すごいリソースを作って公開してください :D)。

例の中で、説明の簡略化のために変数の値をそのまま出力している箇所がありますが、HTMLとしての出力を許可しない場合はhtmlspecialchars()関数でエスケープしなければなりません。また、フォームなどに由来するパラメータを扱う場合、MODx側で何か特別な処理をしてくれるわけではないため、XSSなどへの対策はスニペット側で行う必要があります(more secureプラグインも参照してみてください)。

なお、Wikiのページに書かれている内容はほぼ全て網羅していますが、基本的に意訳です。また、ガイドライン内で矛盾している点は正しいと思われるものに合わせています。この記事はMODx Wikiのライセンスに準じます(日本語ページの作り方がわからない)。

カレントドキュメントのID取得にはdocumentIdentifierを使う

悪い例
$id = $modx->getDocumentIdentifier('id');
良い例
$id = $modx->documentIdentifier;

getDocumentIdentifier()メソッドは、フレンドリーURLsを使っている場合に問題が発生する可能性があるため、ドキュメントのIDを取得する目的では使わないようにします。

ダブルクォーテーションの代わりにシングルクォーテーションを使う

シングルクォーテーションを使うと、PHPのコアがコードをパースする際の高速化につながります。

悪い例
echo "MODx";
良い例
echo 'MODx';

変数は文字列に含めるのではなく、文字列と結合する

変数を文字列に含めてしまうと、バグの混入や可読性の低下につながります。文字列と変数はドット(.)演算子で結合するようにします。

悪い例
echo "My name is $name";
良い例
echo 'My name is ' . $name;

変数・関数・クラスの名前はリソースのイニシャルから始める

変数・関数・クラスには、接頭辞としてリソースのイニシャルを付けるようにすると名前の衝突を避けられます。

悪い例
$lang;
function countRows() { ... }
class RowClass { ... }
良い例
// リソースの名前がResourceNameの場合
$rnLang;
function rnCountRows() { ... }
class RnRowClass { ... }

変数には内容を推測しやすい名前を付ける

変数には、変数名から内容を推測することができる名前を付けることで可読性の向上につながります。

悪い例
$country = 'Japan';
$count = 1;
$rows = array();
良い例
$rnStrCountry = 'Japan'; // 'Str'が文字列(String)であることを表している。
$rnNrCount = 1; // 'Nr'が数字(Number)であることを表している。
$rnArrayRows = array(); // 'Array'が配列(Array)であることを表している。
別の良い例
$rn_str_country = 'Japan';
$rn_int_count = 1;
$nr_ar_rows = array();

関数や引数には返り値や内容が推測できる名前を付ける

関数には、その関数が行う処理や返り値の型を推測できる名前を付けるべきです。

悪い例
function name($userid) { return $name; }
function member($userid) { return true; }
良い例
function rnGetStrName($rnNrUserId) { return $rnStrName; } // 文字列型(String)の名前(Name)を返す関数
function rnIsValidMember($rnNrUserId) { return true; } // 真偽型(Boolean)のメンバー(Member)を返す関数

関数はfunction_exists()で再定義を防ぐ

同じページで一つの関数が2回以上定義された場合、PHPのパースエラーが発生します。リソースが複数回呼び出される可能性がある場合は、function_exists()関数による条件分岐で再定義を防ぐ必要があります。

悪い例
function rnCountRows() { ... }
良い例
if (!function_exists(rnCountRows)) {
    function rnCountRows() { ... } 
}

変数は使う前に宣言・初期化する

変数を初期化することで、変数インジェクションによるバグの混入や攻撃を防ぎます。

悪い例
echo $rnStrCountry;
良い例
$rnStrCountry = ''; echo $rnStrCountry;

生成した変数・オブジェクトは破棄する

スニペットで生成した変数・オブジェクトは使用後に破棄するようにします。サーバメモリなどのリソースの節約につながります。

悪い例
$rnStrCountry = 'Japan';
良い例
$rnStrCountry = 'Japan'; ... unset($rnStrCountry);

コードの中程で大きなリソースを読み込む場合は、スニペットの実行終了を待つのではなく、リソースの使用が終わった時点で破棄するようにするとよいでしょう。

スニペットのパラメータはその妥当性を検証する

スニペットのパラメータは、可能な限り、その妥当性を検証するべきです。

悪い例
$rnTimeStamp = $param;
良い例
if (isTimestamp($param)) $rnTimeStamp = $param;

スニペットのパラメータはデフォルト値を持つべきである

パラメータが初期化されなかった場合に発生するエラーを避けたり、スニペットの使用方法をシンプルにするためにも、スニペットが持つ設定可能なパラメータは初期値を持つべきです。パラメータがデフォルト値を持つことが出来ない場合はチェックを行い、パラメータが設定されなかった場合にはエラーメッセージを表示するべきです。

悪い例
$rnStrName = $param;
良い例
$rnStrName = isset($param) ? $param : 'default value';

プレゼンテーションに関するコードを含むべきではない

スニペットやプラグインのようなロジックの中に、出力するHTMLなどをハードコーディングするべきではありません。プレイスホルダを出力内容で置換するか、出力のためのテンプレートをチャンクから取得するべきです。そうすることで、スニペットはユーザにとってよりフレキシブルなものになるとともに、可読性の向上や多言語化の容易さにもつながるでしょう。

悪い例
$output = '<p>' . $rnStrName . '</p>';
return $output;
良い例
// ユーザはテンプレートの中で「<p>[+rnStrName+]</p>」のように記述する。
$modx->setPlaceholder('rnStrName', $rnStrName);
return;
さらに良い例
// チャンクを使い、その中でデータを出力するためのプレイスホルダを使う。
// 初めにプレイスホルダに値を設定し、プレイスホルダが置き換えられたチャンクを返す。

// プレイスホルダに値を設定する。
$modx->setPlaceholder('name', $varName);		
$modx->setPlaceholder('lastvisit', $varLastvisit);

// チャンクを返す。
return $modx->getChunk('NameOfChunk');

parseChunk()メソッドを使用すると、チャンク内のプレイスホルダを連想配列の値で置き換えることができます。出力するデータが多い場合にはこちらを使用すると良いでしょう。

日時の出力にはdate()の代わりにstrftime()を使う

strftime()関数はset_local()関数の影響を受けるため、出力結果の国際化が容易になります。date()関数はset_local()関数の影響を受けません。

悪い例
$rnStrFormat = 'F j, Y, g:i a'; 
$rnStrToday = date($rnStrFormat); // March 10, 2006, 1:23 pm
良い例
// ユーザがconfig.inc.phpなどでロケールを設定する場合を想定しています。
$locale = setlocale(LC_ALL, 'de_DE.UTF-8');
$rnStrFormat = '%B %d, %Y, %H:%S';
$rnStrToday = strftime($rnStrFormat); // März 23, 2006, 13:23

スニペットのロジックは外部ファイルに分ける

スニペットが巨大なものであれば、スニペットには設定に関するコードだけを残して、ロジックに関するコードは外部ファイルに分けます。例えば、一つのファイルにロジックをまとめ、他のファイルに関数をまとめるなど、スニペットの構成がしやすくなります。

多くのサーバではmod_securityが導入されていますが、ユーザがスニペットを導入するためにコードをコピー&ペーストした際に、mod_securityがPOSTされたデータのいくつかの文字列を拒否した場合に問題が発生することがあります。ロジックに関するコードを外部ファイルに分けることは、ユーザがコピー&ペーストするコードが少なくなり、コードの破損を防止する意味でも良い方法です。

また、スニペットが1つのページで複数回呼び出された場合に、同じファイルが複数回読み込まれることを防ぐために、include()やrequire()、require_once()の代わりにinclude_once()を使うようにしてください。PHPでopen_basedirが設定されている環境をサポートするために、インクルードするファイルのパスには$modx->config['base_path']を使用しなければなりません。

悪い例
$rnNrCount = 10;
for ($rnIndex = 0; $rnIndex < $rnNrCount; $rnIndex++) {
 ロジックに関するコード
}
良い例
$rnStrName = isset($rnStrName) ? $rnStrName : 'default value';
include_once($modx->config['base_path'] . 'assets/snippets/yoursnippet/yourcode.inc.php');

コードには説明となるヘッダを付ける

コードにはリソース名や作者名、バージョン、動作環境、などのリソースの説明となるヘッダを付けると良いでしょう。

コードには可能な限りコメントを付ける

コードには可能な限りのコメントを付けることで、可読性の向上や他の開発者との共同作業の円滑化につながります。

コードをオブジェクト化する

多くの共通の関数を書くのであれば、それらをクラスとしてまとめてるようにします。また、オブジェクト指向プログラミングを採用する場合でも、関連性のない膨大な関数を作成することがないようにしてください。

良いスニペットの例として、スニペットは自身のテンプレートの中に設定のためのパラメータを持ち、実際のロジックは外部ファイルをインクルードします。そして、クラスのインスタンスとメソッドによって実行されます。

<?php
include_once($modx->config['base_path'] . "assets/snippets/paintboard/paintboard.inc.php");

$PaintBoard = new PaintBoard(array(
    'width' => 400,
    'height' => 300,
    'title' => 'My Canvas'
));

$modx->setPlaceholder('pbCanvas', $Paintboard->render());

設定のための配列をクラスのコンストラクタに渡し、インスタンスを生成した後、render()メソッドがペイントボードをレンダリングします。

paintboard.inc.php
<?php
class PaintBoard {
    function __construct($config) {
    // 配列に基づくロジックを実行...
}

public function render() {
    // レンダリングする為のコード...
    return $output;
}

private function createColorPalette() {
    // コード
}

// ..その他のメソッド..
}

この方法の利点はシンプルです。メインのロジックがコネクタとなるスニペットコードから分離されており、出力はプレイスホルダへ行われます。このように最適なMVCプログラミングが行えます。

また、他のスニペットやMODxで使われている変数を上書きしてしまうことを防ぎます。言うまでもなく、コードも大いにクリーンなものとなります。

外部ファイルでPHPのショートタグを使わない

多くのサーバ環境をサポートするリソースを開発するために、PHPのショートタグを使用することは避けるようにします。

悪い例
<? ... ?>
良い例
<?php ... ?>

コメント (5)

  • MEGU
  • 2008-04-08 11:46

Phizeさん。すばらしいです。
Wikiは、mediawikiなので、まとめサイトと同じなのですが(タグとかは)、えーと。本家のWikiの書き方をフォーラムに、書きます(笑)。

  • MEGU
  • 2008-04-08 12:19

書きました~。
ttp://modxcms.com/forums/index.php/topic,24563.0.html

かなりの縦長になりました(笑
日本語ページの作り方、ありがとうございます。
後でWikiの方にも転記しておきます。

  • ZeRo
  • 2008-06-02 00:44

おお、すばらしいですぅ
ちょっと、作ったやつ見直そうかなぁ

書いたかいがありました(笑
変数名とかは諸刃の剣の気がしますが、外部ファイルにわけるのはしておいたほうが後々も楽ですね。

コメントフォーム

トラックバック (0)

この記事へのトラックバックはまだありません。

この記事のトラックバックURI
http://dxd8.com/archives/107/trackback/
この記事のURI
http://dxd8.com/archives/107/