2
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【PHP簡易掲示板1】ログイン機能(mysqli 使用)

Last updated at Posted at 2018-11-11

必要なページをリストアップする

| ページ | ファイル名
|:-----------|------------:|:------------:|
| ログイン | authenticate.php
| 会員登録ページ | register.php
| ユーザフォーム | user_form.php

データベースの作成

CREATE TABLE `users` 
( `id` INT(255) NOT NULL AUTO_INCREMENT , `user_name` VARCHAR(50) NOT NULL 
, `nickname` VARCHAR(50) NOT NULL , `email` VARCHAR(255) NOT NULL 
, `password` VARCHAR(255) NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;

nicknameは重複してはいけないので、以下のSQL も加えて実行します。

ALTER TABLE `users` ADD UNIQUE(`nickname`);

ユーザフォームの作成。

ログイン画面、ユーザ登録画面の両方でユーザフォームを読みこみます。

user_form.php
<?php $fname = basename( $_SERVER['PHP_SELF'] , ".php");?>
<body>
  <div class="container">
    <div class="col-xs-8 col-xs-offset-2">
      <div class="card border-info">
          <div class="card-header h1"><?php print ( FORMTITLE ); ?></div>
          <div class="card-body">
            <form method="POST">
              <div class="form-group">
                <p>ユーザ名</p>
                <input type="text"  class="form-control" name="user_name" placeholder="お名前を10文字以内で入力してください" required />
              </div>
              <?php if ( $fname != 'authenticate' ): ?>
              <div class="form-group">
                <p>ニックネーム</p>
                <input type="text"  class="form-control" name="nickname" placeholder="nicknameを入力してください" required />
              </div>
              <div class="form-group">
                <p>メールアドレス</p>
                <input type="email"  class="form-control" name="email" placeholder="Emailで入力してください" required />
              </div>
              <?php endif; ?>
              <div class="form-group">
                <p>パスワード:<span id ='password_length'>0</span>文字<span>( 半角英数字5から15文字以内でお願いします。)</span></p>
                <input type="password"  class="form-control" name="password" placeholder="Passwordを5~15文字以内で入力してください"  required />
              </div>
              <button type="submit" class="btn btn-lg btn-info" name= "action"><?php print ( BUTTONTEXT ); ?></button>
              <a href="<?php print ( URL ); ?>"><?php print ( LINKTEXT ); ?></a>
            </form>
          </div>
      </div>
    </div>
  </div>
</body>
</html>

navbarの作成

各ファイル共通で読みこむnavbarを作成します。
isAuthenticated()関数は後ほど、作成いたします。

navbar.php
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>
  </title>
  <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
</head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
</script>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
  <div class="container-fluid">
    <div class="navbar-header">
      <a class="navbar-brand">
        php-bulletinboard
      </a>
    </div>
    <div class="collapse navbar-collapse" id="navbarEexample">
      <ul class="navbar-nav mr-auto">
        <?php if ( ! isAuthenticated() ): ?>
        <li class="nav-item">
          <a class="nav-link text-white" href="register.php">新規登録</a>
        </li>
        <li class="nav-item">
          <a class="nav-link text-white" href="authenticate.php">ログイン</a>
        </li>
        <?php else: ?>
        <li class="nav-item">
          <a class="nav-link text-white" href="signout.php">ログアウト</a>
        </li>
        <?php endif; ?>
      </ul>
    </div>
  </div>
</nav>
</body>

defineで各ページのユーザフォームで使う定数を定義します。

register.php
<?php 

include ( "common.php" );
include ( "session.php" );
include ( "user.php" );
include ( "navbar.php" );

// submitボタンが押された場合の処理
if ( isset( $_POST['action'] ) ) {
    user_register( $_POST['user_name'], $_POST['nickname'], $_POST['email'] , $_POST['password'] ));
}

define( "FORMTITLE", "アカウント作成" );
define( "BUTTONTEXT", "アカウントを作成する" );
define( "URL", "authenticate.php" );
define( "LINKTEXT", "ログインはこちら" );

require ( "user_form.php" );
authenticate.php
<?php 

include ( "dbconfig.php" );
include ( "common.php" );
include ( "session.php" );
include ( "navbar.php" );

// submitボタンが押された場合の処理
if ( isset( $_POST['action'] ) ) {
    authenticate( $_POST['user_name'], $_POST['password'] );
}

define( "FORMTITLE", "ログインはこちら" );
define( "BUTTONTEXT", "ログインする" );
define( "URL", "register.php" );
define( "LINKTEXT", "会員登録はこちら" );

require ( "user_form.php" );

データベース接続処理

dbconfig.php
<?php
// データベースの接続 

function get_db() {
    static $mysqli;

    $HOST = "localhost";
    $USERNAME = "root";
    $PASSWORD = "";
    $DBNAME = "bulletinboard";

    $mysqli = new mysqli( $HOST, $USERNAME, $PASSWORD, $DBNAME );

    if ( $mysqli->connect_error ){
        print $mysqli->connect_error();
        exit;
    } else {
        $mysqli->set_charset( "utf8" );
    }

    return $mysqli;
}


function escape( $str ) {
    $mysqli = get_db();

    return $mysqli->real_escape_string( $str );
}

登録処理

user.php
<?php

// 登録処理
function user_register( $user_name, $nickname, $email, $password ) {

    $errors = user_validation( $user_name, $nickname, $email, $password );

    if ( count( $errors ) == 0 ) {
        user_insert( $user_name, $nickname, $email, $password );
    }

    error_display( $errors );
}

// ユーザのバリデーション(若干抜けてる部分があるかもしれません)
function user_validation( $user_name, $nickname, $email, $password ) {

    $errors = array();

    if ( ! mb_strlen( trim( $user_name ) ) ) {
        $errors[] = "ユーザ名が空です";
    } else if ( mb_strlen( trim( $user_name ) ) > 15 ) {
        $errors[] = "ユーザ名は15文字以内で入力してください";
    } else if ( is_numeric( $user_name ) ) {
        $errors[] = "ユーザ名は文字列でなくてはいけません";
    }

    if ( ! mb_strlen( trim( $nickname ) ) ) {
        $errors[] = "nicknameが空です";
    } else if ( mb_strlen( trim( $nickname ) ) > 15 ) {
        $errors[] = "nicknameは15文字以内で入力してください";
    } else if ( ! isUniq( 'users', 'nickname' , trim( $nickname ) ) ) {
        $errors[] = "nicknameが重複しています";
    } else if ( is_numeric( $nickname ) ) {
        $errors[] = "nicknameは文字列でなくてはいけません";
    }

    if ( ! mb_strlen( trim( $email ) ) ) {
        $errors[] = "emailが空です";
    } else if ( mb_strlen( trim( $email ) ) > 255 ) {
        $errors[] = "emailは255文字以内で入力してください";
    } 

    if ( ! mb_strlen( trim( $password ) ) ) {
        $errors[] = "passwordが空です";
    } else if ( ! preg_match( '/^[0-9a-z]{5,15}$/', trim( $password ) ) ) {
        $errors[] = "passwordは半角英数字5文字から15文字以内でお願いします";
    }   

    return $errors;
}

function user_insert( $user_name, $nickname, $email, $password ) {
    // データをエスケープ
    $user_name = escape( $user_name );
    $nickname = escape( $nickname );
    $email = escape( $email );
    $password = escape( $password );
    $password = password_hash( $password, PASSWORD_DEFAULT ); // password hash化

    $query = "INSERT INTO users 
                ( user_name, nickname,  email, password 
            ) VALUES (
                '$user_name', '$nickname', '$email', '$password'
            )";
    
    $result = query( $query );

    print 'ユーザの作成に成功しました';

    $result->close();
}

ログイン処理

session.php
<?php

session_start();
session_regenerate_id();
$fname = basename( $_SERVER['PHP_SELF'] , ".php");
var_dump( $_SESSION );


