@ PHP

Authentication:新規登録とログイン

メールアドレスとパスワードをデータベースに登録し、登録した情報を使って専用ページにログインする処理を考えてみます。

ディレクトリ構成

/approot
├── functions.php
├── sign_up.php
├── sign_in.php
├── index.php
├── sign_out.php
└── style.css

データベースの作成

phpMyAdmin などを利用して、以下のデータベースを作成します。

  • データベース名:shop
  • 照合順序:utf8_general_ci

データベース接続ファイルの作成

PDO クラスを使ってデータベースに接続するためのファイルは次のようになります。

dbconnect.php

<?php

try {
    $dbh = new PDO(
        'mysql:host=localhost; dbname=shop; charset=utf8',
        'user',
        'password',
        [
            PDO::ATTR_ERRMODE =>PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
        ]
    );
} catch (PDOException $e) {
    exit('ERR! : ' . $e->getMessage());
}

データベースに新規テーブルを作成する

データベース shop 配下に新規に users テーブルを作成します。

users テーブルのレイアウトはつぎのようにします。

フィールド名 データ型 概要
id INT(11) ID AUTO_INCREMENT (PK)
name VARCHAR(256) 名前 NOT NUL UNIQUE,
password VARCHAR(50) パスワード NOT NULL

データベースにテーブルを作成する場合には、prepare() メソッドと execute() メソッドを使います。

prepare メソッドにより、データベースに対して発行したい SQL 命令を指定し、execute() メソッドによってデータベースに命令が送信 / 実行されます。

createtable.php

<?php

require_once('dbconnect.php');

$sql = <<<SQL
  CREATE TABLE IF NOT EXISTS users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  uname varchar(50) NOT NULL UNIQUE,
  upassword varchar(256) NOT NULL
)
SQL;


try {

    $sth = $dbh->prepare($sql);
    $sth->execute();

    if ($sth == true) {
        printf("Created table users.\n");
    } else {
        printf("Cannot created table users.\n");
    }
} catch (PDOException $e) {
    print "ERR! : {$e->getMessage()}";
} finally {
    $dbh = null;
}

メンバー登録

  1. ユーザー名を登録できる。
  2. ユーザー名が未入力の場合エラーメッセージを表示する。
  3. パスワードを登録できる。
  4. パスワードが未入力の場合エラーメッセージを表示する。
  5. ユーザー名が重複している場合エラーメッセージを表示する。

sign_up.php

<?php

session_start();

require_once('functions.php');

// ステイタス変数の初期化
$status = '';

// サインアップボタンが押された場合
if (! empty($_POST["signup"])) {
    // ユーザ名・パスワードの入力チェック
    if (empty($_POST["uname"])) {
        $status = 'An username is required!';
    } elseif (empty($_POST["upassword"])) {
        $status = 'An password is required!';
    } elseif (! empty($_POST['uname']) && ! empty($_POST['upassword'])) {
        try {
            // データベースに接続
            $dbh = new PDO(
                'mysql:host=localhost; dbname=crud_44; charset=utf8',
                'root',
                'root',
                [
                    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                    PDO::ATTR_EMULATE_PREPARES => false,
                ]
            );

            // パスワードはハッシュ化する
            $password = password_hash($_POST['upassword'], PASSWORD_DEFAULT);

            // ユーザ入力を使用するのでプリペアドステートメントを使用
            $stmt = $dbh->prepare('INSERT INTO users VALUES (null, ?, ?)');
            $stmt->bindParam(1, h($_POST['uname']), PDO::PARAM_STR);
            $stmt->bindParam(2, h($password), PDO::PARAM_STR);
            if ($stmt->execute()) {
                $status = 'Successfully registered.';
            } else {
                // 既に存在するユーザ名だった場合INSERTに失敗する
            }
        } catch (PDOException $e) {
            echo 'ERR! : ' . $e->getMessage();
            $status = 'User name that already exists.';
        } finally {
            $dbh = null;
        }
    }
}
?>
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="utf-8">
  <title>sign up</title>
  <link rel="stylesheet" href="styles.css">
</head>

<body>
  <main>
    <div class="form-container">
      <section>
        <h1>Sign up</h1>
      </section>
      <hr>
      <div><?= $status ?></div>
      <form action="" method="post">
        <fieldset>
          <div><input type="text" name="uname" value="<?= $_POST['uname'] ?>" placeholder="Name"></div>
          <div><input type="password" name="upassword" value="<?= $_POST['upassword'] ?>" placeholder="Password"></div>
          <div class="text-align-right"><input type="submit" id="signup" name="signup" value="Sign up"></div>
        </fieldset>
      </form>
      <hr>
      <div class="text-align-center">have an account ! <a href="index.php"><b>Login</b></a></div>
    </div>
    </main>
</body>

</html>

functions.php

<?php
function h($str, string $charset = 'UTF-8'): string
{
    return htmlspecialchars($str, ENT_QUOTES | ENT_HTML5, $charset);
}

ログイン

  1. メールアドレスとパスワードを入力してログインできる。
  2. メールアドレスとパスワードが一致しない場合エラーメッセージを表示する。

members_login.php

<?php

session_start();

require_once('functions.php');


// ステイタス変数の初期化
$status = '';

// セッションが保存されていたらログイン済み
if (! empty($_SESSION['user'])) {
    $status = 'loggedin';
}

