修正コード
PHP 4は、
前回は、
では、
<?php
namespace Acme\Controller;
require_once dirname(__DIR__).'/vendor/autoload.php';
use Acme\Lib\BasicController;
use Acme\Lib\Http;
use Acme\Lib\CheckExecuter;
use Acme\Lib\Login;
use Acme\Model\UserModel;
/**
* <b>LoginControler</b>
* ログインコントローラ
*
* @author hoge
*/
class LoginController extends BasicController
{
const LOGIN_ID = 'login_id';
const PASSWORD = 'password';
const PASSWORD_MAX_LENGTH = 8;
/**
* @var string
*/
protected $NEXT_PAGE = 'top.php';
/**
* コンストラクタ
*/
public function __construct()
{
$this->init();
// セッションクリア
unset($_SESSION[UserModel::SESSION_USER]);
if (Http::isPost()) {
$this->values = $_POST;
} else {
$this->values = null;
}
}
/**
* メイン処理
*/
public function execute()
{
if (!Http::isPost()) {
return;
}
// 値チェック
$checker = new CheckExecuter($this);
$checker->check();
$this->errorMessages = $checker->getErrorMessages();
if ($this->isError()) {
return;
}
// セッションクリア
$_SESSION = array();
// ログイン処理
$userModel = Login::auth($this->values[static::LOGIN_ID], $this->values[static::PASSWORD]);
if (empty($userModel)) {
$this->addErrorMessage('error_not_login');
return;
}
// セッションIDの振り直し
session_regenerate_id(true);
unset($_SESSION[UserModel::SESSION_USER]);
$_SESSION[UserModel::SESSION_USER] = $userModel;
// 次ページへ
$this->transferPage($this->NEXT_PAGE);
}
/**
* ログインIDチェック
*
* @return mixed 正常ならnull / エラーならコード?
*/
public function checkLoginId()
{
if (isset($this->values[static::LOGIN_ID])
&& $this->values[static::LOGIN_ID]) {
return null;
} else {
return 'check_login_id';
}
}
/**
* パスワードチェック
* 半角英数8桁以内
*
* @return mixed 正常ならnull / エラーならコード?
*/
public function checkPassword()
{
if (isset($this->values[static::PASSWORD])
&& $this->values[static::PASSWORD]
&& preg_match('/^[a-zA-Z0-9]+$/', $this->values[static::PASSWORD])
&& strlen($this->values[static::PASSWORD]) <= static::PASSWORD_MAX_LENGTH) {
return null;
} else {
return 'check_password';
}
}
}
<?php
namespace Acme\Lib;
use Acme\Lib\Database;
use Acme\Model\UserModel;
/**
* <b>Login</b>
* ログインクラス
* ログイン管理を行う
*
* @author hoge
*/
class Login
{
/**
* 認証
*
* @param string $loginId
* @param string $password
* @return UserModel
*/
public static function auth($loginId, $password)
{
// PostgreSQL には事前に接続済とする
$loginId = pg_escape_literal($loginId);
$password = pg_escape_literal($password);
$where = sprintf('login_id=%s and password=%s', $loginId, $password);
$db = Database::getInstance();
$record = $db->getOneRecord('*', 'v_user', $where);
if (empty($record)) {
return null;
}
$userModel = new UserModel();
$userModel->setAttributes($record);
return $userModel;
}
}
治療ポイント1. PHP 5の書き方へ
PHP 4の流儀で書かれた個所をPHP 5の書き方に変更しています。
診断編で指摘した個所以外では、varキーワード、
PHP 4の形式で記述した場合はpublicが設定されているのと同じ意味となります。修正コードでは、$nextPageはLoginControllerのみで利用されているのでprotectedを、publicを設定しています。
どのアクセス権を用いるのが適切なのかは、protectedを基本にして、publicを、privateを用いるというのがよいでしょう。
治療ポイント2. 名前空間の設定
各PHPファイルに名前空間を設定しました。
名前空間にはAcmeをトップレベルに指定しています。Acmeはサンプルでよく使われる名前空間です。さらに、lib/、controller/、model/)Acme\Lib、Acme\Controller、Acme\Model)
これにより、Acme\Controller\LoginController、Acme\Lib\Loginが完全なクラス名となります。もしほかのライブラリを利用した際にLoginというクラスが存在しても、
また、use文にて、
たとえば下記のようなコードであれば、Acme\Lib\DatabaseというクラスをDatabaseという名前で参照できるようになります。
use Acme\Lib\Database;
治療ポイント3. 定数をクラス定数に変更
define文にて定義している定数をクラス定数として定義しています。
define文で定義した定数はグローバルな名前空間に属することになるので、
LoginControllerクラスではLOGIN_とPASSWORDという定数をクラス定数として定義しています。もしも仮に、FooControllerクラスにも同じLOGIN_とPASSWORDというクラス定数を定義されていても、
クラス定数にする利点は、
治療ポイント4. オートローダの対応
各ファイルの先頭で記述していたrequire_文によるクラスファイルの読み込みをオートローダで行うようにしました。
オートローダは、
Composerのオートローダを利用するには、composer.というファイルがダウンロードされます。
$ curl -sS https://getcomposer.org/installer | php $ ls composer.phar
次にオートローダの設定をcomposer.というJSONファイルに記述します。
{
"autoload": {
"psr-4": {
"Acme\\Lib\\": "lib/",
"Acme\\Controller\\": "controller/",
"Acme\\Model\\": "model/"
}
}
}
今回はPSR-4composer.では、Acme\Lib名前空間はlib/ディレクトリ、Acme\Controller名前空間はcontroller/ディレクトリ、Acme\Model名前空間はmodel/ディレクトリからクラスファイルを読み込むことを指定しています。
このようにPSR-4では、
次にこのcomposer.に記述した内容をオートローダに反映するために次のコマンドを実行します。
$ php composer.phar dump-autoload Generating autoload files
最後に、require_文でオートローダを読み込みます。
require_once dirname(__DIR__).'/vendor/autoload.php';
これでComposerのオートローダが利用できるようになりました。クラスファイルはrequire_文で読み込まなくても、
オートローダを用いることで、
治療ポイント5. 制御構造の見直し
LoginControllerクラスのexecute()メソッドの実装について、
execute()メソッド内の制御構造のみを抜き出したのが下記です。ログイン処理を行うために3つのif文による判定を経る必要があるため、
if (Http::isPost()) {
if (!$this->isError()) {
if (Login::auth($userModel)) {
// ログイン処理
}
}
}
この制御構造を修正コードで組み直したのが以下です。それぞれのif文にてログイン処理が成立しない場合はreturnで処理を終了するようにしています。このようにすればインデントを深くする必要がなく、if文の判定を気にしなくてよいので、
if (!Http::isPost()) {
return;
}
if ($this->isError()) {
return;
}
if (empty($userModel)) {
$this->addErrorMessage('error_not_login');
return;
}
// ログイン処理
治療ポイント6. Login::auth()メソッドのシグネチャ見直し
Loginクラスのauth()メソッドのシグネチャを変更しました。
元のコードでは、UserModelクラスオブジェクトを引数で受け取り、$userModelに必要な値をセットしています。呼び出し元では、$userModelを以降の処理で利用する形になっています。
function auth(&$userModel) {
auth()メソッドでは、UserModelクラスのオブジェクトを生成し、
public static function auth($loginId, $password)
このようにすれば、
治療を終えて
今回は、
PHPが進化してきたように、
今回の診察はここまでです。また次回お会いしましょう。まだまだ寒い日が続くので風邪など引かれませんように。