SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

S2Container.PHP5を用いたDIベースの開発

S2Containerの自動バインディング機能の利用


  • X ポスト
  • このエントリーをはてなブックマークに追加

ログインサービス

 それでは、実際にS2Containerを使いながら説明していきます。ここでは、サンプルとしてコマンドラインから使用するログインサービスを作成します。ユーザーはコマンドラインからユーザー名とパスワードを入力します。ログインサービスは入力された情報でユーザー認証を行いその結果を表示します。

セットアップ

 S2Containerをセットアップページに従ってインストールします。ダウンロードパッケージとして、PEARパッケージとフルパッケージの2種類が用意されています。PEARパッケージをダウンロードし、「pear install」コマンドでインストールする方法が簡単です。インストール後、S2Containerを使用するためには__autoload関数の設定が必要になります。こちらもセットアップページ__autoload関数のサンプルが掲載されているので参照ください。

インターフェイスを定義する

 今回は、ログインサービスと認証ロジックに分けて問題を考えます。

ログインサービス

 ログインサービスのインターフェイス名はLoginServiceとします。ログインサービスは、ログインに必要な情報を取得し、認証ロジックに認証を委譲します。最後に取得した情報および認証結果をまとめた情報を返します。

認証ロジック

 認証ロジックのインターフェイス名はAuthLogicとします。認証ロジックは、渡された情報を元にユーザー認証を行います。ユーザー認証の方法はいろいろ考えられます。ここでは、最初にシンプルなテキストマッチを行う認証ロジックを作成し、後ほど、INI形式のパスワードファイルを用いる認証ロジックに差し替えることにします。

作成するファイル

login.php

 login.php がメインの実行ファイルです。処理の流れは次のようになります。

  1. S2Containerを使用するための設定
  2. login.diconからS2Containerを生成
  3. S2Containerからログインサービスを取得
  4. ログインサービスのログイン処理を実行
  5. ログイン結果のメッセージを表示

 実際のファイルは次のとおりです。

login.php
<?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;
}
?>
s2setup.inc.php
<?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クラスのインスタンスを返します。

 実際のファイルは次のとおりです。

LoginService.class.php
<?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タグで設定し、コンテナにインジェクションしてもらいます。

 実際のファイルは次のとおりです。

ConsoleLoginService.class.php
<?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メソッドはユーザー名とパスワードを引数にとります。実際のファイルは次のとおりです。

AuthLogic.class.php
<?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を返します。

 実際のファイルは次のとおりです。

SimplePasswdAuthLogic.class.php
<?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です。

 実際のファイルは次のとおりです。

UserInfo.class.php
<?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コンポーネント
  • loginServiceコンポーネントは、ログインサービスのコンポーネントで、class属性にConsoleLoginServiceクラスを指定します。また、ログインのトライ回数をpropertyタグで設定します。propertyタグのname属性にプロパティ名tryCountを設定し、propertyタグのボディに回数を指定します。ここではトライ回数は3回までとしました。
  • authLogicコンポーネント
  • authLogicコンポーネントは、認証ロジックのコンポーネントで、class属性にSimplePasswdAuthLogicを指定します。

 実際のファイルは次のとおりです。

login.dicon
<?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回認証に失敗するとログインが失敗になります。

login.phpの実行
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ファイルは次のとおりです。

passwd.ini
; ユーザー名 = "パスワード"
hoge = "abcd1234"
foo  = "bar2006"

 パスワードファイルはdiconファイルで指定できるようにします。IniPasswdAuthLogicpasswdFileプロパティを用意し、そのセッターメソッドを実装します。実際のファイルは次のとおりです。

IniPasswdAuthLogic.class.php
<?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)を設定します。これによりパスワードファイルはセッター・インジェクションされます。実際のファイルは次のとおりです。

login.dicon
<?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回失敗するとログイン失敗となります。

login.phpの実行
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コンポーネントを差し替えることを試していただければと思います。

次のページ
UnitTestについて

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

klove(klove)

Seasarファウンデーション Seasar.PHPプロジェクトにて、コミッタ活動しています。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/495 2007/04/13 12:53

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング