DIARY :: AROUND THE CORNER :: 20140630001
翻訳「Drupal 8: Hello OOP, Hello world!」

DIARY :: AROUND THE CORNER :: 20140630001

久方ぶりの更新となってしまいましたが、今回はこれまでとは趣向を変えて、当方としては初めての試みとなる英文記事の翻訳です。翻訳させていただいのは、Drupal の次期バージョンである Drupal 8 におけるモジュール開発方法について紹介している、drupal.org のユーザー effulgentsia 氏 による 2013 年 8 月 2 日付けのブログ記事「Drupal 8: Hello OOP, Hello world!」です(当然ですが、effulgentsia 氏は翻訳を許可してくれています)。原文は、日付的にはかなり以前のものです。しかし、Drupal 8 上での新しい開発方法に関してそれが必要とされる背景から説明がなされており、Drupal 8 における開発に関してより深い理解を得ることができるものとなっています。また、object-oriented programming(OOP)の導入に伴い、実務的には必須となる namespace(名前空間)についての考え方も主要な論点の 1 つであるため、Drupal に限らず、PHP フレームワーク一般における namespace の考え方に関する解説としても有益であるように思われます。ただし、原文公開後の規約変更に伴い一部読み替えが必要となっているため、それについては、当方により、当前書き(要点)・翻訳文中の該当箇所での訳注・別ページでの補足(詳細) の 3 カ所で説明を行っています。翻訳文には、適宜、原文を補完するための情報として、文中および文末に訳注を入れています。また、以下のリンク先ページで、当方による補足を行っています。・補足 1:Drupal、Drupal における開発の概要、及び、その中での原文の位置付けについて・補足 2:原文公開時以降の規約変更とその読み替えについて(詳細)前者は、Drupal に関する予備知識を補っていただくことを意図したもの、後者は、前述した、原文公開後の規約変更に伴い一部読み替えが必要となっていることに対する詳細説明です。さて、その懸案となる原文公開後の規約変更に伴う原文・翻訳文の一部読み替えについてですが、まずは、ここで、以下のようにその要点をまとめておきます([文中訳注 3]ではほぼ同内容を文章形式でまとめています)。
(1)原文・翻訳文は、Autoloading に関して、「PSR-0」に準拠した Drupal 8 の規約を前提として記述されている。(2)原文公開後、PHP FIG において「PSR-4」が承認され、Drupal 8 の規約も「PSR-4」に準拠したものへと変更された。(3)それに伴い(Drupal 8 においては)、原文・翻訳文の「namespace」における具体的指定、すなわち、「Drupal\hello\Controller」には何ら変更は生じない。従って、原文・翻訳文のサンプルコードの中身には全く影響はない。(4)一方(Drupal 8 においては)、「class ファイルの配置」は、新しい規約に対応するために変更する必要がある。従って、その変更内容を踏まえると、原文・翻訳文の「class ファイルの配置」についての具体的記述、すなわち、「lib/Drupal/hello/Controller/HelloController.php」は、「src/Controller/HelloController.php」と読み替える必要がある。
「Autoloading」「namespace」「class(ファイルの配置)」は原文における主題である「object-oriented programming(OOP)の導入」に関連するプログラミング用語であり、「PSR-0」「PSR-4」はそれらに関する規約、「PHP FIG」はその規約の発行主体です。それを踏まえて、原文・翻訳文の読み替えに関して最低限必要となる部分のみを抜き出すと、次のようになります。
規約変更に伴い、原文・翻訳文の「class ファイルの配置」についての具体的記述を以下のように読み替えるPSR-0 準拠の旧規約に基づくディレクトリパス&ファイル名:「lib/Drupal/hello/Controller/HelloController.php」PSR-4 準拠の新規約に基づくディレクトリパス&ファイル名:「src/Controller/HelloController.php」
なお、変更に際しての考え方は、この例においては次の通りです。
(1)PSR-0 準拠において要求された「Drupal/hello/」というディレクトリパスは、PSR-4 準拠においては不要となる。(2)PSR-0 準拠において「lib」であった class ファイル用ディレクトリの名称は、PSR-4 準拠においては「src」とする。
翻訳文の該当箇所には訳注も入れていますので、これ以上の深入りが不要でしたら、特に 補足 2 は不要です。当方は Drupal 上での開発を行う者として、昨年末に、次期バージョンである Drupal 8 の仕組みやその開発方法の把握・理解を兼ねて、私自身が開発した module の中の 1 つDrupal 8 に対応させるべく試行してみた のですが、その際に参考にさせていただいた複数の情報源の中の 1 つが、今回ここで翻訳させていただいた effulgentsia 氏のブログポストでした。当時これを読んだ時に、コメントしている他の多くの開発者同様「非常にわかりやい」という印象でしたし、また、コメント欄に「翻訳してもいいですよ」との氏のメッセージがありましたので、「だったら翻訳してみようかな」と思った次第です。そして、時は流れ、今年 4 月には、日本で初めてとなる「Drupal Camp」が「DrupalCamp in Japan Vol.1 KYOTO」として「Drupal Camp in Japan 実行委員会」さんの主催で開催され、当方も一聴講者として参加させていただいたわけですが、主催者の方々の気持ちの良いおもてなしと、オーストラリア・カナダ・フィリピン・東京・大阪・京都・福岡・沖縄などからのスピーカーの方々のプレゼンテーションに大いに刺激を受けたことで、「今回、実際に翻訳してみた」というわけです。動機としては、「若干の貢献になれば」ということでもありますが、それよりも、Drupal も含めたオープン・ソース・ソフトウエアのカルチャーに対する興味が今更ながらに高じてきたという要因が大きいように感じています。その意味で、きっかけを与えてくださった、原著者 effulgentsia 氏、Drupal Camp in Japan 実行委員会の方々とそのスピーカーの方々、そして、drupal.org 上の開発者の方々に感謝申し上げます。前置きが長くなってしまいましたが・・・以下、effulgentsia 氏のブログポストの翻訳になります。

