Edited at

PHPでログイン機能を実装するチュートリアル #6

More than 3 years have passed since last update.


  1. 基本設計、ユーザーモデル

  2. オートローダー

  3. 例外・ログ


  4. PDO シングルトン SQLインジェクション



  5. ユーザーモデルの作成


  6. クラスの継承


  7. テンプレート・クラスの実装


  8. アカウントロック機能の実装


  9. メール送信機能の実装


  10. アカウントロック解除機能の実装


  11. CSRF対策


前回作成した UserModel.class.php には、システム側からの要求で作成したメソッド(A)とDBの定義から要求されたメソッドとプロパティ(B)が混在しています。そこで、この(A)と(B)については分けておきたくなります。

(B)の部分については、テーブルの定義から自動的に決定されるので、ソースコードを書き出すプログラムを作成しておけば、テーブルの定義が変わってもそのプログラムで書き出せば良いし、 手作業で書き換えることはかえってバグの元 になります。

また、動作確認などでバグがあったとしても、UserModelBase.class.php にバグがある可能性は低く、UserModel.class.phpに書かれた処理を対象にデバッグ対象を絞り込みやすくなります。

こうしておくことで、基本的に UserModelBase.class.php はいじってはならず、UserModel.class.phpに必要な処理を記述するという「基本的なルール」を設定することができます。


UserModel.class.php

<?php

namespace MyApp\model;

use MyApp\dao\UserDao;

/**
* UserModel
*/

final class UserModel extends UserModelBase
{

/**
* アカウントをロックするログイン失敗回数
*/

const LOCK_COUNT = 3;

/**
* アカウントをロックする時間(分)
*/

const LOCK_MINUTE = 15;

/**
* メールアドレスからユーザーを検索する
* @param string $strEmail
* @return \MyApp\model\UserModel
*/

public function getModelByEmail($strEmail)
{
$dao = UserDao::getDaoFromEmail($strEmail);
return (isset($dao[0])) ? $this->setProperty(reset($dao)) : null;
}

/**
* パスワードが一致しているかどうかを判定する
* @param type $password
* @return bool
*/

public function checkPassword($password)
{
$hash = $this->getPassword();
return password_verify($password, $hash);
}

/**
* ログイン失敗をリセットする
* 1以上のときに0にする
* @return bool
*/

public function loginFailureReset()
{
$count = $this->getLoginFailureCount();
if (0 < $count) {
$this->setLoginFailureCount(0)
->setLoginFailureDatetime(null);
return $this->save();
}
//変更の必要がない
return true;
}

/**
* ログイン失敗をインクリメントする
* 指定回数(self::LOCK_COUNT)に満たないときのみ+1
* @return bool
*/

public function loginFailureIncrement()
{
$count = $this->getLoginFailureCount();
if (self::LOCK_COUNT > $count) {
$now = (new \DateTime())->format('Y-m-d H:i:s');
$this->setLoginFailureCount(1 + $count)
->setLoginFailureDatetime($now);
return $this->save();
}

//ログイン失敗が設定以上のとき
return true;
}

/**
* アカウントがロックされているかどうかを判定する
* @return bool ロックされていたら true
*/

public function isAccountLock()
{
$count = $this->getLoginFailureCount();
$datetime = $this->getLoginFailureDatetime();

$lastFailureDatetime = new \DateTime($datetime);
$interval = new \DateInterval(
sprintf('PT%dM', self::LOCK_MINUTE)
);
$lastFailureDatetime->add($interval);

//設定時間以内で、かつ設定回数以上の失敗を記録しているとき
if ($lastFailureDatetime > new \DateTime() && self::LOCK_COUNT <= $count) {
return true;
}
return false;
}

}



UserModelBase.class.php

<?php

/**
* **** 注意 ****
* このファイルは自動的に生成されます。
* 手動でいじったりしないようにしてください。
*/

namespace MyApp\model;

use MyApp\dao\UserDao;

/**
* UserModelBase
*/

