CodeIgniterでコントローラーでbefore_filterやafter_filterするにはどうすればいいんだろう?と思い、調べてみたところ、CodeIgniterではフックが使えるらしいです。
フックとは、決められたタイミングで行われるコールバックのことです。
今回は認証機能をフックで呼んでみるような感じのサンプルを作ってみます。
- application/config/config.phpの$config[‘enable_hooks’]をTRUEに設定します。
- application/config/hooks.phpに、フックで使用するクラスを指定します。ここでは、BeforeFilterクラスを指定してみます。
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); // コントローラーのコンストラクタが終了したタイミングで呼ばれるフック名 $hook['post_controller_constructor'] = array( 'class' => 'BeforeFilter', // 呼ぶクラス名 'function' => 'is_login', // 呼ぶメソッド 'filename' => 'before_filter.php', // ファイル名 'filepath' => 'hooks' // applicationフォルダからの相対パス );
フックのタイミングは色々あります。一覧を記述します。
- pre_system
- システム実行の最初に呼ばれる。ベンチマーククラスとフッククラスだけロードされている。ルーティングはまだ。
- pre_controller
- コントローラーが呼ばれる直前に呼ばれる。すべての基本クラスのロード、ルーティング、セキュリティチェックなどが終わっている
- post_controller_constructor
- コントローラーがインスタンスされた直後。Railsのbefore_filterのタイミング。
- post_controller
- コントローラーが完全に実行された直後。Railsのafter_filterのタイミング。
- display_override
- ブラウザに送信するdisplayメソッドをオーバーライドした動きになる。処理済みのデータは$this->output->get_output()で取得可能。
- cache_override
- display_cacheメソッドの代わりに独自のメソッドを呼び出すことが可能なタイミング。独自キャッシュメカニズムとかに使える。
- post_system
- ブラウザに送信し終わったときのタイミング。ベンチマークをとるようなところ。
今回はbefore_filterのようなフックを作りたかったので、post_controller_constructorを指定しています。また、同じタイミングで複数のフックを仕掛けたい場合は、二次元配列にすることで指定できます。次のようにするとよいでしょう。
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); // コントローラーのコンストラクタが終了したタイミングで呼ばれるフック名 $hook['post_controller_constructor'][] = array( 'class' => 'Hoge', 'function' => 'foo', 'filename' => 'hoge.php', 'filepath' => 'hooks' ); $hook['post_controller_constructor'][] = array( 'class' => 'Fuga', 'function' => 'bar', 'filename' => 'fuga.php', 'filepath' => 'hooks' );
- さて、あとはフックで呼ばれる対象のクラスを作成するだけです。application/hooks/before_filter.phpを作成します。
- before_filter.phpを以下のように記述します。
<?php class BeforeFilter { private $CI; public function __construct() { // CIオブジェクトを取得 $this->CI =& get_instance(); } function is_login() { if(!$this->CI->session->userdata('logged_in') && $this->CI->uri->uri_string != '/auth/login') { // ログインしていない redirect('auth/login'); } } }
CIオブジェクトをget_instance()で取得してしまえば、あとはどうとでもなると思います。ここでは、セッションにログイン情報が入っているかを見てます。ただし、ログイン画面でもチェックしてしまうと無限ループになるので、ログイン画面以外でのチェックにしています。
Rails風なものと違って、別ファイルでフィルタ(フック)を定義するのですが、まぁ使い方はほとんど同じだなーという感じです。とりあえずこれでログインチェックをいちいち全てで行わなくて済むようになったので一安心です。