Drupal 8: Hello OOP, Hello world!
Submitted by effulgentsia on August 2nd, 2013 at 6:50:32 PM
Hello world(世界の皆さん、こんにちは)[訳者脚注01]。これは私の最初のブログポストです。私のことを知らない方々のために簡単に自己紹介しますと、私は、drupal.org 上でのユーザー effulgentsia です。Acquia の OCTO グループ [訳者脚注02]で働いています。Drupal core[訳者脚注03]の開発にも数多く取り組んでいますし、Drupal のモジュール等の修正プログラムであるパッチを書いたりそのレビューをしたりしてもしています。
Drupal 8 の開発に取り組んでいる中で、また、様々な Drupal 関連イベントに参加する中で、私は、Drupal 8 用のモジュール開発者として成功するために学ぶ必要があるであろうことについて関心を抱いている、かなりたくさんの Drupal 7 用のモジュール開発者に会いました。Drupal コミュニティの中の何人かの方は、そうした Drupal 8 用のモジュール開発に関して、既にブログで投稿し始めています。その中には、例えば、Joe Shindelar 氏が今週始めに書いた「writing a Hello World module」があります[訳者脚注04]。
このブログポストでは、私は、もしあなたが Joe 氏のブログポストの中の動画を見てその中で題材となっているモジュールを実際にコーディングしてみたなら恐らく最初に注目するであろう事項に関して、少し深く突っ込んでみたいと考えています。その事項とは、とても簡単な仕事のためでさえ、「global function(グローバル関数)」の代わりに「namespaced PHP class(名前空間を伴った PHP クラス)」を書くということです。もし、あなたがここ最近 2 〜 3 年のうちに最初に PHP を学んだなら、もしくは、何らかのモダンなオブジェクト指向の PHP プロジェクトに取り組んだことがあるなら、ここで言及することの全ては、あなたにとって既に深くに身に付いた習慣であることでしょう。しかしながら、もしあなたが私のようであるなら[訳者脚注05]、そして、Drupal 7 やそれ以前のバージョンの Drupal での開発のためにのみ PHP を学んだのであれば、PHP におけるオブジェクト指向のベストプラクティスについてここでも少しは学ぶべきことや慣れ親しむべきことがあるかもしれません。しかしながら、皆さん、学ぶことや慣れ親しむことは何と言ってもプログラミングそのものですよね。それは、クライアントサイド、すなわち、HTML、CSS、JavaScript において、ここ最近の 3 年間超に渡りベストプラクティスがどんなに大きく変化したのかを見れば、十分お分かりのことだと思います。であるなら、サーバーサイドにおける開発も変化・進化せずに従前のままで停滞していることなどできませんよね?
ということで、さっそく始めていきましょう。まずは、Drupal 8 の前身となる、現バージョン Drupal 7 において「Hello」モジュールがどのようなものであるのかを見てみることにします[訳者脚注06][訳者脚注07]。
Drupal 7 : hello.info
name = Hello
core = 7.x
Drupal 7 : hello.module
<?php

function hello_menu() {
return array(
'hello' => array(
'title' => 'Hello',
'page callback' => 'hello_page',
'access callback' => 'user_access',
'access arguments' => array('access content'),
),
);
}

function hello_page() {
return array(
'#type' => 'markup',
'#markup' => t('Hello.'),
);
}
ここまではかなりシンプルですよね? 1 つ目のファイルは「hello.info」です。.info ファイルは、そのモジュールについての様々な情報を Drupal に認識させるためのファイルです。2 つ目のファイルは「hello.module」ファイルです[訳者脚注08]。ここでは、その中で、「hook_menu()」関数を実装しています[訳者脚注09]。この関数の中では、必要とあれば Drupal サイトにおいてデフォルトでナビゲーションの中に表示されることになる「menu link(メニューリンク)」についての指定を行うことも可能です[訳者脚注10]。もしその指定を行った場合には、そのリンクのタイトル(リンク・テキスト)は「Hello」となり、そのリンク先のアドレスは(相対パス(厳密には「drupal path」)として(以下同じ))「hello」になります[訳者脚注11]。そして、そのリンクをクリックするとかブラウザのアドレスバーにタイプするとかにより「hello」がリクエストされた場合には、「hello_page()」関数のコンテンツがリターンされることになります[訳者脚注12]。ただし、「access content」というパーミションを有するように設定されたユーザーのみが閲覧可能であるように指定しています[訳者脚注13]。
しかしながら、上記のプログラムにおいては、Drupal 7 のためでさえ改善すべき点が 2 つあります。
1 つ目は、「hello_page()」の関数定義が「hello.module」ファイルの中に置かれているため、PHP は、どのようなリクエストに対しても、「hello_page()」をメモリにロードする必要がある、という点です[訳者脚注14]。それは、ほとんどのリクエストがおそらくは「hello」以外の他のページへのリクエストであるとしても、です。そこで、次のように、「hello_page()」の定義を「hello.pages.inc」に移すことにより、必要時のみ(つまり、「hello」がリクエストされた時のみ)、「hello_page()」を読み込むようにすることができます。
Drupal 7 : hello.info
name = Hello 
core = 7.x
Drupal 7 : hello.module
<?php

