PHP・MySQLを使った実践的な会員登録フローの実装方法

推奨の会員登録フロー

今回はウェブアプリケーションで使用される実践的な会員登録フローを、PHPとMySQLを使って実装する具体的な手順(ユーザー視点)について考えてみたいと思います。

  • ①メールアドレス入力~送信
  • ②確認メール受信
  • ③確認メール内のリンククリック
  • ④会員登録画面へのアクセス~入力
  • ⑤登録完了

最近先にメールアドレスだけ送信させる会員登録フローをよく見ると思います。

登録画面で会員情報を入力してもらってそのまま登録としてしまった方が早そうですが、なぜこんなまわりくどいやり方をするのでしょうか。

それには以下のようなメリットがあります。

先にメールアドレスを登録させるメリット

  • ・本人確認ができる
  • ・存在しないメールアドレスでの登録を回避できる
  • ・簡単なため登録の敷居が下がる

会員登録にあたって本人以外のメールアドレス、存在しないメールアドレスで登録ができてしまうというのは大きな問題です。

会員登録した覚えのない人のメールアドレスにサイトからのメールが届いてしまったりすれば問題ですし、存在しないメールアドレスでスパム的にユーザーを作られてしまうことも懸念されます。

そのため、先にメールアドレスを登録させて、そのメールアドレスに確認メールを送り、そのメールを確認したユーザーだけが登録画面に進むというステップを設ける必要があるのです。

今回はこのフローでの会員登録の実装方法について紹介したいと思います。
 


 

実装方法

実装にあたって必要となる要素を先に記載します。

  • ・データベース:pre_usersテーブル、usersテーブル
  • ・ファイル:db.php、regist_mail.php、regist.php

ファイルは通常はモデル・ビュー・コントローラーに分割して実装するのですが今回はわかりやすいように処理をひとつのファイルに集約します。

データベース

pre_usersテーブル

  • ・id(int)
  • ・email(text):メールアドレス
  • ・token(varchar10):アクセス許可用の認証文字列(リンクに引数として付加)
  • ・enabled(int):有効フラグ(1:有効 2:無効)
  • ・created(timestamp):生成日時

usersテーブル

  • ・id(int):会員ID
  • ・email(text):メールアドレス
  • ・password(text):パスワード
  • ・name(text):ユーザー名
  • ・created(timestamp):登録日時

データベースはメールアドレス送信時にメールアドレスを登録しておくための「pre_users」テーブルと、本会員登録を行う「users」テーブルのふたつを用意しておきます。

「pre_users」テーブルのtokenカラムには登録画面にアクセスする際に照合するための10桁のランダムな文字列を生成してセットしておきます。

enabledカラムは本登録完了時に2に変更し、以降このレコードのtokenで登録画面にアクセスできないようにするためのものです。

ファイル

db.php

<?
// データベース接続情報
define("CHARSET", "UTF-8");
define("DB_HOST", "");
define("DB_USER", "");
define("DB_PASS", "");
define("DB_NAME", "");

// メールアドレス照合
function emailExist($email){

  $db = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
  $db->query('SET NAMES utf8');
  $sql = "SELECT COUNT(*) as cnt FROM users WHERE email= :email;";
  $stt = $db->prepare($sql);
  $stt->bindParam(':email', $email);
  $stt->execute();
  $row = $stt->fetch();
    if($row['cnt']>=1){
      return true;
    } else {
      return false;
    }

}

// 仮登録
function insertPreusers($email, $token){

  $db = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
  $db->query('SET NAMES utf8');
  $sql = "INSERT INTO pre_users (email, token, enabled) VALUES (:email, :token, 1);";
  $stt = $db->prepare($sql);
  $stt->bindParam(':email', $email);
  $stt->bindParam(':token', $token);
  $ret = $stt->execute();
  return $ret;

}

// トークンから仮登録情報取得
function selectPreusersToken($token){

  $db = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
  $db->query('SET NAMES utf8');
  $sql = "SELECT id, email, token FROM pre_users WHERE token = :token AND enabled = 1;";
  $stt = $db->prepare($sql);
  $stt->bindParam(':token', $token);
  $ret = $stt->execute();
  while($row=$stt->fetch()){
    $result['id'] = $row['id'];
    $result['email'] = $row['email'];
    $result['token'] = $row['token'];
  }
  if(isset($result)){
    return $result;
  }

}