// ログインボタンが押された場合
if (! empty($_POST["login"])) {
    // ユーザ名・パスワードの入力チェック
    if (! empty($_POST['uname']) && ! empty($_POST['upassword'])) {
        try {
            // データベースに接続
            $dbh = new PDO(
                'mysql:host=localhost; dbname=crud_44; charset=utf8',
                'root',
                'root',
                [
                    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                    PDO::ATTR_EMULATE_PREPARES => false,
                ]
            );

            // 入力したユーザ名と一致するレコードを検索
            $sth = $dbh->prepare('SELECT upassword FROM users WHERE uname = ?');
            $sth->bindParam(1, h($_POST['uname']), PDO::PARAM_STR);
            $sth->execute();
            // 結果セットの行数が1だったら成功
            if ($sth->rowCount() == 1) {
                // 結果セットのupasswordカラムを$hashに関連付ける
                $sth->bindColumn('upassword', $hash);
                while ($sth->fetch()) {
                    // upasswordカラムの値が$_POST['upassword']で取得したパスワードにマッチするかどうか
                    if (password_verify($_POST['upassword'], $hash)) {
                        $status = 'success';
                        // $_POSTで取得したリクエストパラメータの値をセッションに保存
                        $_SESSION['user'] = $_POST;
                        header('Location: index.php');
                        exit();
                    } else {
                        $status = 'Password mismatch.';
                    }
                }
            } else {
                    $status = 'Login failed';
            }
        } catch (PDOException $e) {
            echo 'ERR! : ' . $e->getMessage();
        } finally {
            $dbh = null;
        }
    }
}
?>
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Sign in</title>
  <link rel="stylesheet" href="styles.css">
</head>

<body>
  <main>
    <div class="form-container">
      <section>
        <h1>Login</h1>
      </section>
      <hr>
      <div><?= $status ?></div>
      <form action="" method="post">
        <fieldset>
          <div><input type="text" name="uname" value="<?= $_POST['uname'] ?>" placeholder="Name"></div>
          <div><input type="password" name="upassword" value="<?= $_POST['upassword'] ?>" placeholder="Password"></div>
          <div class="text-align-right"><input type="submit" id="login" name="login" value="Login"></div>
        </fieldset>
      </form>
      <hr>
      <div class="text-align-center">Don't have account yet ! <a href="sign_up.php"><b>Sign Up</b></a></div>
    </div>
  </main>
</body>

</html>

ユーザートップ

index.php

<?php

session_start();

include('functions.php');

if (!isset($_SESSION['user'])) {
    header('Location: sign_out.php');
    exit;
}
?>
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="utf-8">
  <title>welcome - <?= h($_SESSION['user']['uname']) ?></title>
  <link rel="stylesheet" href="styles.css">
</head>

<body>
  <header>
    <div class="left">
      <p><a>PHP Authentication sample</a></p>
    </div>
    <div class="right">
      <p><a href="sign_out.php">sign out</a></p>
    </div>
  </header>
  <div class="content">welcome : <?= h($_SESSION['user']['uname']) ?></div>
</body>

</html>

ログアウト

sign_out.php

<?php

session_start();

$_SESSION=array();

if (isset($_COOKIE[session_name()])==true) {
    setcookie(session_name(), '', time()-42000, '/') ;
}

session_destroy();

header("Location: sign_in.php");
exit();

CSS

style.css

@charset "utf-8";

/* CSS Document */

* {
  box-sizing: border-box;
}

body,
input {
  color: #000;
  font-family: '游ゴシック', YuGothic, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', 'メイリオ', Meiryo, 'MS Pゴシック', Verdana, 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-size: 1rem;
  font-weight: normal;
  font-style: normal;
  line-height: 1.8rem;
  word-break: keep-all;
  color: #333;
}

body {
  padding-bottom: 40px;
  background-color: #eee;
}

main {
  margin-top: 80px;
}

h1 {
  font-size: 1.5rem;
  color: #337ab7;
  ;
}


/* form */

.form-container {
  max-width: 360px;
  margin: 0 auto;
  background: #fff;
  padding: 20px;
  box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.4);
  border-radius: 3px;
}

fieldset {
  margin: 0;
  padding: 0;
  border: transparent;
  max-width: 400px;
}

hr {
  margin-top: 20px;
  margin-bottom: 20px;
  border: 0;
  border-top-width: 0px;
  border-top-style: none;
  border-top-color: currentcolor;
  border-top: 1px solid #eee;
}

input[type="text"],
input[type="password"] {
  border: 0;
  padding: 5px 15px;
  font-size: 1rem;
  color: #aaa;
  border: solid 1px #ccc;
  margin: 10px 0 0;
  width: 100%;
  border-radius: 4px;
}

input[type="text"]:focus,
input[type="password"]:focus {
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 5px rgba(102, 175, 233, .6);
}

input[type=submit] {
  padding: 5px 15px;
  margin: 10px 0 0;
  background: #337ab7;
  color: #fff;
  border: 0 none;
  cursor: pointer;
  -webkit-border-radius: 4px;
  border-radius: 4px;
}

.text-align-center {
  text-align: center;
}

.text-align-right {
  text-align: right;
}


/* layout */

header {
  text-align: center;
  font-size: 25px;
  color: #fff;
  background: #286890;
  height: 60px;
  width: 100%;
  overflow: hidden;
}

header a {
  color: #f9f9f9;
  font-size: 1.5rem;
  text-decoration: none;
}

header p {
  margin: 15px;
}

header .left {
  float: left;
  position: relative;
  left: 150px;
}

header .right {
  float: right;
  position: relative;
  right: 150px;
}

.content {
  margin: 0 auto;
  margin-top: 100px;
  text-align: center;
  font-size: 4rem;
  color: #286890;
}