ログインサービス
それでは、実際にS2Containerを使いながら説明していきます。ここでは、サンプルとしてコマンドラインから使用するログインサービスを作成します。ユーザーはコマンドラインからユーザー名とパスワードを入力します。ログインサービスは入力された情報でユーザー認証を行いその結果を表示します。
セットアップ
S2Containerをセットアップページに従ってインストールします。ダウンロードパッケージとして、PEARパッケージとフルパッケージの2種類が用意されています。PEARパッケージをダウンロードし、「pear install」コマンドでインストールする方法が簡単です。インストール後、S2Containerを使用するためには__autoload
関数の設定が必要になります。こちらもセットアップページに__autoload
関数のサンプルが掲載されているので参照ください。
インターフェイスを定義する
今回は、ログインサービスと認証ロジックに分けて問題を考えます。
ログインサービス
ログインサービスのインターフェイス名はLoginServiceとします。ログインサービスは、ログインに必要な情報を取得し、認証ロジックに認証を委譲します。最後に取得した情報および認証結果をまとめた情報を返します。
認証ロジック
認証ロジックのインターフェイス名はAuthLogicとします。認証ロジックは、渡された情報を元にユーザー認証を行います。ユーザー認証の方法はいろいろ考えられます。ここでは、最初にシンプルなテキストマッチを行う認証ロジックを作成し、後ほど、INI形式のパスワードファイルを用いる認証ロジックに差し替えることにします。
作成するファイル
login.php
login.php がメインの実行ファイルです。処理の流れは次のようになります。
- S2Containerを使用するための設定
- login.diconからS2Containerを生成
- S2Containerからログインサービスを取得
- ログインサービスのログイン処理を実行
- ログイン結果のメッセージを表示
実際のファイルは次のとおりです。
<?php /** * S2Container.PHP5 設定 */ require_once('s2setup.inc.php'); /** * S2Containerインスタンスの生成 * ログインサービスの取得 * ログイン処理の実行 */ $container = S2ContainerFactory::create('login.dicon'); $loginService = $container->getComponent('loginService'); $userInfo = $loginService->login(); /** * ログイン結果の表示 */ print PHP_EOL; if ($userInfo->isAuth()) { print "hello {$userInfo->getUsername()} " . PHP_EOL; print " ( login {$userInfo->getDate()} )" . PHP_EOL; } else { print "sorry login failed." . PHP_EOL; } ?>
<?php /** * include_pathの設定 */ ini_set('include_path', dirname(__FILE__) . PATH_SEPARATOR . ini_get('include_path')); /** * S2Container.PHP5をPEARでインストールした場合の初期設定 */ require_once('S2Container/S2Container.php'); function __autoload($class = null) { if($class != null) { include_once("$class.class.php"); } } ?>
LoginService.class.php
LoginService
インターフェイスでは、ログインサービスを提供するlogin
メソッドを定義します。login
メソッドはログイン情報および認証結果をまとめたUserInfo
クラスのインスタンスを返します。
実際のファイルは次のとおりです。
<?php /** * ログインサービス インターフェイス */ interface LoginService { /** * ログイン処理を実行します。ログイン情報と認証結果を * UserInfoインスタンスに格納し返します。 * * @return UserInfo */ public function login(); } ?>
ConsoleLoginService.class.php
LoginService
インターフェイスの実装であるConsoleLoginService
クラスを作成します。ConsoleLoginService
クラスは、標準入力からユーザー名とパスワードを取得し、ユーザー認証を行います。入力情報と認証結果をUserInfo
クラスのインスタンスに格納して返します。
ユーザー認証はAuthLogicに委譲するため、AuthLogicの実装クラスのインスタンスを保持するauthLogic
プロパティを用意します。また、authLogic
プロパティ用のセッターメソッドを実装し、その引数をAuthLogic
インターフェイスでType Hintingします。これにより、S2Containerの自動バインディング機能を用いたセッター・インジェクションが可能となります。
認証については、telnetやsshでのログインと同じように複数回トライできるようにします。トライできる回数を設定するtryCount
プロパティを用意し、そのセッターメソッドを実装します。トライ回数はdiconファイルのproperty
タグで設定し、コンテナにインジェクションしてもらいます。
実際のファイルは次のとおりです。
<?php /** * 標準入力からログイン名とパスワードを取得し、ログイン処理を行います。 */ class ConsoleLoginService implements LoginService { /** * AuthLogicの実装クラスインスタンス用プロパティ * @var AuthLogic */ private $authLogic; /** * 認証処理をトライできる回数を指定するプロパティ * @var integer */ private $tryCount = 1; /** * @see LoginService::login() */ public function login() { $userInfo = new UserInfo(); for ($i = 0; $i < $this->tryCount ; $i++) { print PHP_EOL; print "username : "; $username = trim(fgets(STDIN)); print "password : "; $password = trim(fgets(STDIN)); if ($this->authLogic->auth($username, $password)) { $userInfo->setAuth(true); $userInfo->setUsername($username); $userInfo->setLoginTime(time()); break; } else { print " Uummm... Try again !! " . PHP_EOL; } } return $userInfo; } /** * authLogicプロパティのセッターメソッドです。 * @param AuthLogic $authLogic */ public function setAuthLogic(AuthLogic $authLogic) { $this->authLogic = $authLogic; } /** * tryCountプロパティのセッターメソッドです。 * @param integer $val */ public function setTryCount($val) { $this->tryCount = (integer)$val; } } ?>
AuthLogic.class.php
AuthLogic
インターフェイスでは、ユーザー認証を行うauth
メソッドを定義します。auth
メソッドはユーザー名とパスワードを引数にとります。実際のファイルは次のとおりです。
<?php /** * 認証ロジック インターフェイス */ interface AuthLogic { /** * 認証処理を実行します。 * * @param string $username ログイン名 * @param string $password パスワード * @return boolean 認証に成功した時に true を返します。 * 認証に失敗した時に false を返します。 */ public function auth($username, $password); } ?>
SimplePasswdAuthLogic.class.php
AuthLogic
インターフェイスの実装であるSimplePasswdAuthLogic
クラスを作成します。SimplePasswdAuthLogic
クラスはauth
メソッドを実装し、その引数であるユーザー名とパスワードでユーザー認証を行います。認証結果はbooleanを返します。ユーザー名が「foo」、パスワードが「bar2006」の場合にのみtrueを返します。
実際のファイルは次のとおりです。
<?php /** * 固定のユーザー名とパスワードでユーザー認証を行います。 */ class SimplePasswdAuthLogic implements AuthLogic { /** * @see AuthLogic::auth() */ public function auth($username, $password) { if ($username == 'foo' and $password == 'bar2006') { return true; } return false; } } ?>
UserInfo.class.php
UserInfo
クラスはログイン情報と認証結果を管理します。ログイン情報は、ユーザー名とログインした時の時間(time
関数の戻り値)とします。認証結果はbooleanです。
実際のファイルは次のとおりです。
<?php /** * ログイン情報とユーザー認証結果を管理します。 */ class UserInfo { /** * ユーザー認証結果プロパティ * @var boolean */ private $auth = false; /** * ログインユーザー名プロパティ * @var string */ private $username = null; /** * ログインした時間を設定するプロパティ * @var integer */ private $loginTime = -1; /** * authプロパティのセッターメソッドです。 * @param boolean $val */ public function setAuth($val) { $this->auth = $val; } /** * ユーザー認証結果を返します。 * @return boolean */ public function isAuth() { return $this->auth; } /** * usernameプロパティのセッターメソッドです。 * @param string $name */ public function setUsername($name) { $this->username = $name; } /** * usernameプロパティのゲッターメソッドです。 * @return string */ public function getUsername() { return $this->username; } /** * loginTimeプロパティのセッターメソッドです。 * @param integer $val */ public function setLoginTime($val) { $this->loginTime = $val; } /** * loginTimeプロパティのゲッターメソッドです。 * @return integer */ public function getLoginTime() { return $this->loginTime; } /** * loginTimeプロパティ値より、日付と時間を返します。 * @return string */ public function getDate() { return date('Y-m-d H:i:s',$this->loginTime); } } ?>
login.dicon
loginダイコンファイルには、2つのコンポーネントを定義します。
- loginServiceコンポーネント
- authLogicコンポーネント
class
属性にConsoleLoginService
クラスを指定します。また、ログインのトライ回数をproperty
タグで設定します。property
タグのname
属性にプロパティ名tryCount
を設定し、property
タグのボディに回数を指定します。ここではトライ回数は3回までとしました。class
属性にSimplePasswdAuthLogic
を指定します。実際のファイルは次のとおりです。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN" "components21.dtd"> <components> <component name="loginService" class="ConsoleLoginService"> <property name="tryCount">"3"</property> </component> <component name="authLogic" class="SimplePasswdAuthLogic"/> </components>
実行
login.phpをコマンドラインから実行すると、ユーザー名とパスワードの入力が求められます。ユーザー名に「foo」、パスワードに「bar2006」と入力するとログインが成功した旨のメッセージが表示されます。ユーザー名またはパスワードが違う場合は、認証に失敗したメッセージが表示され、再度ユーザー名とパスワードの入力が促されます。ここでは3回認証に失敗するとログインが失敗になります。
C:\CodeZine>php login.php username : foo password : bar Uummm... Try again !! username : foo password : bar2006 hello foo ( login 2006-07-23 12:00:00 ) C:\CodeZine>
AuthLogic実装の変更
次にユーザー固定のSimplePasswdAuthLogic
の実装変更として、ユーザー名とパスワードをINIファイルから読み込むことができるIniPasswdAuthLogic
を実装します。
IniPasswdAuthLogic.class.php
IniPasswdAuthLogic
は、セクション無しのINIファイルに記述されている複数のユーザー名とパスワードで認証を行います。サンプルのINIファイルは次のとおりです。
; ユーザー名 = "パスワード" hoge = "abcd1234" foo = "bar2006"
パスワードファイルはdiconファイルで指定できるようにします。IniPasswdAuthLogic
にpasswdFile
プロパティを用意し、そのセッターメソッドを実装します。実際のファイルは次のとおりです。
<?php /** * INIパスワードファイルに記述されているユーザー名とパスワードで * ユーザー認証を行います。INIパスワードファイルは、セッション名はなく、 * ユーザー名 = "パスワード" * の形式とします。 */ class IniPasswdAuthLogic implements AuthLogic { /** * INIパスワードファイルへのパス設定プロパティ * @var string */ private $passwdFile; /** * @see AuthLogic::auth() */ public function auth($username, $password) { $passwords = $this->getPasswords(); if (array_key_exists($username, $passwords)) { if ($passwords[$username] == $password) { return true; } } return false; } /** * passwdFileプロパティのセッターメソッドです。 * @param string $passwdFile */ public function setPasswdFile($passwdFile) { $this->passwdFile = $passwdFile; } /** * passwdFileプロパティで設定されたパスワードファイルから * パスワードを読み込みます。 * @return array 連想配列 [ ユーザー名 => パスワード, ・・・ ] */ public function getPasswords() { return parse_ini_file($this->passwdFile); } } ?>
login.dicon
authLogicコンポーネントのclass
属性値を、新しく作成したIniPasswdAuthLogic
に変更します。また、authLogicコンポーネントにproperty
タグを追加し、name
属性にpasswdFile
、そのボディにパスワードファイル(passwd.ini)を設定します。これによりパスワードファイルはセッター・インジェクションされます。実際のファイルは次のとおりです。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN" "components21.dtd"> <components> <component name="loginService" class="ConsoleLoginService"> <property name="tryCount">"3"</property> </component> <component name="authLogic" class="IniPasswdAuthLogic"> <property name="passwdFile">"passwd.ini"</property> </component> </components>
実装変更後の実行
「login.php」をコマンドラインから実行すると、ユーザー名とパスワードの入力が求められます。ここで、「passwd.ini」に設定済みのユーザー名とパスワードを入力し、ログインできることを確認します。認証ロジックをIniPasswdAuthLogic
に変更しましたが、ログインサービスについては変更はありませんので、ログインサービスの動作は前回と変わりません。認証に3回失敗するとログイン失敗となります。
C:\CodeZine>php login.php username : hoge password : abcd Uummm... Try again !! username : hoge password : abcd1234 hello hoge ( login 2006-07-23 12:00:00 ) C:\CodeZine>
認証ロジックをSimplePasswdAuthLogic
からIniPasswdAuthLogic
に変更したように、あるコンポーネントがもつ機能の実装に変更が生じた場合(インターフェイスには変更がない場合)には、diconファイルのコンポーネント定義を再設定することで対応できます。今回の例では、login.phpやConsoleLoginService
クラスへの変更は特にありませんでした。例えば、パスワードを暗号化するShadowPasswdAuthLogic
などを実装してみて、diconファイルでauthLogicコンポーネントを差し替えることを試していただければと思います。