// ユーザ新規登録画面、ログイン画面のどちらかかつログアウトページでない場合
if ( $fname == 'authenticate' || $fname == 'register' && $fname != 'signout') {
    if ( isAuthenticated() ) {
        header( "Location: root.php" );
        exit();
    } 
// ユーザ新規登録画面、ログイン画面以外
} else if ( ! isAuthenticated() ) { 
    // ログインしていない場合はログイン画面に遷移  
    header( "Location: authenticate.php" );
    exit();
} else {
    // ログインしている場合は、前回ログインからの時間が30分以上ならsessionを破棄する
    $now = strtotime("now");
    $session_last = session_get( 'session_created_at' );
    $elapsed_time = $now - $session_last;

    if( $elapsed_time > 1800 ){
        session_clear();
        print 'ログイン後、30分が経過したのでログアウトします お手数ですが、リロードして再度ログインをお願いいたします';
        exit();
    }
} 

function authenticate( $user_name, $password ) {

    $user_name = escape( $user_name );
    $password = escape( $password );

    $query = "
        SELECT * FROM users 
        WHERE user_name='$user_name'
        ";

    $result = query( $query );

    while ($row = $result->fetch_assoc()) {
        $user_id = $row['id'];
        $user_name = $row['user_name'];
        $nickname = $row['nickname'];
        $email = $row['email'];
        $db_password = $row['password'];
    }

    // データベースの切断
    $result->close();

    if ( password_verify( $password, $db_password ) ) {
        session_set( 'user_id', $user_id );
        session_set( 'user_name', $user_name );
        session_set( 'nickname', $nickname );
        session_set( 'email', $email );
        session_set( 'password', $db_password );
        session_set( 'session_created_at', strtotime( 'now' ) );
        session_regenerate_id( true );
        header( "Location: posts.php" );
        exit;
    } else {
        print 'ログインに失敗しました';
    }
}

function session_set( $key, $value ) {
    $_SESSION[$key] = $value;    
}

function session_clear() {
    $_SESSION = array();
}

// ログインしていればtrueを返却、していなければfalseを返却
function isAuthenticated()
{
    if ( isset( $_SESSION['user_name'] ) ) {
        return true;
    }

    return false;
}

function session_get( $key ) {
    if ( isset( $_SESSION[$key] ) ) {
        return $_SESSION[$key];
    } 

    return null;
}

複数回行う処理は関数にします。

common.php

function h( $str ) {
    return htmlspecialchars( $str, ENT_QUOTES, 'UTF-8');
}

function query(  $query ) {
    $mysqli = get_db();

    $result = $mysqli->query( $query );

    if ( ! $result ) {
        print 'クエリが失敗しました' . "Errormessage: %s\n" . $mysqli->error;
        $mysqli->close();
        exit();
    }

    return $result;
}   

// テーブル名、カラム、値を指定してテーブルに、指定したカラムを持つ値のレコードが存在するか判定
function isUniq( $table, $column, $value ) {

    $value = escape( $value );

    $query = "
        SELECT * FROM $table
        WHERE $column = '$value'
        ";

    $result = query( $query );

    if ( mysqli_num_rows( $result ) == 0  ) {
        return true;
    }

    return false;
}


function get_current_datetime() {
    $now = new DateTime();
    $now = $now->format('Y-m-d H:i:s');

    return $now;
}

function error_display( $errors ) {   
    foreach ( $errors as $error ) {
        ?>
        <div class="container">
            <div class="alert alert-dismissible alert-warning">
            <?php print h( $error ) ?>
            </div>
        </div>
        <?php
    }
}

サインアウト

navbar.phpに以下を追記。

<li class="nav-item">
   <a class="nav-link text-white" href="signout.php">ログアウト</a>
</li>
signout.php
<?php 

include ( "session.php" );
include ( "navbar.php" );

$_SESSION = array();

header( "Location: authenticate.php" );
exit();
2
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?