function hello_menu() {
return array(
'hello' => array(
'title' => 'Hello',
'page callback' => 'hello_page',
'access callback' => 'user_access',
'access arguments' => array('access content'),
'file' => 'hello.pages.inc',
),
);
}
Drupal 7 : hello.pages.inc
<?php

function hello_page() {
return array(
'#type' => 'markup',
'#markup' => t('Hello.'),
);
}
2 つ目は、このモジュールのための自動テストが存在しないということです。それは次のことをほぼ確約するでしょう。まず、自動テスト無しなら、私は、このモジュールにバグを持ち込んでしまうことになるかもしれないということです。もしくは、私がこのモジュールを drupal.org に contribute する(寄贈する)なら、バグを持ち込んでしまうかもしれないこのモジュールに対して、他の人がパッチを送信することになるであろうということです。賢く入念な開発者であったとしても、ミスをゼロにすることはできません。そして、ざっくばらんに言えば、私は非効率的に物事を進めることが好きではありません。ですから、私が変更を加えるたびに、もしくは、他の誰かのパッチを私がレビューするたびに、手動でこのモジュールをテストするようなことはしたくありません。それよりはむしろ、私のためにそれを行ってくれる機械のような仕組みがあれば良いと思います。もし私がテストコードを書いたなら、それ以後は、レビューを必要とする全てのパッチのために、drupal.org は、自動的にそのテストコードを実行してくれるでしょう。そして、もしテストが失敗したなら、それに関するレポートとともに、その問題を「needs work(作業が必要です)」として自動的に設定・提示してくれるはずです。それらは、全て、私が眠っている間に行われます。ということで、次のコードがテストを伴ったモジュールです。
Drupal 7 : hello.info
name = Hello 
core = 7.x
files[] = hello.test
Drupal 7 : hello.module
<?php

function hello_menu() {
return array(
'hello' => array(
'title' => 'Hello',
'page callback' => 'hello_page',
'access callback' => 'user_access',
'access arguments' => array('access content'),
'file' => 'hello.pages.inc',
),
);
}
Drupal 7 : hello.pages.inc
<?php

function hello_page() {
return array(
'#type' => 'markup',
'#markup' => t('Hello.'),
);
}
Drupal 7 : hello.test
<?php

