PDOを使用して新規登録とログインシステムを実装する

バックエンド

データベースの設定

ユーザーはグローバル権限のあるものか、今回作るベースに権限のあるものを使用します。 グローバル権限でももちろん問題はないですが、必要なデータベースにのみ権限があるユーザーを使用する方が好ましいです。

データベース名

testData ここは好きなものでOKです。

テーブル

user

idint(11)被り防止のためにAUTO_INCREMENTの設定、primary key設定
namevarchar(10)
passwordtextまたはvarchar登録時にハッシュ化を登録するため長めに設定

新規登録ページの作成

フォームで入力された値を受け取って、パスワードが一定桁数以上の時(今回は8桁でデモ)にデータベース登録。 パスワードはpassword_hash()を使用しハッシュ化を行ったあとに登録します。 登録完了後は、アラートで登録完了を表示し、メインページへ遷移するようになっています。

<?php
    // セッション開始
    session_start();
    // 既にログインしている場合にはメインページに遷移
    if (isset($_SESSION['USERID'])) {
        header('Location: main.php');
        exit;
    }
    $db['host'] = 'localhost';
    $db['user'] = 'root';
    $db['pass'] = 'root';
    $db['dbname'] = 'testData';
    $error = '';
    // ログインボタンが押されたら
    if (isset($_POST['signUp'])) {
        if (empty($_POST['username'])) {
            $error = 'ユーザーIDが未入力です。';   
        }else if (empty($_POST['password'])) {
            $error = 'パスワードが未入力です。'; 
        }
        if (!empty($_POST['username']) && !empty($_POST['password'])) {
            $username = $_POST['username'];
            $password = $_POST['password'];
            $dsn = sprintf('mysql: host=%s; dbname=%s; charset=utf8', $db['host'], $db['dbname']);
            $pdo = new PDO($dsn, $db['user'], $db['pass'], array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
            // idの重複とパスワードの桁数チェック
            function cheak($id,$count){
                if($count > 0){
                    throw new Exception('そのユーザーIDは既に使用されています。');
                }
                if ($id < 8) {
                    throw new Exception('パスワードは8桁以上で入力してください。');    
                }
            }
            try{
                $sqlname = 'SELECT COUNT(*) FROM user WHERE `name` = '$username'';
                $ss = $pdo->query($sqlname);
                $count = $ss->fetchColumn();

                $id = strlen($_POST['password']);
                cheak($id,$count);

                $stmt = $pdo->prepare('INSERT INTO `user`(`name`, `password`) VALUES (:username, :password)');
                $pass = password_hash($password, PASSWORD_DEFAULT);
                $stmt->bindParam(':username', $username, PDO::PARAM_STR);
                $stmt->bindParam(':password', $pass, PDO::PARAM_STR);
                $stmt->execute();
              
                $_SESSION['USERID'] = $username;
                echo '<script>
                        alert("登録が完了しました。");
                        location.href="main.php";
                      </script>';
            } catch(Exception $e){
                $error = $e->getMessage();
            }
        }  
    }
?>

<!DOCTYPE html>
<html>
    <head>
        <title>新規登録</title>
    </head>
    <body>
        <main>
            <form id="loginForm" name="loginForm" action="" method="POST">
                <p style="color:red;"><?php echo $error ?></p>
                <label for="username">ユーザーID<br>
                    <input type="text" id="username" name="username" placeholder="ユーザー名を入力" value="<?php if (!empty($_POST["username"])) {echo htmlspecialchars($_POST["username"], ENT_QUOTES);} ?>">
                </label><br>
                <label for="password">パスワード<br>
                    <input type="password" id="password" name="password" value="" placeholder="パスワードを入力">※8桁以上
                </label>
                <input type="submit" id="signUp" name="signUp" value="新規登録" class="btn up">
            </form>
        </main>
    </body>
</html>

ソースの内容について

if (isset($_SESSION['USERID'])) {
        header('Location: main.php');
        exit;
}

セッションに値が入っているとき=ログインを既にしているときには、このページを表示しないようにしています。

function cheak($id,$count){
        if($count > 0){
               throw new Exception('そのユーザーIDは既に使用されています。');
        }
        if ($id < 8) {
               throw new Exception('パスワードは8桁以上で入力してください。');    
        }
}

下のtryの中から引数を渡して、それぞれIDの重複とパスワードの桁数をチェックしています。 エラーの場合にはthrow new Exception()で例外が出たことを投げ、エラー文をあとのcatchで表示しています。

$stmt = $pdo->prepare('INSERT INTO `user`(`name`, `password`) VALUES (:username, :password)');
$pass = password_hash($password, PASSWORD_DEFAULT);
$stmt->bindParam(':username', $username, PDO::PARAM_STR);
$stmt->bindParam(':password', $pass, PDO::PARAM_STR);
$stmt->execute();

prepare()の値にはパラメータ(:任意の文字列)をおき、実行はパラメータに値が入るまで待機となります。 password_hash()を使用してパスワードハッシュを生成しています。 bindParamは指定した変数を文字列としてパラメータに入れるもので、また、PDO::PARAM_STRは変数の値を文字列として扱うことを宣言するものです。 今回は数値がないので使用してませんが、数値などを登録する場合にはbindValuePDO::PARAM_INTを使用します。

$_SESSION['USERID'] = $username;
echo '<script>
           alert("登録が完了しました。");
           location.href="main.php";
    </script>';

セッションに入力されたユーザー名を入れ(=ログイン)、その後アラートを表示し、メインページへ遷移をさせています。 PHPとJavaScriptでは処理するタイミングが違うため、アラート後のリダイレクトもJavaScriptで行う必要があります。

ログインページの作成

ハッシュ化したパスワードをユーザー名を照らし合わせてログインをします。 先ほど同様、ログインをすでにしている場合にはログインページには飛べないようになっています。

<?php
    // セッション開始
    session_start();
    // 既にログインしている場合にはメインページに遷移
    if (isset($_SESSION["USERID"])) {
        header('Location: main.php');
        exit;
    }
    $db['host'] = 'localhost';
    $db['user'] = 'root';
    $db['pass'] = 'root';
    $db['dbname'] = 'testData';
    $error = '';
    // ログインボタンが押されたら
    if (isset($_POST['login'])) {
        if (empty($_POST['username'])) {
            $error = 'ユーザーIDが未入力です。';
        } else if (empty($_POST['password'])) {
            $error = 'パスワードが未入力です。';
        }
        if (!empty($_POST['username']) && !empty($_POST['password'])) {

            $username = $_POST['username'];

            $dsn = sprintf('mysql: host=%s; dbname=%s; charset=utf8', $db['host'], $db['dbname']);
            try {
                $pdo = new PDO($dsn, $db['user'], $db['pass'], array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
                $stmt = $pdo->prepare('SELECT * FROM user WHERE name = ?');
                $stmt->execute(array($username));
                $password = $_POST['password'];

                $result = $stmt->fetch(PDO::FETCH_ASSOC);
                if (password_verify($password, $result['password'])) {
                    $_SESSION['USERID'] = $username;
                    header('Location: main.php');
                    exit();
                } else {
                    $error = 'ユーザーIDあるいはパスワードに誤りがあります。';
                }

            } catch (PDOException $e) {
                echo $e->getMessage();
            }
        }
    }
?>

<!DOCTYPE html>
<html>
    <head>
        <title>ログイン</title>
    </head>
    <body>
    <main>
        <form id="loginForm" name="loginForm" action="" method="POST">
            <p style="color:red;"><?php echo $error ?></p>
            <br>
            <label for="username">ユーザーID<br>
                <input type="text" id="username" name="username" placeholder="ユーザーIDを入力" value="<?php if (!empty($_POST["username"])) {echo htmlspecialchars($_POST["username"], ENT_QUOTES);} ?>">
            </label><br>
            <label for="password">パスワード<br>
                <input type="password" id="password" name="password" value="" placeholder="パスワードを入力">
            </label>
            <input type="submit" id="login" name="login" value="ログイン">
        </form>
        <p><a href="signup.php">新規登録はこちら</a></p>
    </main>
    </body>
</html>

ソースの内容について

array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION)

エラーレポートについて例外をスローする設定にしています。 デフォルトはPDO::ERRMODE_SILENTでエラーコードのみが設定されるようになっています。

$result = $stmt->fetch(PDO::FETCH_ASSOC);

結果セットを1行ごとに連想配列形式で取り出し、返り値を $result に代入しています。 $result['name']などで値が表示できます。

password_verify($password, $result['password'])

入力されたパスワードがハッシュ化されたパスワードと一致するかどうかを判定しています。 $passwordの部分には入力されたパスワードが、$result['password']の部分にはハッシュ化パスワードが入ることで判定ができます。 返り値は「true」「false」が返ってきます。

ログイン後のメインページの作成

ログアウトへのリンクと、ログイン状態のチェックをします。

<?php
    session_start();

    // ログイン状態チェック
    if (!isset($_SESSION["USERID"])) {
        header("Location: login.php");
        exit;
    }
?>
<!DOCTYPE html>
<html>
    <head>
        <title>メインページ</title>
    </head>
    <body>
        <main>
            <p>メイン</p>
            <a href="logout.php">ログアウト</a>
        </main>
    </body>
</html>

ソースの内容について

if (!isset($_SESSION["USERID"])) {
        header("Location: login.php");
        exit;
}

セッションに値がない場合にはログイン画面に遷移させています。

ログアウトページの作成

ログインページへのリンクと、セッションのクリアを行います。

<?php
    session_start();
    // セッションクリア
    session_destroy(); 
    $error = "ログアウトしました。";
?>

<!doctype html>
<html>
    <head>
        <title>ログアウト</title>
    </head>
    <body>
        <div><?php echo $error; ?></div>
        <ul>
            <li><a href="login.php">ログインページへ</a></li>
        </ul>
    </body>
</html>

ソースの内容について

session_destroy(); 

上記の一文でセッションをクリアしています。

まとめ

ソースは下記からダウンロードできます。


ファイルを開く

PDOの使用&がっつりphp書いたのが初めてなので、間違いやここはこうじゃなくて....って部分があるかと思いますので、気づいた方はコメントまたはご連絡ください!


PHP

この記事の読んだ方におすすめ