私は今回が Studio Umi ブログ初投稿となります。今後 Drupal や JS など技術系を中心におもしろトピックをご紹介していければと思いますので、どうぞよろしくお願いいたします。
今回は初投稿!ということで、張り切って「 Drupal コーディングスタンダード」を翻訳してみました。
まず最初に「 Drupal のコーディングスタンダードとは何なのか?」というところから見ていきたいと思います。
Drupal コーディングスタンダードについて
Drupal には、「 Drupal コーディングスタンダード」と呼ばれる、コードを書く上での標準ルール集が存在します。
内容は言語レベルで提唱されている各種コーディング規約とほぼ同じもので、「Drupal でコードを書くときにはこのルールを守りましょうね」という「お作法集」となっています。
その原文が Drupal.org のこちらのページで紹介されているのですが、何分英語ですので、、ちょっとしたときにパッと気軽に読むことができません。。
そして日本語版はといえば、こちらのページに日本語翻訳バージョンを載せてくださっている方がいらっしゃるのですが、、少しバージョンが古くなっているようで、最終更新が数年前という状況です。
ですのでこの度、現時点での Drupal コーディングスタンダード最新版を訳してみました。以下がその翻訳文となります。 Drupal に興味のある方はぜひご参考にしていただければと思います。 (意訳ベースになっていますので、参考にされる際はあらかじめその旨ご了承いただければ幸いです。正確なニュアンスが知りたい方はぜひ原文の方にあたってみてください。)
Drupal コーディングスタンダード 翻訳(2012年12月23日更新版)
メモ: Drupal コーディングスタンダードは Drupal とそのコントリビュートモジュールのコードに適用されるものです。これはざっくりと PEAR のコーディングスタンダードをベースにしています。コメントや名称についてはアメリカ英語のつづりで書くようにしましょう。
Drupal コーディングスタンダードはバージョンからは独立させて「常に最新のものを使おう」という考え方に則っています。新しいコードは Drupal コアのバージョンにかかわりなくすべてそのときどきのスタンダードで書くべきです。ただし、既存の古いバージョンのコードを更新する際は必ずしもそうする必要はありません。特に( Drupal コアなど)コードベースが大きい場合には、以前のコードを現在のスタンダードに合わせて更新するのは大変過ぎるかもしれません。しかし、現在のバージョンのコードは現在のスタンダードに従って書くべきです。
メモ: コーディングスタンダードの更新やクリーンアップを関係のないパッチにねじこむのはやめましょう。関係のあるコード行だけを触るようにしましょう。また、既存のコードを現在のスタンダードに合わせて更新する際は常に別々のイシューやパッチを作るようにしましょう。
コーディングスタンダードの問題(やその他いくつかのケース)に関連したコードレビューができるモジュールの情報については、このページの最下部のヘルパーモジュールセクションをご覧ください。
このページのコンテンツ
- インデント・空白
- 演算子
- キャスト
- 制御構造
- 行の長さ・折り返し
- 関数呼び出し
- 関数宣言
- クラスコンストラクタ呼び出し
- 配列
- 引用符
- 文字列連結
- コメント
- コードの読み込み
- PHPコードタグ
- セミコロン
- example URL
- 命名規約(関数・定数・グローバル変数・クラス・ファイル)
- ヘルパーモジュール
インデント・ホワイトスペース
インデントはタブではなく空白2つを使うこと。
行末には空白は置かないこと。
ファイルの改行は \n
( Unix の改行)を使い、 \r\n
( Windows の改行)は使わないこと。
テキストファイルはすべて改行ひとつで終わらせること。そうすることで、「No newline at end of file
」というパッチ警告が出なくなり、ファイルの末尾に行が追加されたときのパッチが読みやすくなります。
演算子
+ - = != == >
などの二項演算子(ふたつの値の間にくるもの)は、読みやすさのためその前後に空白を置くこと。たとえば代入は $foo=$bar;
ではなく $foo = $bar;
とすること。 ++
などの単項演算子(ひとつの値だけの演算に使うもの)は演算子と値や数値との間に空白ははさまないこと。
キャスト
(型名)
と $変数名
との間に空白をおくこと。たとえば (int) $mynumber
など。
制御構造
制御構造とは if for while switch
などを含むものです。このうち最も複雑なのが if
文なので、そのサンプルを見てみましょう。
if (条件1 || 条件2) {
アクション1;
}
elseif (条件3 && 条件4) {
アクション2;
}
else {
デフォルトのアクション;
}
(メモ:「else if
」は使わないこと。必ず elseif
を使うこと。)
制御文は制御キーワードと丸かっことの間に空白をひとつはさむこと。これは関数呼び出しと区別するためです。
中かっこは、技術的にはあってもなくてもどちらでも場合でも必ず使うようにしましょう。それらがある方が読みやすさが向上しますし、新しい行が追加されたときに論理的なエラーが入り込む可能性が少なくなります。
switch 文の場合:
switch (条件) {
case 1:
アクション1;
break;
case 2:
アクション2;
break;
default:
デフォルトのアクション;
}
do-while 文の場合:
do {
アクション;
} while (条件);
テンプレートのための別の制御文シンタックス
テンプレートでは、また別の制御文シンタックスが使えます。かっこを使わない形のものです。この場合、次のことに気をつけましょう。制御キーワードのあとの閉じかっことコロンとの間には空白はおかないこと。そして、制御構造の中の HTML/PHP はインデントされていること。
<?php if (!empty($変数)): ?>
<p><?php print $変数; ?></p>
<?php endif; ?>
<?php foreach ($配列 as $要素): ?>
<p><?php print $要素; ?></p>
<?php endforeach; ?>
行の長さ・折り返し
以下のルールを適用しましょう。 Doxygen とコメントの書き方の規約に目を通しましょう。
- 一般的に、行はすべて 80 文字以下の長さにすること。
- それよりも長い名前や関数やクラスの定義、変数宣言などを含む行の場合は 80 文字を超えてもかまいません。
- 制御構造は、読みやすくわかりやすいシンプルなものであれば、 80 文字を超えてもかまいません。たとえば
if ($something['with']['something']['else']['in']['here'] == mymodule_check_something($whatever['else'])) {
...
}
if (isset($something['what']['ever']) && $something['what']['ever'] > $infinite && user_access('galaxy')) {
...
}
// Non-obvious conditions of low complexity are also acceptable, but should
// あまり複雑なものでなければ明瞭でない条件も使ってかまいませんが
// その場合は必ずコメントをつけて、「なぜ」そのチェックがなされているのかを説明しましょう。
if (preg_match('@(/|\\)(\.\.|~)@', $target) && strpos($target_dir, $repository) !== 0) {
return FALSE;
}
- 条件は複数行にわたって書かないこと。
- 制御構造の条件では、「Most Compact Condition In Least Lines Of Code Award」(最短行数で最もコンパクトな条件で賞)を獲得しようとしてはいけません。たとえば
// こうやってはいけません!
if ((isset($key) && !empty($user->uid) && $key == $user->uid) || (isset($user->cache) ? $user->cache : '') == ip_address() || isset($value) && $value >= time())) {
...
}
代わりに、複数の条件を切り分けて個別の条件として準備する形がおすすめの方法です。そうするとそれぞれの条件に対するコメントを書くこともできるようになります。
// キーは、カレントユーザの ID に一致するときのみ valid となる
// そうしないとユーザが他のユーザのものアクセスできてしまうから
$is_valid_user = (isset($key) && !empty($user->uid) && $key == $user->uid);
// IP はセッションなりすましを防ぐためにキャッシュと一致しなければならない
$is_valid_cache = (isset($user->cache) ? $user->cache == ip_address() : FALSE);
// また、リクエストクエリのパラメータが未来であれば常に valid となる
// 宇宙はいずれ破れ崩壊するのだから
$is_valid_query = $is_valid_cache || (isset($value) && $value >= time());
if ($is_valid_user || $is_valid_query) {
...
}
メモ: このコード例はまだ少し難解です。あなたのコードを知らない人たちがそのロジックを理解することができるか、いつも自分なりに考え決断するようにしましょう。
関数呼び出し
関数は、関数名と開きかっこ、開きかっこと最初の引数との間には空白を入れずに呼び出すこと。各コンマとパラメータの間には空白を入れ、最後のパラメータと閉じかっこ、閉じかっことセミコロンの間に空白を入れないこと。こんな例があります。
$var = foo($bar, $baz, $quux);
上記のとおり、関数の戻り値を変数に代入する際の等号については、その前後に空白を1つはさむこと。関係のある代入式ブロックがある場合には、読みやすさを上げるためもっとたくさんの空白を入れてもかまいません。
$short = foo($bar);
$long_variable = foo($baz);
関数宣言
function funstuff_system($field) {
$system["description"] = t("This module inserts funny text into posts randomly.");
return $system[$field];
}
デフォルト値を持った引数は引数リストの末尾に置くこと。関数はそれが適切な値であれば必ず意味のある値を返すように心がけましょう。
クラスコンストラクタ呼び出し
引数なしでクラスコンストラクタを呼び出すときは、常にかっこをつけましょう。
$foo = new MyClassName();
これは引数を持つコンストラクタとの間で一貫性を持たせるためです。
クラス名が変数の場合は、まず変数がクラス名を取得するために評価され、それからコンストラクタが呼び出されます。共通のシンタックスを使いましょう。
$bar = 'MyClassName';
$foo = new $bar();
$foo = new $bar($arg1, $arg2);
配列
配列は各要素を分けるために(コンマのあとに)空白を入れ、キーひもづけの演算子 =>
のまわりには空白をはさむこと。
$some_array = array('hello', 'world', 'foo' => 'bar');
配列を宣言する行が 80 文字を越える場合は、各要素をインデントした形で1行ずつに分けて配置すること。
引用符
Drupal にはシングルクォートとダブルクォートの使用に関して厳しいスタンダードはありません。それが可能な場所では、各モジュール内で一貫性を保ち、他の開発者の個人的なスタイルをリスペクトしましょう。
この警告を心に留めておきましょう。パーサーがインライン変数を探す必要がない分シングルクォート文字列の方が早いことが知られています。次のふたつの場合を除いて、この使い方をすることをおすすめします。
- 「 `$header` 」 などのインライン変数の使用。
- 文字列をダブルクォートで囲うことで、シングルクォートのエスケープをしなくても良くなる翻訳文字列の場合。
"He's a good person."
などがその例にあたる。これをシングルクォートで書くと'He\'s a good person.'
になる。このようなエスケープは、言語翻訳のための .pot ファイルジェネレータによって適切に処理されない可能性があります。また、少し読みづらいところがあります。
文字列連結
読みやすさを上げるため、ドットと連結されるパーツの間には必ず空白を入れましょう。
$string = 'Foo' . $bar;
$string = $bar . 'foo';
$string = bar() . 'foo';
$string = 'foo' . 'bar';
シンプルな変数を連結するときはダブルクォートを使いその中に変数を入れても大丈夫です。その他の場合はシングルクォートを使いましょう。
$string = "Foo $bar";
連結を行う代入演算子(
.=
)を使う際は、代入演算子の前後にスペースを置きましょう。
$string .= 'Foo';
$string .= $bar;
$string .= baz();
コメント
コメントのスタンダードはこれとは別の Doxygen とコメントスタイルの規約のページで議論されています。
コードの読み込み
無条件にクラスファイルを読み込むところでは必ず
require_once()
を使いましょう。条件付きでクラスファイルを読み込むところ(たとえばファクトリメソッドなど)では include_once()
を使いましょう。どちらの場合もクラスファイルは一度だけの読み込みとなります。これらは同一のファイルリストを共有するため、混ぜこぜで使うことについては心配はいりません。 require_once()
で読み込まれたファイルが再び include_once()
で読み込まれることはありません。
メモ:
include_once()
や require_once()
は文であり、関数ではありません。読み込むファイル名の前後にかっこをおく必要ありません。
同じディレクトリやサブディレクトリからコードを読み込むときは、ファイルパスを "." で始めましょう。
include_once ./includes/mymodule_formatting.inc
Drupal 7.x とその後のバージョンでは、
DRUPAL_ROOT
を使いましょう。
require_once DRUPAL_ROOT . '/' . variable_get('cache_inc', 'includes/cache.inc');
PHP コードタグ
PHP コードを区切る場合は必ず
<!--?php ?-->
を使い、省略形の <!--? ?-->
は使わないようにしましょう。これは Drupal 準拠のためには欠かせないもので、異なる OS や環境で PHP コードを読み込むうえで最も使い回ししやすい方法です。
Drupal 4.7 については、コードファイルの末尾の
?>
は意図的に省略することに注意してください。これはモジュールとインクルードファイルにもあてはまります。この理由は次のようにまとめることができます。
?>
を削除することで 「header already sent
」 エラーや XHTML/XML バリデーション問題、その他の問題の原因となりうるファイル末尾の空白が生じる可能性をなくすことができます。ファイル末尾の
?>
はあってもなくても大丈夫。PHP.net 自体、ファイル末尾の
?>
を削除するようになっています(例: prepend.inc )。だからこれは「ベストプラクティス」と見ることもできます。
セミコロン
PHP 言語ではほとんどの行の末尾にセミコロンが必要ですが、コードブロックの末尾では省略しても大丈夫です。 Drupal コーディングスタンダードでは、コードブロックの末尾においてもセミコロンを置くことが求められます。特に、ワンラインの PHP ブロックの場合に注意が必要です。
print $tax;
-- YESprint $tax
-- NO
example URL
RFC 2606 に基づき、URL 例(example URL)にはすべて「
example.com
」を使いましょう。
命名規約(関数・定数・グローバル変数・クラス・ファイル)
関数・変数
関数と変数名には小文字を使うこと。単語は _ ひとつで分けること。さらに関数は、モジュール間の名前の衝突を防ぐため分類名・モジュール名をプレフィックスとして使うこと。
永続変数
永続変数( Drupal の
variable_get() variable_set()
関数を使って定義された変数や設定のこと)はすべて小文字で書くこと。単語は _
ひとつで分けること。モジュール間の名前の衝突を防ぐため分類名・モジュール名をプレフィックスとして使うこと。
定数
定数は必ずすべて大文字で、単語を分けるために
_
を使うこと。(TRUE FALSE NULL
のような事前に定義されている PHP 定数もこれに含みます。)また、モジュールで定義された定数の名前にはモジュール名のつづりの大文字を先頭におくこと。
Drupal 8 以降では、定数には(
define()
のかわりに) PHP のconst
キーワードを使うこと。そちらの方がパフォーマンスが良いからです。
/**
* Indicates that the item should be removed at the next general cache wipe.
*/
const CACHE_TEMPORARY = -1;
const
は PHP 式とは一緒には使えないことに注意が必要です。条件付きで定数を定義するときやリテラル以外の値を使って定義するときには define()
を使いましょう。
if (!defined('MAINTENANCE_MODE')) {
define('MAINTENANCE_MODE', 'error');
}
グローバル変数
グローバル変数を定義する必要がある場合、グローバル変数の名前は
_
ひとつで始めて、そのあとにモジュール名やテーマ名と _
をもうひとつつなげましょう。
クラス
クラスとインタフェース関連のスタンダードはすべて、ネーミングの問題も含めて、ここではなく http://drupal.org/node/608152 でカバーされています。
ファイル名
ドキュメント用のファイルはすべて、 Windows システムでの閲覧が簡単になるようにファイル名拡張子「.txt」をつけること。また、ドキュメント用のファイル名はすべて大文字とし( readme.txt ではなく README.txt )、拡張子はすべて小文字とすること( TXT ではなく txt )。
例: README.txt INSTALL.txt TODO.txt CHANGELOG.txt など。
ヘルパーモジュール
コーディングスタンダードに準拠しているかどうかをレビューするときに使えるコントリビュートモジュールやプロジェクトがいくつか存在します。
-
Coder モジュール。これは、コーダーレビューとコーダーアップデートの両方を含みます。利用するには
(他のモジュールと同じように)モジュールをインストールしてください。
ナビゲーションメニューの「Code Review」のリンクをクリックしてください。
「Select Specific Modules」のところまでスクロールしてください。
レビューしたいモジュールを選択し「Submit」ボタンをクリックしてください。
ナビゲーションのコードレビューリンクからスタートするかわりに、モジュールの admin 画面でリンクをクリックする形で特定のモジュールのコードをレビューすることもできます。
Dreditor (パッチなどをレビューするためのブラウザプラグイン)
PARevview (コーディングテストを実行してプロジェクトのアプリケーションをレビューするためのスクリプト集)
Coder Sniffer ( Drupal をロードすることなしにコーディングスタンダードのバリデーションを走らせるもの)
Grammar Parser モジュールはコードスタンダードに準拠した形でコードファイルを自動リライトする方法を提供します。おそらく Grammar Parser UI モジュールも必要になるでしょう。
API ドキュメント・コメントスタンダード
名前空間
サービスと Symfony 拡張の命名スタンダード
オブジェクト指向コード
PHP 例外
PSR-0 パス命名規約
SQL コーディング規約
テンポラリのプレースホルダとデリミタ
Twig コーディングスタンダード
文字列のための Drupal ユニコード関数を使う
E_AAL 準拠のコードを書く
Drupal SimpleTest コーディングスタンダード
Drupal マークアップスタイルガイド
CSS コーディングスタンダード
JavaScript コーディングスタンダード
・・・以上です。
いかがだったでしょうか。
カスタムモジュールを作るときにこのルールを守る必要は必ずしもありませんが、最近ますます重視されている「コードの読みやすさ」を高めるうえで、 Drupal 開発に関わる人であればぜひとも押さえておきたい内容です。
最後にリストアップされているその他関連スタンダードについても、今後機を見て翻訳していければと思います。
Drupal と Druplicon ロゴの権利は Dries Bytaert 氏に帰属します。
募集しています
スタジオ・ウミは「Drupal」に特化したサービスを提供する Drupal のエキスパートチーム。
フルリモート&フレックス制だから、働く場所を選ばず時間の使い方も自由です。
そんなワークライフバランスの整った環境で、当ブログに書かれているような
様々な技術を共に学びながら、Drupalサイト開発に携わってみたい方を募集しています。
まずはお話だけでも大歓迎!ぜひお気軽にご連絡ください。