class HelloTest extends DrupalWebTestCase {

public static function getInfo() {
return array(
'name' => 'Hello functionality',
'group' => 'Hello',
);
}

public function setUp() {
parent::setUp('hello');
}

public function testPage() {
$this->drupalGet('hello');
$this->assertText('Hello.');
}

}
さあ、皆さん、いかがでしょうか。もし開発するモジュールにテストを追加するなら、Drupal 7 においては、モジュールのコードの約半分が既に object-oriented(オブジェクト指向)になりましたね!。では、いよいよ、本題である Drupal 8 ではどうなるのかを見ていくことにしましょう。初学者のために、まずは、「hello.pages.inc」ファイルにおけるコードを class の中に再構成してみることにします。それが意味するのは、私たちはまず第一に class のための名前を選ぶ必要がある、ということです。予め断っておきますが、私が選ぶであろう名前は次のようにかなり冗長なものとなります。それは「Drupal\hello\Controller\HelloController」です。これは一体何でしょうか?。なぜこんなに長ったらしいのでしょうか?。理由は以下の通りです。
理由 1 :このモジュールに「hello」という名前をつける前に、まず最初に、私は drupal.org 上で「hello」という名前が既に使われているかどうかを確認しています。なぜなら、私は、drupal.org 上にある既存のコードなどとコンフリクトを起こすようなモジュールを作りたいとは思わないからです。それはとても重要なことです。ですから、私は Drupal の世界の中で利用可能な、つまり、Drupal の世界の中で唯一となる名前を選びます。しかし、Drupal の世界の外側の、更に広い世界においてはどうでしょうか。そこには Drupal 以外にも複数の PHP オープンソースプロジェクトがあります。そして、それらのプロジェクトに取り組んでいる人々が私の class を見つけて「自分達のプロジェクトの中にそれを組み入れたい」と考えたとしたらどうでしょうか。けれども、それらのプロジェクトは、既に、彼ら自身によって開発された「hello」という名前をもつ何らかの構成要素を持っているかもしれません。そこで、私のモジュールの名前の中に「Drupal」という単語を含めることにします。その結果、Drupal の世界の外側のプロジェクトにおいてもコンフリクトを回避できるようなるわけです。しかしながら、あなたはこう思うかもしれません。「おっしゃっていることはもっともですが、Drupal 専用の「render array([訳者脚注12]参照)」を返すメソッドを 1 つ持つだけのこのように貧弱な小さな class に対して、一体誰が何の価値を見い出すというのでしょうか」、と。そうです、その指摘はまさにその通りです。けれども、現時点では確かに指摘のとおりであったとしても、将来的にはこのモジュールがより興味深いモジュールへと進化・発展することになるかもしれません。その中の class のいくつかが、より一般的な方法で実際の興味深い問題を解決することになるかもしれません。そして、それが、他の人に何らかの利用価値をもたらすことになるかもしれません。ですから私は、class の命名について次のように考えます。即ち、開発する全ての class について、完全な名前を使うか簡略化した名前を使うかどうかをケース・バイ・ケースで決めるよりも、常に他のプロジェクトにおいても使用可能となるべく命名するようにした方が実際的によりシンプルかつ確実である、と。
理由 2 :「Drupal\hello\」の後に、私はもう一つ「Controller」という名前を追加しています。「Controller(コントローラ)」とは、「MVC」という専門用語における「C」に対応するものです[訳者脚注15]。そして、それは、web アプリケーション開発フレームワークにおいては、共通的に、リクエストされた特定の URL に対してレスポンスを行うべきトップレベルのコードである、とされています。私のモジュールは、将来的には、他にもいくつかの役割を持つようになり、より多くの複数の class を含むように成長するかもしれません。そうした場合においても、名前の中に「Controller」という用語を追加してあることで、私が開発する複数の class のうちコントローラとしての機能を果たす class の全てをここでグルーピングすることができますし、それにより、それらをコントローラではない他の class から区別することができるようになるわけです。
理由 3 :名前の中の最後の「\(バックスラッシュ)」までの部分、つまり「Drupal\hello\Controller」という部分は、PHP において「namespace(名前空間)」として知られているものです。PHP のドキュメントによると、各 namespace は、概念的には、関連するアイテムをグルーピングするべく機能する、ファイルシステムにおけるディレクトリと類似のものである、とされています[訳者脚注16]。「\」で区切られた各部分は、それ自身が namespace です。つまり、各部分は階層が深くなるに従いより細分化されていくことになるわけですが、その 1 つ 1 つが各階層においてグルーピングの機能を果たすわけです。具体的には、一番最初の「Drupal」という部分ですが、これも namespace であり、Drupal プロジェクトの一部となる class の全てをグルーピングします。それは、広義には、「Drupal core」や「contrib(contributed module:drupal.org に寄贈されたモジュール)」を含みます。次に「Drupal\hello」という部分ですが、これも同様に namespace であり、私がここで Drupal のために開発している hello モジュールの構成要素となる class の全てをグルーピングします。そして、さらにその次の「Drupal\hello\Controller」という部分ですが、これもまた同様に namespace であり、この hello モジュールの中の「controller」の全てをグルーピングします。さて、この第三階層において、最も利用価値があると思われる、最も細分化された namespace に到達したわけですが、私は、さらに、今開発している class を namespace に組み入れるために、その class 自身に対して命名する必要があります。そこで、私は、今のところ、それに対して「HelloController」という名前を選んでいます。「hello」も「Controller」も、既に、この namespace の中に含まれているにも関わらず、です。というのは・・・、このモジュールが将来的に多くのページを含むように成長した時には、私は、恐らく、それらのページを、単一の controller class ではなく複数の controller class によって編成したいと考えるでしょうし、そして、その時には、各 class の機能や役割が明確になるように、その一つ一つに意味のある適切な名前を付けることができるでしょう。しかし、今のところ、このモジュールはたった 1 つの class を持っているだけですし、それに対して何らかの名前をつけるという必要に迫られているだけです。ですから、私は、今のところ、その冗長さを受け入れているというわけです。
今や、私たちは、完全に namespace の中に位置付けられた class name(クラス名)を持っています。そこで、私たちは、その class をサイトのディレクトリ構造の中に配置するために、それを格納するファイルの名称を決定する必要があります。Drupal 8 の現段階での仕様においては、開発者であるあなた自身が独自に登録したりローディングしたりするコードを書きたいと思わないのでしたら[訳者脚注17]、Drupal 8 は、そのファイル名がクラス名に適合することを要求します。その命名規約に従うと、具体的には、「lib/Drupal/hello/Controller/HelloController.php」となります。つまり、そのファイルは、モジュールのディレクトリから更に 4 段階の深さにあることが要求されるわけです。理由は以下の通りです。
[文中訳注 1(重要:規約変更に伴う読み替えについて)]:ここでの記述は原文公開時の 2013 年 8 月 2 日時点のものであり、「PSR-0」準拠であった過去の規約に基づくものです。しかしながら、前書き・補足 2 での説明の通り、その後、PHP FIG および Drupal 8 における規約が変更となり、赤字部分の「class ファイルの配置」に関する具体的指定は、「PSR-4」準拠となった現在の規約には適合しなくなっています。すなわち、現在の規約に従うと、原文・翻訳文中の「lib/Drupal/hello/Controller/HelloController.php」は「src/Controller/HelloController.php」となり、原文・翻訳文中の「4 段階の深さにある」は「2 段階の深さにある」となります。変更に際しての考え方は、この例においては次の通りです。(1)「PSR-0」準拠において要求された「Drupal/hello/」というディレクトリパスは不要となる。(2)「PSR-0」準拠において「lib」であった class ファイル用ディレクトリの名称は、(Drupal 8 では)「src」とする。
理由 1 「lib」ディレクトリは、あなたのモジュールに含まれる他のファイル(例えば、後で説明する YML ファイルや CSS ファイルなど)から、そのモジュールの PHP class を区別して管理するために必要とされます。
[文中訳注 2(重要:規約変更に伴う読み替えについて)]:繰り返しになりますが、文中訳注・前書き・補足 2 での説明の通り、赤字部分の「class ファイルの配置」に関する具体的指定は、「PSR-0」準拠であった過去の規約に基づくものであり、「PSR-4」準拠となった現在の規約には適合しなくなっています。すなわち、現在の規約に従うと、「「src」ディレクトリ」となります。
理由 2 ディレクトリパスの一部分である「Drupal/hello」は、「PSR-0」に従う必要があります。「PSR-0」とは、完全なクラス名はファイルシステム上で表されなければならないとする規約です。これは少々やっかいな規約であり、それに対して責任を有する PHP の標準化グループは、これを要求しないような新しい規約を作るべく検討しているところです。彼らが他の規約を受け入れるよりも新しい規約を作ることを優先するならば、それは「PSR-4」と命名されるはずです。現在、「PSR-4」となるであろう望ましいルールへと転換することが、Drupal core にとっての課題の 1 つとなっています。そしてそれが実現したならば、喜ぶべきことに、Drupal 8 のモジュールの中でより浅いディレクトリパスが使用可能になることでしょう。
[文中訳注 3(重要:規約変更に伴う読み替えについて)]:繰り返しになりますが、文中訳注・前書き・補足 2 での説明の通り、この段落全体の記述(特に赤字部分のファイルパス)は、「PSR-0」準拠であった過去の規約に基づくものです。その後、原文著者のここでの見通しが現実のものとなり、赤字部分の「class ファイルの配置」に関する具体的指定は、「PSR-4」準拠となった現在の規約には適合しなくなっています。仮に、現在、原著者が追記するとしたら、次のようになるものと考えられます。
このブログポストの公開は 2013 年 8 月 2 日ですが、2013 年 12 月 3 日に、原文での見通しの通り、主要な PHP Framework コミュニティをコアメンバーとする PHP FIG において、Autoloading に関する新しい規約「PSR-4」が承認されました。そして、PHP FIG のメンバーである Drupal においても、Drupal 8 の規約を「PSR-4」に準拠するように変更しています。そのため、現在では、「PSR-0」準拠では要求された「Drupal/hello」というディレクトリパス(赤字部分)は不要となり、その分だけより浅いディレクトリパスが使用可能となっています。すなわち、このブログポストにおけるサンプルコードに対応する「class ファイルの配置」は、具体的には、「PSR-0」準拠であった過去の規約においては「lib/Drupal/hello/Controller/HelloController.php」となっていましたが、「PSR-4」準拠となった現在の規約においては「src/Controller/HelloController.php」となります。
理由 3 :最初に「冗長である」と言いましたが、その次にある「Controller」ディレクトリは役に立ちます。下位の namespase としてそれを追加することの利点は、何より、このモジュールがより多くのクラスを持つまでに成長した時に、そこでクラスやファイルをグルーピングしやすくなるからです。
以上で一通りの説明を行ったことになります。ということで、ここまでの全てを踏まえると、Drupal 7 における「hello.pages.inc」ファイルは Drupal 8 においては次のようになります。
Drupal 8 : lib/Drupal/hello/Controller/HelloController.php
[文中訳注 4(重要:規約変更に伴う読み替えについて)]:繰り返しになりますが、文中訳注・前書き・補足 2 での説明の通り、赤字部分の「class ファイルの配置」に関する具体的指定は、「PSR-0」準拠であった過去の規約に基づくものであり、「PSR-4」準拠となった現在の規約には適合しなくなっています。すなわち、現在の規約に従うと、「src/Controller/HelloController.php」となります。
<?php

