PHP
ログイン

PHPログイン機能サンプル

某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');