某QAサイトでよく見るログイン機能のコードはとあるQiitaの記事を参考にしているものが多いのですが、if
のネストも深くて美しいコードじゃないし、SQLに変数を直接埋め込んでいたりと、決して参考にしてはいけないサンプルだったので、こちらを書いた。
詳しく仕組みを理解したいのであれば、「PHPでログイン機能を実装するチュートリアル #1」を読んで欲しいが、コピペで済ませるなら、せめてこのコードを使って欲しい。
サンプルデータは、ユーザー名:user、パスワード:password となっている。
<?php
/**
* database.php
* @since 2018/09/18
*/
function h($string)
{
return htmlspecialchars($string, ENT_QUOTES, 'utf-8');
}
function connect()
{
$dsn = 'mysql:host=localhost;dbname=sample;charset=utf8mb4;';
$username = 'root';
$password = 'password';
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
];
$pdo = new PDO($dsn, $username, $password, $options);
return $pdo;
}
<?php
/**
* index.php
* @since 2018/09/18
*/
ini_set('display_errors', true);
error_reporting(E_ALL);
session_start();
require 'database.php';
// エラーを格納する変数
$err = [];
// 「ログイン」ボタンが押されて、POST通信のとき
if (filter_input(INPUT_SERVER, 'REQUEST_METHOD') === 'POST') {
$user_name = filter_input(INPUT_POST, 'user_name');
$password = filter_input(INPUT_POST, 'password');
if ($user_name === '') {
$err['user_name'] = 'ユーザー名は入力必須です。';
}
if ($password === '') {
$err['password'] = 'パスワードは入力必須です。';
}
// エラーがないとき
if (count($err) === 0) {
// DB接続
$pdo = connect();
// ステートメント
$stmt = $pdo->prepare('SELECT * FROM User WHERE user_name = ?');
// パラメータ設定
$params = [];
$params[] = $user_name;
// SQL実行
$stmt->execute($params);
// レコードセットを取得
$rows = $stmt->fetchAll();
// パスワード検証
foreach ($rows as $row) {
$password_hash = $row['password'];
// パスワード一致
if (password_verify($password, $password_hash)) {
session_regenerate_id(true);
$_SESSION['login_user'] = $row;
header('Location:main.php');
return;
}
}
$err['login'] = 'ログインに失敗しました。';
}
}
?>
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ログイン</title>
<style type="text/css">
.error {
color: red;
}
</style>
</head>
<body>
<div id="wrapper">
<form action="" method="post">
<?php if (isset($err['login'])) : ?>
<p class="error"><?php echo h($err['login']); ?></p>
<?php endif; ?>
<p>
<label for="user_name">ユーザー名</label>
<input id="user_id" name="user_name" type="text" />
<?php if (isset($err['user_name'])) : ?>
<p class="error"><?php echo h($err['user_name']); ?></p>
<?php endif; ?>
</p>
<p>
<label for="">パスワード</label>
<input id="password" name="password" type="password" />
<?php if (isset($err['password'])) : ?>
<p class="error"><?php echo h($err['password']); ?></p>
<?php endif; ?>
</p>
<p>
<button type="submit">ログイン</button>
</p>
</form>
</div>
</body>
</html>
<?php
/**
* main.php
*
* @since 2018/09/18
*/
session_start();
require 'database.php';
$login_user = $_SESSION['login_user'];
?>
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<?php foreach ($login_user as $key => $val) : ?>
<p><?php echo h($key); ?> : <?php echo h($val); ?></p>
<?php endforeach; ?>
</body>
</html>
<?php
/**
* adduser.php
*
* @since 2018/09/21
*/
ini_set('display_errors', true);
error_reporting(E_ALL);
session_start();
require 'database.php';
$err = [];
// 「ログイン」ボタンが押されて、POST通信のとき
if (filter_input(INPUT_SERVER, 'REQUEST_METHOD') === 'POST') {
$user_name = filter_input(INPUT_POST, 'user_name');
$password = filter_input(INPUT_POST, 'password');
$password_conf = filter_input(INPUT_POST, 'password_conf');
if ($user_name === '') {
$err['user_name'] = 'ユーザー名は入力必須です。';
}
if ($password === '') {
$err['password'] = 'パスワードは入力必須です。';
}
if ($password !== $password_conf) {
$err['password_conf'] = 'パスワードが一致しません。';
}
// エラーがないとき
if (count($err) === 0) {
// DB接続
$pdo = connect();
// ステートメント
$stmt = $pdo->prepare('INSERT INTO `User` (`id`, `user_name`, `password`) VALUES (null, ?, ?)');
// パラメータ設定
$params = [];
$params[] = $user_name;
$params[] = password_hash($password, PASSWORD_DEFAULT);
// SQL実行
$success = $stmt->execute($params);
}
}
?>
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
.error {
color: red;
}
</style>
</head>
<body>
<?php if (count($err) > 0) : ?>
<?php foreach ($err as $e): ?>
<p class="error"><?php echo h($e); ?></p>
<?php endforeach; ?>
<?php endif; ?>
<?php if (isset($success) && $success) : ?>
<p>登録に成功しました。</p>
<p><a href="index.php">こちらからログインしてください。</a></p>
<?php else: ?>
<form action="" method="post">
<p>
<label for="user_name">ユーザー名</label>
<input id="user_id" name="user_name" type="text" />
</p>
<p>
<label for="">パスワード</label>
<input id="password" name="password" type="password" />
</p>
<p>
<label for="">確認用パスワード</label>
<input id="password_conf" name="password_conf" type="password" />
</p>
<p>
<button type="submit">ログイン</button>
</p>
<p>
<a href="adduser.php">新規ユーザー登録</a>
</p>
</form>
<?php endif; ?>
</body>
</html>
CREATE TABLE `User` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'AI',
`user_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '氏名',
`password` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'パスワード',
PRIMARY KEY (`id`),
UNIQUE KEY `user_name` (`user_name`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
INSERT INTO `User` (`id`, `user_name`, `password`)
VALUES
(1,'user','$2y$10$ecRmAWY4n/jLa0tTzIaG7.SMhb1TfdROy3nXeG5aVZorUX1n6/WHO');