namespace Drupal\hello\Controller;

class HelloController {
public function content() {
return array(
'#type' => 'markup',
'#markup' => t('Hello.'),
);
}
}
あなたが Joe 氏のブログ記事におけるビデオを見たなら、Drupal core が提供する interface(インターフェース)である「ControllerInterface」を実装するために[訳者脚注18]、彼が「HelloController」を少し強化していることに気が付くことでしょう。そのような対応は、この例の場合のような、外部のコードをコールしない大変シンプルなコントローラーにとっては必須ではありません。しかしながら、OOP において重要となる「interface(インターフェース)」「dependency injection(ディペンデンシー・インジェクション = 依存性の導入)」については、私の将来のブログポストにおいてもより深く検討してみたいと考えています。さらに、そのブログポストではカバーされるであろうはずですが、Drupal 8 においては、従来の .info ファイルの内容は「.info.yml」ファイルに記述することになりましたし、従来の hook_menu() 関数の中の一部は「.routing.yml」ファイルに記述することになりました。[訳者脚注19]それらを追加すると、テスト以外の完全なモジュールは以下のようになります。
Drupal 8 : hello.info.yml
name: Hello 
core: 8.x
type: module
Drupal 8 : hello.routing.yml
hello: 
path: '/hello'
defaults:
_content: '\Drupal\hello\Controller\HelloController::content'
requirements:
_permission: 'access content'
Drupal 8 : hello.module
<?php

