アイキャッチ画像: デスクの上でパソコンを使用している

今回は Drupal (ドルーパル)の特徴的なコンセプトのひとつ「フック」について解説してみたいと思います。

本題に入る前にまずは前提から。

前提

今回の想定読者は PHP その他の言語で開発をしたことがある開発者 / プログラマの方です。 コードのお話が出てきます。

また、今回の説明は本記事執筆時点での最新安定版 Drupal 7 を前提としています。 Drupal 8 では Drupal のコアとやりとりする仕組みがフック以外にも導入される予定なので、 Drupal 8 の場合はフックの説明も少し変わるかもしれません。 この記事はあくまでも Drupal 7 ベースの解説であることをご認識いただければと思います。

では早速フックとは何ぞやの説明から始めてみます。

フックとは?

Drupal におけるフック( hooks )とは、「 Drupal のモジュールが他のモジュールや Drupal のコアとの間でやりとりをするための仕組み」のことです。

具体的には、指定されたパターンの名前の関数を書けばそれが「フック関数」として認識され所定の場所で呼び出されるという仕組みになっています。 考え方としては、オブジェクト指向のデザインパターンでいうところの「オブザーバーパターン」( observer pattern )と同等のものと考えるとよろしいかと思います。

ことばでの説明だけだとわかりづらいかと思いますので、例をご覧いただきましょう。

フックのサンプル: node_view その 1

Drupal サイトに「 umimodule 」という名前のモジュールが有効化された形で存在するものとします。 その本体ファイル( umimodule.module )の中に次のコードを追加してみましょう。

/**
 * umimodule の node_view フック関数
 */
function umimodule_node_view($node, $view_mode, $langcode) {
  drupal_set_message('Here is umimodule_node_view. Your are seeing node: ' . $node->nid);
}

追加して保存した後にコンテンツ(=ノード)のページを開くと、次のように「 Here is ... 」というメッセージが表示されるようになります。

コンテンツ以外のページ(ユーザのページなど)ではこのメッセージは表示されません。

今回 umimodule_node_view という名前の関数を書いてその中に走らせたい処理を書くと、 Drupal が自動的にこれを認識しコンテンツが表示されるタイミングで実行してくれました。 関数名が固定ではなくモジュール名がプレフィックスとして付いているのに、自動で認識され読み込まれました。

これが、フックの基本的な仕組みです。 モジュール名_node_view という名前の関数を書くと、コンテンツが表示されるタイミングで自動で実行してくれます。 Drupal の既存のコードを書き換える必要は一切ありません。

ちなみに関数内で利用されている drupal_set_message() というのはページにメッセージを表示するための関数です。 このあたりは今回重要ではないのでスルーしていただければと思います。

フックのサンプル: node_view その 2

試しに、続けて「 umidevel 」という別のモジュールを作成して有効化し、その本体ファイル( umidevel.module )の中に次のコードを追加してみましょう。

/**
 * umidevel の node_view フック関数
 */
function umidevel_node_view($node, $view_mode, $langcode) {
  drupal_set_message('Here is umidevel_node_view. Your are seeing node: ' . $node->nid);
}

すると次は、コンテンツのページに「 Here is ... 」のメッセージが umimodule_node_view のメッセージとはまた別に表示されるようになります。

こちらもコンテンツ以外のページでは表示されません。

複数のモジュールで定義した xxx_node_view という関数がコンテンツ表示のタイミングでともに実行されるようになりました。 他にもモジュールを作成して xxx_node_view という名前の関数を作ると、さらに処理が追加されていきます。 これも Drupal フックの大きな特徴です。

ということで、いったんまとめます。

Drupal のフックとは開発者が任意の処理を Drupal システムの特定のタイミングに差し込める仕組みのことです。 jQuery をご存知の方であれば、 PHP 上で jQuery でやるような次のような処理をするイメージに近いと考えると理解しやすいかもしれません。

// ノードが読み込まれたタイミングで Here is ... と表示
$('.all-nodes').on('load', function(event){
  console.log('Here is ...');
});

今回のサンプルではコンテンツが表示されるときに実行される「 node_view 」という名前のフックを例にあげましたが、 Drupal のコアにはそのほかにも多数のフックが用意されています。 コアのフックはあらゆる箇所に用意されており、たとえば以下のようなタイミングで任意の処理を行うことができます。

  • コンテンツが保存されるタイミング
  • ユーザがログインしたタイミング
  • ブロックが表示されるタイミング
  • ページが表示されるタイミング

ここまでのところで、フックの基本的な説明はひとまず終了です。