class UserModelBase
{

/**
* ユーザーID
*/

private $_userId = null;

/**
* パスワード(ハッシュ)
*/

private $_password = null;

/**
* 表示名
*/

private $_displayName = null;

/**
* メールアドレス
*/

private $_email = null;

/**
* トークン
*/

private $_token = null;

/**
* ログイン失敗回数
*/

private $_loginFailureCount = null;

/**
* ログイン失敗日時
*/

private $_loginFailureDatetime = null;

/**
* 削除フラグ
*/

private $_deleteFlag = null;

/**
* プロパティをセットする
* @param array $arrDao
* @return \MyApp\model\UserModel
*/

protected function setProperty(array $arrDao)
{
$this->setUserId($arrDao['userId'])
->setDisplayName($arrDao['displayName'])
->setEmail($arrDao['email'])
->setPassword($arrDao['password'])
->setToken($arrDao['token'])
->setLoginFailureCount($arrDao['loginFailureCount'])
->setLoginFailureDatetime($arrDao['loginFailureDatetime'])
->setDeleteFlag($arrDao['deleteFlag']);
return $this;
}

/**
* 更新する
* @return bool
*/

public function save()
{
return UserDao::save($this);
}

/**
* 新規作成する
* @return int
*/

public function create()
{
return UserDao::insert($this);
}

/**
* ユーザーIDを設定する
* @param int $userId
* @return \MyApp\model\UserModel
*/

public function setUserId($userId)
{
$this->_userId = $userId;
return $this;
}

/**
* パスワード(ハッシュ)を設定する
* @param string $password
* @return \MyApp\model\UserModel
*/

public function setPassword($password)
{
$this->_password = $password;
return $this;
}

/**
* 表示名を設定する
* @param string $displayName
* @return \MyApp\model\UserModel
*/

public function setDisplayName($displayName)
{
$this->_displayName = $displayName;
return $this;
}

/**
* メールアドレスを設定する
* @param string $email
* @return \MyApp\model\UserModel
*/

public function setEmail($email)
{
$this->_email = $email;
return $this;
}

/**
* トークンを設定する
* @param string $token
* @return \MyApp\model\UserModel
*/

public function setToken($token)
{
$this->_token = $token;
return $this;
}

/**
* ログイン失敗回数を設定する
* @param int $loginFailureCount
* @return \MyApp\model\UserModel
*/

public function setLoginFailureCount($loginFailureCount)
{
$this->_loginFailureCount = $loginFailureCount;
return $this;
}

/**
* ログイン失敗日時を設定する
* @param string $loginFailureDatetime
* @return \MyApp\model\UserModel
*/

public function setLoginFailureDatetime($loginFailureDatetime)
{
$this->_loginFailureDatetime = $loginFailureDatetime;
return $this;
}

/**
* 削除フラグを設定する
* @param bool $deleteFlag
* @return \MyApp\model\UserModel
*/

public function setDeleteFlag($deleteFlag)
{
$this->_deleteFlag = $deleteFlag;
return $this;
}

/**
* ユーザーIDを取得する
* @return int
*/

public function getUserId()
{
return $this->_userId;
}

/**
* パスワード(ハッシュ)を取得する
* @return string
*/

public function getPassword()
{
return $this->_password;
}

/**
* 表示名を取得する
* @return string
*/

public function getDisplayName()
{
return $this->_displayName;
}

/**
* メールアドレスを取得する
* @return string
*/

public function getEmail()
{
return $this->_email;
}

/**
* トークンを取得する
* @return string
*/

public function getToken()
{
return $this->_token;
}

/**
* ログイン失敗回数を取得する
* @return int
*/

public function getLoginFailureCount()
{
return $this->_loginFailureCount;
}

/**
* ログイン失敗日時を取得する
* @return string
*/

public function getLoginFailureDatetime()
{
return $this->_loginFailureDatetime;
}

/**
* 削除フラグを取得する
* @return bool
*/

public function getDeleteFlag()
{
return $this->_deleteFlag;
}

}


次回は、当初予定していた「ログインメソッドの実装」はすでに作成済みですので、テンプレート・クラスを作成することにします。