function hello_menu() {
return array(
'hello' => array(
'title' => 'Hello',
'route_name' => 'hello',
),
);
}
Drupal 8 : lib/Drupal/hello/Controller/HelloController.php
[文中訳注 5(重要:規約変更に伴う読み替えについて)]:繰り返しになりますが、文中訳注・前書き・補足 2 での説明の通り(「文中訳注 5」と同様)、赤字部分の「class ファイルの配置」に関する具体的指定は、「PSR-0」準拠であった過去の規約に基づくものであり、「PSR-4」準拠となった現在の規約には適合しなくなっています。すなわち、現在の規約に従うと、「src/Controller/HelloController.php」となります。
<?php

namespace Drupal\hello\Controller;

class HelloController {
public function content() {
return array(
'#type' => 'markup',
'#markup' => t('Hello.'),
);
}
}
テストを実施するまでには、モジュールの class は namespace の中に位置付けられている必要があります。それは、今のところは、規約「PSR-0」に適合するファイル名でなければいけません。つまり、それは、(1)1 つのファイルに 1 つのクラスでなければならない(2)単一のテストファイルの中に全てをひとまとめにすることはできないということを意味しています。そして、Drupal 8 へテストをポーティングするためのいくつかの他の変更もあります。それは drupal.org 上の「these change notices」で読むことができますが、ここでも以下にその要諦となるポイントを掲載しておきます。
[文中訳注 6(重要:規約変更に伴う読み替えについて)]:繰り返しになりますが、文中訳注・前書き・補足 2 での説明の通り、赤字部分の「class ファイルの配置」に関する具体的指定は、「PSR-0」準拠であった過去の規約に基づくものです。すなわち、現在の規約に従うと、「規約「PSR-4」に適合するファイル名でなければいけません。」となります。
ポイント 1 :Drupal 8 においては、あなたのモジュールのコードの多くは、今や、global function(グローバル関数)の中ではなく、class の中に配置されることになるでしょう。すべてがそうであるわけではありませんが、ほとんどはそうなります(例えば、「hello_menu()」 という hook の実装は、まだ .module ファイルの中の function であることが可能です[訳者脚注20])。このような変更は、ソフトウエアを、より「メインテナンス」「テスト」「堅牢化」「大規模化」しやくするという目的のために、「interface」「type hinting(タイプ・ヒンティング)」「dependency injection」をはじめとするOOP のベストプラクティスの技術のための舞台を整えます。ですから、これらのトピックのそれぞれをカバーするであろう将来のブログポストのためにも、引き続き、私のサイト「http://effulgentsia.drupalgardens.com」をチェックしてみてください[訳者脚注21]。
ポイント 2 :class は、「Drupal\YOUR_MODULE_NAME」から始まる namespace の中にある必要があります。これに従うことで、あなたが開発する class は、Drupal の世界の中にある他のどんなモジュールのコードとも、そして、さらに、Drupal の世界の外にあるどんなコードともコンフリクトしたりしないようになります。あなたは、他のプロジェクトがあなたが開発する class を使いたいであろうなどとは思わないかもしれません。しかしながら、あなたは、その可能性を完全にかき消すようなことをするべきではありません。結局、オープンソースとは、どんなことでも「共有する」ということなのです。ですから、もっと広く「共有する」ための機会を作っていきましょう!
ポイント 3 :その namaspace の中で、あなたは、あなた自身の概念に合致するように、さらの下位の namaspace を作ることも可能です。
ポイント 4 :それぞれのクラスは、その namaspace の構造に合致したディレクトリ構造を伴って、それ自身のファイルの中に配置します[訳者脚注22]。
私の人生における正真正銘初めてのブログポストを読んでくださってありがとうございます。今回の、そして、今後のブログポストが、あなたにとって何かしら役立つものとなるようでしたら幸いです。
[訳者脚注01]:原文本文の内容を踏まえると、ここでの「Hello world」には、以下のような、複数の意味が込められているように思われます。(1)文字通り、原著者のブログデビューに際しての挨拶。(2)本文中で、Drupal 8 の開発手法を、「Hello」モジュールを通じて紹介とすることに対する暗示。(3)既存の Drupal モジュール開発者が、Drupal の外部の世界と接近・遭遇することに対する暗示。(4)開発者を含め、Drupal の外部の人々に対して、Drupal 側から歓迎・歓待することに対する暗示。
[訳者脚注02]:「Acquia」は、Drupal そのもののオリジナルの創作者である Dries Buytaert 氏 を共同設立者として、Drupal 専門のクラウド・ホスティング・サービス等を展開している企業です。また、その OCTO グループとは、Dries 氏を補佐する、技術とマーケティングの両分野に渡る領域を担当する部門であるようです。
[訳者脚注03]:「Drupal core」とは Drupal において文字通り「核(core)」となる存在で、「補足 1」の中でも触れたように、Drupal において OS のような働きを担うファイル群のことです。リクエストに対して、レスポンスを行うための一連のワークフローをバックグランドで遂行し、その過程において、適宜「hook」と呼ばれる仕組みにより「module(モジュール)」「theme(シーム)」と連携しつつ、それらに具体的な仕事させることで、CMS・フレームワークとして一貫性のあるレスポンスを生成・管理しています。
[訳者脚注04]:Joe Shindelar 氏 のブログポストは、Drupalize.Me という Drupal 系の Web ラーニングサービスを展開する企業のサイト上にあります。その 経歴 によると、Joe 氏もまた、Drupal core の開発などに貢献されておられるようです。
[訳者脚注05]:この部分の原文は以下のようになっています。
However, if you're like me, and only learned PHP in order to develop for Drupal 7 or earlier,
私は、「if you're like me(あなたが私のようであるなら)」の部分に関して、(1)その具体的内容が何を指すのか、(2)それに続く「only learned PHP in order to develop for Drupal 7 or earlier」という部分と関係があるのか、という点について、英語文法面および事実面から、何らかの確信をもつことができませんでした。そこで、翻訳文では、「and」を単に「そして」として、原文をそのまま直訳しています。氏の経歴にも関わることですので、該当する原文を引用させていただき、ご判断いただきたく思います。ちなみに、氏は、drupal.org のユーザープロフィール欄の記述によると、2006 年頃から drupal.org のユーザーとなり、活発な開発歴を有しておられるようです。
[訳者脚注06]:プログラマの方なら『「Hello」モジュール』と言われただけですぐにおわかりになるはずですが、プログラミングの世界では、あるプログラミング言語やフレームワークなどを初学者に紹介する際には、歴史的・文化的な背景から、「Hello.」「Hello, World.」などと表示させるだけの非常にシンプルなプログラムを題材にすることが通例となっています。それは、単に形式的なものではなく、ある言語・あるフレームワークを習得している者にとっては、短時間で大きな学習効果を得られる実際的・実践的なものであります。
[訳者脚注07]:現バージョンである Drupal 7 におけるモジュール開発では、最低限、「.info」と「.module」という 2 つファイルが必須とされています。そこで、原文著者 effulgentsia 氏も、「hello」という名称のモジュールを開発するために、Drupal 7 の命名規約に従って、そのモジュールを構成する最低限の要件として、「hello.info」と「hello.module」という 2 つのファイルを作成しています。.info ファイルは、(本文にも明示的な説明がありましたが)その module についての情報を Drupal に伝える役割を、.module ファイルは、主にそのモジュールと Drupal core との連携機能部分を実装する役割を、それぞれ担っています。そして、氏は、.module ファイルの実装内容に関して、「hook」と呼ばれる Drupal core との連携部部分以外は、 (1)Drupal 7 用のモジュール開発においては「.module」以外の別ファイルとして実装することが「推奨される」(2)Drupal 8 用のモジュール開発においては「namespaced PHP class」として別ファイル化することが「規約である」という 2 点を、読者であるモジュール開発者に例示しています。
[訳者脚注08]:.module ファイルは、そのモジュールの中心的存在であり、主にそのモジュールが Drupal core と様々な連携を行うためのコードを記述するという役割を担うファイルです。
[訳者脚注09]:hook_menu()」関数は、Drupal 7 においては、各モジュールがルーティング等の設定を行うための機能を担います。そして、その名称は、Drupal の命名規約に従い、このサンプルコードにおいては「hello_menu()」となります。
[訳者脚注10]:ただし、このサンプルコードにおいては「menu link」については指定されていません。
[訳者脚注11]:例えば、サイトの URL が「www.example.com」だとすると、このサンプルコードにおける完全な URL は、「www.example.com/?q=hello」もしくは「www.example.com/hello」になります(以下同じ)。それは、そのサイトにおいて Drupal の「clean URL」機能が有効化されているかどうかに依存します(有効なら後者になります)。そして、ここでの「hello」は、開発上、「drupal path」と呼ばれるものに該当します。
[訳者脚注12]:このサンプルコードにおいては、「hello_page()」関数は 2 つの要素をもつ連想配列を返すようになっています。出力のためのこのような連想配列を Drupal の世界では「render array(もしくは「renderable array」)」と呼びます。そして、その具体的指定から、この例では、実際のページには、「Hello.」という文字列を受け取った drupal コアの「t()」関数がリターンする結果がそのまま表示されるようになっています。「t()」関数は、Drupal においてトランスレーション機能を担う汎用関数で、別途、具体的な翻訳設定ファイルを作成・配置することで、例えば英語を主言語としたサイトにおいて多言語対応として日本語ページ用に「こんにちは」と出力させるなど、コンテンツを様々な言語にローカライズやカスタマイズして表示させることを可能とするものです。このようなトランスレーションを考慮しないならば、このサンプルコードにおいて、出力は、単に「Hello.」という文字列そのものとなります。
[訳者脚注13]:このサンプルコードにおいては、各ユーザーのパーミッションを判定するために、Drupal のデフォルトのアクセス管理関数である「user_access()」が指定されています。
[訳者脚注14]:「hello_page()」関数が毎回メモリにロードされるのは、サイトにおいて有効化された全てのモジュールの「.module」ファイルがリクエストの都度毎回読み込まれることになるためです。
[訳者脚注15]:「MVC」は、周知の通り、それぞれ「Model」「View」「Controller」を意味しています。Drupal と「MVC」という概念との関係については色々な見解があり、Drupal は「MVC」よりも「PAC」という概念との親和性が高いという観方もあるようです。それはさておき、補足 1 でも触れましたが、Drupal においては、Drupal core 上で、「module」が主に「ビジネスロジック面(機能面)」の仕事を、「theme」が主に「プレゼンテーション面(表示面)」の仕事を担当することが期待されています。しかしながら、Drupal 7 においては、デフォルトのテンプレートエンジンが「PHP Template」であり、theme 内で画面表示用の最終的な html を構築するテンプレートファイルが「.php」ファイルであるため、仕組み上は、(全く非推奨ですが)そこで機能面について本来は module が行うべき仕事を実装・実行することも可能であったわけです。それに対して、Drupal 8 では、テンプレートエンジンとして「twig」が採用されました。「.twig」ファイルではほぼプレゼンテーションに特化した仕事に限定されることになりますので、Drupal 8 においては、module(ビジネスロジック面)と theme(プレゼンテーション面)との役割分担はより明確・厳密になっています。
[訳者脚注16]:原文著者が言及している「PHP のドキュメント」とは、「名前空間の概要英語版)」であると思われます。
[訳者脚注17]:具体的には、プログラミングの中身のことになりますが、Drupal 8 に実装された Autoloading 機能を利用せずに、開発者自身が、そのプログラムの中で自ら「require」「include」により class ファイルを読み込むということです。
[訳者脚注18]:原文ページのコメント欄に読者の方からの投稿もあるように、「ControllerInterface」は「ContainerInjectionInterface」へと名称変更となっているようです(参照)。
[訳者脚注19]:当翻訳文公開の 2014 年 6 月 30 日時点において、Drupal 8 では、「hook_menu」関数については、ここで言及されているルーティング機能だけでなく、これまで担ってきたその機能の全てを別の設定ファイルに移行するものとされています。Drupal 8 そのものの開発はまだ「API completion phase」であり、今後も変更はあり得ることから(後々の混乱を避けるために)ここでは詳細は省きますが、現段階においては、「.module」ファイルにおいて「hook_menu」関数の実装はもはや不要となっています。この点を踏まえてここでのサンプルコードについて言えば、具体的には、Drupal 7 用コードにおいて「hello_menu」関数はルーティング機能のみを担っていたため、Drupal 8 用コードにおいて現状に追加するファイルやコードは何もありませんし、原文・翻訳文のサンプルコードのように「.module」ファイルに「hello_menu」関数が記述されていたとしても、そのままでも動きます。しかし、より正確には、Drupal 8 用コードにおいては、現段階では「hook_menu」という「hook」そのものが存在しないために、「.module」ファイル上の「hello_menu」関数はコールされることが無い、つまり、その記述は何の意味も持たないということであり、したがって、「.module」ファイルから「hello_menu」関数の記述を削除するということが本来的対応ということになります。
[訳者脚注20]:上記[訳者脚注19]での説明の通り、当翻訳文公開の 2014 年 6 月 30 日時点において、Drupal 8 では、「hook_menu」関数はこれまで担ってきたその機能の全てを別の設定ファイルに移行するものとされており、その結果、「hook_menu」という「hook」そのものが存在しないことになっています。
[訳者脚注21]:その後、原文著者のサイトでは、2013 年 8 月 12 日に、2 つめの投稿「Drupal 8: Forms, OOP style」が行われていますが、それ以後の更新はないようです(当翻訳文公開の 2014 年 6 月 30 日時点において)。
[訳者脚注22]:この部分も、狭義には、すなわち、厳密には「PSR-0」を前提した記述であると考えられます。しかし、「PSR-0」「PSR-4」などに準拠する必要がない場合には、つまり、PHP の言語仕様のみに従う場合には、「namespace」と「ディレクトリ構造」にはどんな対応関係も要求されない(即ち、開発者が自由に配置できる)ことを鑑みると、広義には、「namespace」と「ディレクトリ構造」の関係に一定のルールが要求される「PSR-4」においても成り立つ記述であると言えます。

TAG 1 ::
DIARY :: AROUND THE CORNER :: Drupal
ARCHIVES 201406