必要なページをリストアップする
| ページ | ファイル名
|:-----------|------------:|:------------:|
| ログイン | 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();