// 本登録
function insertUsers($email, $password, $name){

  $db = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
  $db->query('SET NAMES utf8');
  $sql = "INSERT INTO users (email, password, name) VALUES (:email, :password, :name);";
  $stt = $db->prepare($sql);
  $stt->bindParam(':email', $email);
  $stt->bindParam(':password', $password);
  $stt->bindParam(':name', $name);
  $ret = $stt->execute();
  return $ret;

}

// 仮登録無効化
function updatePreusersDisabled($email, $token){

  $db = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
  $db->query('SET NAMES utf8');
  $sql = "UPDATE pre_users SET enabled = 2 WHERE email = :email AND token = :token;";
  $stt = $db->prepare($sql);
  $stt->bindParam(':email', $email);
  $stt->bindParam(':token', $token);
  $ret = $stt->execute();
  return $ret;

}
?>

モデル(データベース操作)部分のファイルです。

regist_mail.php


<?php require_once('./db.php'); ?>
<html>
<head></head>
<body>
<?php if($_POST['mode']=="send"){ ?>
  <?php if(!emailExist($_POST['email'])){ ?>
    <?php $token = createToken(); // トークン生成関数 ?>
    <?php if(insertPreusers($_POST['email'], $token)){ ?>
      <?php sendPreMail($_POST['email'], $token); // 確認メール送信関数 ?>
      <p>ご登録頂いたメールアドレスに確認メールを送信致しましたので、ご確認の上本登録にお進みください。</p>
    <?php } else { ?>
      <p>メールアドレスを正常に登録できませんでした。</p>
    <?php } ?>
  <?php } else { ?>
    <p>このメールアドレスは登録できません。</p>
  <?php }  ?>
<?php } else { ?>
  <form action="./regist_mail.php" method="post">
    <input type="email" name="email" value="" placeholder="メールアドレス"/>
    <input type="hidden" name="mode" value="send" />
    <input type="submit" value="送信する" />
  </form>
<?php } ?>
</body>
</html>

メールアドレスの入力~送信後、そのメールアドレスが既に本登録済みか確認し、本登録済でなければ、ランダム文字列10文字のトークンを生成、メールアドレスとセットでpre_usersテーブルに登録します。

登録が成功したら確認メールを送信後表示を出します。

※トークン生成関数と確認メール送信関数の詳細はここでは省きます。

regist.php


<?php require_once('./db.php'); ?>
<html>
<head></head>
<body>
<?php if($_POST['mode']=="regist"){ ?>
  <?php if(insertUsers($_POST['email'], $_POST['password'], $_POST['name'])){ ?>
    <?php updatePreusersDisabled($_POST['email'], $_POST['token']); ?>
    <p>登録が完了しました。</p>
  <?php } else { ?>
    <p>登録に失敗しました。</p>
  <?php } ?>
<?php } else { ?>
  <?php if(!empty($_GET['token'])){ ?>
    <?php if($preUserData = selectPreusersToken($_GET['token'])){ ?>
      <form action="./regist.php" method="post">
        <input type="hidden" name="email" value="<?php echo $preUserData('email'); ?>" />
        <input type="hidden" name="token" value="<?php echo $preUserData('token'); ?>" />
        <input type="password" name="password" value="" placeholder="パスワード"/>
        <input type="text" name="name" value="" placeholder="ニックネーム"/>
        <input type="hidden" name="mode" value="regist" />
        <input type="submit" value="登録する" />
      </form>
    <?php } else { ?>
      <p>このページは存在しません。</p>
    <?php } ?>
  <?php } else { ?>
    <p>このページは存在しません。</p>
  <?php } ?>
<?php } ?>
</body>
</html>

この登録画面には、確認メール内に出力された以下トークンの引数付きのURL形式でアクセスされるものとする。

regist.php?token=**********

トークンが発行されているメールアドレスのみアクセスができるように制御を行う必要がある。

トークンが照合できたらフォームを表示し、usersテーブルに会員情報を登録、また、pre_usersのenabledを2にして登録を終了する。
 

まとめ

いかがでしたでしょうか?割とシンプルに実装できることがおわかりになるかと思います。

今回はシステムの構成になるべく依存しないように制御部(コントローラー)と表示(ビュー)は分割せずにひとつのファイルに実装としましたが、本来はこのふたつは分離して実装した方が管理はしやすいかと思います。

登録画面の実装はいろいろな方法があると思いますので、実際の実装時には環境や人によってだいぶ変わってくるかと思います。

尚、本コードは実際にデバッグしながら書き進めたものではないため、うまく動かないところなどあればコメントからお問合せください。

都度修正をいたします。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です