フックを理解するときの注意点

上述のとおり、フックとは開発者が任意の処理を Drupal システムの特定のタイミングに差し込める仕組みという理解で基本的には問題ないのですが、フックにはもうひとつの側面があります。 それが、冒頭で述べた「 Drupal のモジュールが他のモジュールや Drupal のコアとの間でやりとりをするための仕組み」という側面です。

「処理を差し込む」というのがフックの本来の働きですが、それが一見フックっぽくない場面でも使われておりそれが Drupal 入門者のフックの理解を妨げる要因のひとつになっているように思います。 たとえば、 Drupal 7 では「テーブルスキーマの定義」や「ルーティングの設定」といったフックとは無関係と思えるところの設定もフックを使って行うことになっています。 私自身 Drupal に入門したての頃は「それをフックって呼ぶの?」と大きな違和感を覚えました。

このあたりがあまりに独自に進化しすぎた(ドルパゴス?)ということで、 Drupal 8 ではもう少し PHP スタンダードに寄り添ったスタイルに変えていこうとの試みがなされています。

フックの応用

あとひとつだけ、 Drupal のフックの特徴をあげておきます。

それは、開発者がフックを独自に定義できるというポイントです。 開発者は自作のモジュールの中でオリジナルのフックを作成し、他のモジュールが自由に処理を差し込めるチャンスを作ることができます。

実際、汎用性の高い定番コントリビュートモジュールではオリジナルのフックが用意されていることがほとんどです。 開発者は、 Drupal のコアだけでなく公開されているコントリビュートモジュールに対しても、それらのコードを書き換えることなく処理の差し込みを行うことができます。

以上で「フックとは何ぞや」というところがざっくりとご理解いただけたのではないかと思います。

最後に、 Drupal コアが提供する代表的なフックをいくつかご紹介したいと思います。 フックを指すときには hook_node_view というような形で、モジュール名を入れる部分に「 hook 」を入れた形で表す形が一般的です。

具体的なフックの定義方法はまた機会を見つけてご紹介できればと思います。

Drupal コアの代表的なフック

hook_init

hook_init はページの表示前に実行されるフックです。 キャッシュされていないすべてのページの表示直前に実行したい処理があるときに利用できます。

hook_node_view

hook_node_view はコンテンツ(=ノード)表示前に実行されるフックです。 表示内容を追加したい、変更したいといった場面で利用することができます。

hook_form_alter

hook_form_alter は Drupal が HTML フォームの描画データを用意した後、ページに表示する前に実行されるフックです。 この関数に渡された引数を変更することによって、フォームのフィールドや説明テキストを追加したい場合などに利用できます。

hook_menu

hook_menu はルーティングの設定を行うためのフックです。 Drupal がルーティングの設定( URL とその処理を担う関数の対応づけ)を再構築するときに各モジュールの hook_menu が呼び出され、それぞれの戻り値をもとにルーティング設定がデータベースに保存されます。 ちなみに Drupal 7 ではメニューの設定もあわせてこの hook_menu の中で行います。

次の記事で hook_menu の利用例をご紹介しているので興味のある方はご覧になってみてください。

hook_menu_alter

hook_menu_alter はルーティングの設定を変更するためのフックです。 hook_menu で定義されたルーティングの設定の読み込みがひととおり終わった後、それらがデータベースに保存される前に呼び出されます。 これを利用すれば Drupal コアや他のモジュールが行ったルーティングやメニューの設定を変更することができます。

hook_schema

hook_schema はデータベーステーブルのスキーマを定義するためのフックです。 モジュールの有効化時に呼び出され、所定のフォーマットで正しいスキーマ定義が返された場合にはそれがデータベース上のテーブルとして作成されます。

コアが提供するフックが一覧になったページがありますので、興味のある方はよろしければ。

おわりに

以上です。 いかがだったでしょうか?

今回は Drupal の中心的な概念である「フック」についてご紹介してみました。 仕組みとしても用語としても少し独特な感じがするので、他の CMS やフレームワークをふだんお使いの方にとっては最初はなかなかとっつきづらいのではないかと思います。

ただ、本格的な Drupal 開発を行う場合はもちろん、既存の Drupal サイトをほんの一部変更したい、処理を追加したい、といった場合にも、このあたりのフックについての理解は必要になってきます。 いったん理解するとシンプルかつとても便利な仕組みなので、興味のある方は以下のリンクなども参考にしながらぜひぜひ活用してみてください。

また機会を見つけて、もう少し踏み込んだフックの使い方などをご紹介していきたいと思います。