修正コード
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が進化してきたように、
今回の診察はここまでです。また次回お会いしましょう。まだまだ寒い日が続くので風邪など引かれませんように。