Umi Blog
ウミブログ
前回の私の投稿では Drupal のコーディングスタンダードを翻訳してみました。 Drupal のコーディングスタンダードは前回ご紹介したもの以外にも、 オブジェクト指向、HTML、CSS、JS などさまざまな範囲のものが提案されています。
今回も前回に引き続き、数ある Drupal スタンダードの中から、オブジェクト指向に関するスタンダードを翻訳してご紹介できればと思います。
Drupal オブジェクト指向スタンダード、原文はこちらです。 Object-Oriented Code | Drupal.org
以下、日本語訳になります。2013年12月20日更新分を元にしています。
日本語としてのわかりやすさを重視し、ところによっては直訳、ところによっては意訳にしていますので、参考にされる際はその点ご認識いただければと思います。原文の厳密なニュアンスが知りたい方は原文の方にあたってみてください。
Drupal は一般的なオブジェクト指向コードのための PHP 規約や確立された産業ベスト・プラクティスをフォローしています。ただし、いつものとおり Drupal 特有の考慮ポイントがあります。
オブジェクト指向にも適用可能な インデント の基本についてのセクションも参照してください。オブジェクト指向特有のポイントというのはほんのわずかです。
クラス/インタフェースの定義の始まりとプロパティ/メソッドの定義の始まりとの間の空行は削除しましょう。
<?php
class GarfieldTheCat implements FelineInterface {
public function meow() {
...
...
...
プロパティの定義の終わりとメソッドの定義の始まりの間の空行は削除しましょう。
<?php
...
...
...
protected $lasagnaEaten = 0;
public function meow() {
return t('Meow!');
メソッドの終わりの部分とクラスの定義の終わりの部分の間の空行は削除しましょう。
<?php
class GarfieldTheCat implements FelineInterface {
...
...
...
public function eatLasagna($amount) {
$this->lasagnaEaten += $amount;
}
}
スタンドアロンな名前の例:(訳注:ここではリストで表示していますが原文ではテーブル表記になっています)
名前空間: Drupal\Core\Cache\
名前空間: Drupal\entity\
名前空間: Drupal\comment\Tests\
クラス/インタフェース/メソッド名の完全な例:
<?php
interface FelineInterface {
public function meow();
public function eatLasagna($amount);
}
class GarfieldTheCat implements FelineInterface {
protected $lasagnaEaten = 0;
public function meow() {
return t('Meow!');
}
public function eatLasagna($amount) {
$this->lasagnaEaten += $amount;
}
}
コードを後々拡張する際の柔軟性が高まるので、クラスの実装とは別にインタフェースを定義することは強く推奨されます。分離されたインタフェースの定義は、ドキュメントをコンパクトにまとめ読みやすさも向上してくれます。すべてのインタフェースには、定番のドキュメントスタンダードに従ってドキュメントをもれなく作りましょう。
いつか将来クラスの実装が変わる可能性が少しでもあるのであれば、メソッドの定義をフォーマルなインタフェースに分割するようにしましょう。継承して使うことを意図したクラスについては、それを extend して使うベースクラスを作るよりも、 implement するインタフェースを提供するようにしましょう。
クラスを extend したりインタフェースを implement したりする場合は、 .inc ファイルを使って .info ファイルの中で files[] で指定するようにしましょう。クラスを extend したりインタフェースを implement したりするファイルを include したとき、親となるクラスやインタフェースが読み込まれていなければ PHP はフェイタルエラーを返します。ですので、コントリビュートモジュールや場合によってはコアによってクラスが提供されている場合は、 .module ファイルの中にクラスを置くのは安全ではありません。 .inc ファイルを使って .info ファイルの中で files[] を使う方法の方がベターです。たとえば、モジュールが依存対象を持つ場合でも、あなたの .module ファイルが読み込まれているときにモジュールとその依存先を無効にすることは可能です。レジストリが無効化されたモジュール内のクラスをオートロードすることはないため、これはエラーを発生させます。また、 hook_boot() が呼ばれたとき、モジュールの依存先は読み込まれません。ですので、クラスを追加し、のちほど hook_boot() を実装すれば、あなたのモジュールは依存関係を伴わずに読み込むことができてしまい、フェイタルエラーを生成します。これらのエラーを防ぐには、 .inc ファイルを使い .info ファイルの中で files[] を使うことが必要となります。
クラスのメソッドとプロパティにおいては必ず public や protected 、 private などの可視性を指定しなければなりません。 PHP 4 スタイルの「 var 」宣言は使ってはいけません。
public プロパティの使用は非推奨です。 public プロパティの使用は副作用をもたらすためです。また、実装の詳細を外部に晒してしまい、クラスの実装の変更を困難にしてしまうからです(これはオブジェクトを使う大きな理由のひとつです)。プロパティはクラス内部のものと考えましょう。
PHP は、関数やメソッドの引数に対して、クラスや配列などのタイプ指定(型指定)をオプションでサポートしています。「タイプヒンティング」と呼ばれますが、これは特定のタイプであることを必須条件とします。特定のタイプにあわないオブジェクトが渡されたら、タイプヒンティングはフェイタルエラーを発生させます。
関数やメソッドが特定のインタフェースに一致することを仮定しているなら、必ずタイプを指定するようにしましょう。必要なインタフェースを指定すると、適切でない値が渡されたときにより有用なエラーメッセージが表示されるようになり、デバッグがかんたんになります。タイプヒンティングで指定タイプとしてクラスを使うのはやめましょう。タイプを指定するときは、必ずインタフェースを指定するようにしましょう。そうすることで、必要に応じて他の開発者が既存のコードに手を加えることなくそれぞれの実装を提供することが可能となります。
例:
<?php
// まちがい:
function make_cat_speak(GarfieldTheCat $cat) {
print $cat->meow();
}
// 正解:
function make_cat_speak(FelineInterface $cat) {
print $cat->meow();
}
クラスを直接作るやり方はおすすめではありません。かわりに、適切なオブジェクトを生成して返すファクトリ関数を使いましょう。これにはふたつのメリットがあります:
PHP は関数やメソッドから返されたオブジェクトの「チェイン」を許しています。つまり、戻り値オブジェクトのメソッドがすかさず呼ばれることを許しています。
<?php
// チェインしないバージョン
$result = db_query("SELECT title FROM {node} WHERE nid = :nid", array(':nid' => 42));
$title = $result->fetchField();
// チェインしたバージョン
$title = db_query("SELECT title FROM {node} WHERE nid = :nid", array(':nid' => 42))->fetchField();
一般的なルールとして、メソッドが $this を返すようにすればチェイン可能となり、論理的に異なる値が返されることはありません。よくある例は、特定の状態やオブジェクトのプロパティをセットするメソッドです。このタイプのメソッドでは TRUE/FALSE や NULL を返すよりも $this を返す方がよいでしょう。
・・・以上です。
いかがだったでしょうか?
現在主流の Drupal 7 では今回ご紹介したオブジェクト関連のスタンダードを意識する機会はあまりありませんが、次のリリースとなる Drupal 8 ではオブジェクト指向を本格的に取り入れた実装になるようですので、このあたりのスタンダードもぜひしっかりと押さえていきたいところです。
小さなご意見も、私たちにとっては大きなヒントです!
ぜひ率直なご感想をお寄せください
この

Masters of Drupal Engineering.
人に、