学習目的
ここでは、既存のプログラム(在庫管理・ショッピングカート・掲示板・予約フォームなど)に 「新規会員登録」+「ログイン機能」 をあとから追加できるようになることを目指します。
- メインとなるプログラムを「会員制」に切り替える練習を通じて、認証とセッション管理の基礎を理解する。
password_hash()/password_verify()を利用し、安全なパスワード保存と認証処理を体験する。- セッションを用いて「ログイン状態を保持する仕組み」を実装できるようになる。
- 実際の現場でよくある「後からログイン機能を追加する」という開発フローを体験し、応用力を身につける。
フォルダとファイル名
php-login フォルダを作成します。ファイル構成は以下になります。
php-login/ ├── common.php # DB接続と共通関数 ├── register.php # 会員登録フォーム ├── register_done.php # 登録完了ページ ├── login.php # ログインフォーム ├── auth.php # ログイン認証(DBから照合) ├── member.php # ログイン後の会員専用ページ(実際は「在庫一覧」「カート画面」「掲示板」など差し替え可能) ├── logout.php # ログアウト処理 └── members.sql # 会員テーブル作成用SQL
member.php はダミーの「会員専用ページ」として用意しておき、
実際のプロジェクト(在庫管理やカートなど)ではここを差し替えるイメージです。
フローチャート
register.php(会員登録フォーム:ユーザー名・パスワード入力)
↓
register_done.php(データベースに保存)
↓
login.php(ログインフォーム)
↓
auth.php(ログイン認証:データベースと照合)
→ 成功 → member.php(会員専用ページ)
→ 失敗 → login.php に戻る
↓
logout.php(セッション情報の破棄)
→ login.php に戻る
データベース:会員テーブル作成
まずは members テーブル を用意します。
すでに php-db 学習で作成済みの場合は、このステップは不要です。
CREATE TABLE members (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
usernameに UNIQUE制約 をつけて、同じユーザー名は登録できないようにするpasswordはハッシュ化文字列(60文字以上)を保存するので、余裕をもってVARCHAR(255)にしておくcreated_atは自動的に登録日時が入る
1.common.phpを作成しておこう
php-login フォルダ内に common.php を用意します。
これは「DB接続」と「セッション開始」をまとめた共通ファイルです。
他のフォルダにある common.php をコピーしても構いませんが、
そのファイルの先頭に session_start(); がない場合は、必ず追記してください。
解答例
<?php
session_start();
// DB接続
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';
$user = 'root';
$password = '';
try {
$pdo = new PDO($dsn, $user, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo "DB接続エラー:" . $e->getMessage();
exit;
}
// エスケープ関数
function h($str)
{
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
ここまでで「ログイン用の土台」が完成!
2.register.php(会員登録フォーム)
ここでは「ユーザー名」と「パスワード」を入力できるだけのシンプルなフォームを作成します。
セッションを使うので、先頭で common.php を読み込んでおきましょう。style.cssは作成しておきましょう。
- common.php の読み込み
- → セッションを使うので必ず最初に
require 'common.php';を書く
- → セッションを使うので必ず最初に
- form の action
- →
action="register_done.php"にして、登録処理用のプログラムへ送信する
- →
- ユーザー名入力欄
- →
<input type="text" name="username"> - → name属性
"username"が送信キーになる
- →
- パスワード入力欄
- →
<input type="password" name="password"> - → name属性
"password"が送信キーになる
- →
- method=”post”
- →
postメソッドで安全にデータを送る(URLに表示されない)
- →
- style.css の作成
解答例
<?php require 'common.php'; ?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>新規会員登録</title>
<link rel="stylesheet" href="style.css">
<style>
.err {
text-align: left;
color: red;
font-size: 0.9em;
}
.err.hidden {
display: none;
}
.err.show {
display: block;
}
</style>
</head>
<body>
<main>
<h1>新規会員登録</h1>
<form action="register_done.php" method="post">
<div>
<label for="username">ユーザー名</label><br>
<input type="text" name="username" id="username">
<p id="errorUsername" class="err hidden">ユーザー名を入力してください。</p>
</div>
<div>
<label for="password">パスワード</label><br>
<input type="password" name="password" id="password">
<p id="errorPassword" class="err hidden">パスワードを入力してください。</p>
</div>
<button type="submit">登録</button>
</form>
<p>
<a href="login.php">ログインはこちら</a>
</p>
</main>
<script>
const username = document.getElementById('username');
const password = document.getElementById('password');
const errorUsername = document.getElementById('errorUsername');
const errorPassword = document.getElementById('errorPassword');
// ユーザー名のエラー
username.addEventListener('blur', function() {
if (username.value.trim() === '') {
errorUsername.classList.remove('hidden');
errorUsername.classList.add('show');
} else {
errorUsername.classList.add('hidden');
errorUsername.classList.remove('show');
}
});
// パスワードのエラー
password.addEventListener('blur', function() {
if (password.value.trim() === '') {
errorPassword.classList.remove('hidden');
errorPassword.classList.add('show');
} else {
errorPassword.classList.add('hidden');
errorPassword.classList.remove('show');
}
});
</script>
</body>
</html>
3.register_done.php(登録処理)
- common.php を読み込む
- DB接続・セッション開始が使えるようになる
- POSTデータを受け取る
$_POST['username']と$_POST['password']をtrim()で前後の空白を削除して変数に入れる
- バリデーションチェック
- もし空なら
exit('エラーメッセージ');で処理を止める - (発展として「文字数チェック」や「パスワードの複雑性チェック」も考えてみよう)
- もし空なら
- パスワードをハッシュ化
password_hash($password, PASSWORD_DEFAULT)を使う- 元のパスワードは保存せず、ハッシュ化された値だけを保存する
- SQLを準備して実行
$stmt = $pdo->prepare("INSERT INTO members (username, password) VALUES (?, ?)");$stmt->execute([$username, $hashed]);
- エラーハンドリング
try ... catchで囲む$e->getCode() === '23000'の場合は「ユーザー名が既に存在する」と表示する- それ以外は
$e->getMessage()を表示
- 成功したらメッセージを出す
- 「登録完了!ログインはこちら」と案内リンクを表示
解答例
<?php
require 'common.php';
$username = trim($_POST['username'] ?? '');
$password = trim($_POST['password'] ?? '');
// バリデーション
if ($username === '' || $password === '') {
exit('ユーザー名とパスワードは必須です。');
}
// パスワードをハッシュ化
$hashed = password_hash($password, PASSWORD_DEFAULT);
// 登録
try {
$stmt = $pdo->prepare("INSERT INTO members (username, password) VALUES (?, ?)");
$stmt->execute([$username, $hashed]);
echo "登録完了!<a href='login.php'>ログインする</a>";
} catch (PDOException $e) {
if ($e->getCode() === '23000') { // UNIQUE制約エラー
echo "このユーザー名は既に使われています。<a href='register.php'>戻る</a>";
} else {
echo "エラー:" . $e->getMessage();
}
}
4.login.php (ログインするためのフォーム)
common.phpを読み込む(セッションを使うため)formのactionをauth.phpにする- ユーザー名は
name="username"、パスワードはname="password"にする - method は
"post"にして送信する
解答例
<?php require 'common.php'; ?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ログイン</title>
<link rel="stylesheet" href="style.css">
<style>
.err {
text-align: left;
color: red;
font-size: 0.9em;
}
.err.hidden {
opacity: 0;
transition: opacity 0.5s ease;
}
.err.show {
opacity: 1;
}
</style>
</head>
<body>
<main>
<h1>ログイン</h1>
<form action="auth.php" method="post">
<div>
<label for="username">ユーザー名</label><br>
<input type="text" name="username" id="username">
<p id="errorUsername" class="err hidden">ユーザー名を入力してください。</p>
</div>
<div>
<label for="password">パスワード</label><br>
<input type="password" name="password" id="password">
<p id="errorPassword" class="err hidden">パスワードを入力してください。</p>
</div>
<button type="submit">ログイン</button>
</form>
<p>
<a href="register.php">新規会員登録はこちら</a>
</p>
</main>
<script>
const username = document.getElementById('username');
const password = document.getElementById('password');
const errorUsername = document.getElementById('errorUsername');
const errorPassword = document.getElementById('errorPassword');
// ユーザー名のエラー
username.addEventListener('blur', function() {
if (username.value.trim() === '') {
errorUsername.classList.remove('hidden');
errorUsername.classList.add('show');
} else {
errorUsername.classList.add('hidden');
errorUsername.classList.remove('show');
}
});
// パスワードのエラー
password.addEventListener('blur', function() {
if (password.value.trim() === '') {
errorPassword.classList.remove('hidden');
errorPassword.classList.add('show');
} else {
errorPassword.classList.add('hidden');
errorPassword.classList.remove('show');
}
});
</script>
</body>
</html>
JavaScriptの解説
このプログラムでは、ログインフォームに入力された内容を 送信する前にチェック しています。
- DOM要素を取得
const username = document.getElementById('username'); const password = document.getElementById('password'); const errorUsername = document.getElementById('errorUsername'); const errorPassword = document.getElementById('errorPassword');getElementByIdを使って、HTMLの入力欄やエラー表示用の<p>を取得しています。
- blurイベントの設定
username.addEventListener('blur', function() { ... });blurは「入力欄からフォーカスが外れたとき」に実行されます。- 入力が空の場合はエラーメッセージを表示し、入力されていればエラーを隠します。
- classListで表示切り替え
errorUsername.classList.remove('hidden'); errorUsername.classList.add('show');- CSSのクラスを切り替えて、エラーメッセージを フェードイン / フェードアウト させています。
.hidden→ 非表示、.show→ 表示。
5.auth.php(認証処理)
common.phpを読み込んでセッションを使えるようにする$_POST['username']と$_POST['password']をtrim()して変数に入れる- どちらかが空なら
exit('エラーメッセージ');で処理を終了 - SQLで
SELECT * FROM members WHERE username = ?を実行し、ユーザー情報を取得 - ユーザーが存在し、かつ
password_verify()でパスワード一致ならログイン成功if ($user && password_verify($password, $user['password']))
- 成功したら
$_SESSION['member']にユーザー名を保存し、member.phpにリダイレクト - 失敗したら「エラーメッセージ」を出し、
login.phpに戻るリンクを表示
解答例
<?php
require 'common.php';
$username = trim($_POST['username'] ?? '');
$password = trim($_POST['password'] ?? '');
// バリデーション
if ($username === '' || $password === '') {
exit('ユーザー名とパスワードを入力してください。<a href="login.php">戻る</a>');
}
// ユーザー検索
$stmt = $pdo->prepare("SELECT * FROM members WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
// 認証判定
if ($user && password_verify($password, $user['password'])) {
// ログイン成功 → セッションに保存
$_SESSION['member'] = $user['username'];
header('Location: member.php');
exit;
} else {
// ログイン失敗 → メッセージ表示
echo <<<HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ログイン失敗</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>ログイン失敗</h1>
<p>ユーザー名またはパスワードが違います。</p>
<p><a href="login.php">ログイン画面に戻る</a></p>
</main>
</body>
</html>
HTML;
}
6.member.php(会員専用ページ)
このページは、各プログラム(在庫処理・カート・掲示板など)でメインとなるページです。
- 最初に
common.phpを読み込んでセッションを利用できるようにする if (empty($_SESSION['member'])) { ... }を使って、ログインしていなければlogin.phpに強制移動させる- ログイン中なら「ようこそ ○○さん」とユーザー名を表示する
- ページの最後に
logout.phpへのリンクを置いて、ログアウトできるようにする
解答例
<?php
require 'common.php';
// ログインしていない場合はログイン画面へ
if (empty($_SESSION['member'])) {
header('Location: login.php');
exit;
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>会員専用ページ</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>ようこそ <?= h($_SESSION['member']) ?> さん</h1>
<p>このページは会員専用コンテンツです。</p>
<p><a href="logout.php">ログアウト</a></p>
</main>
</body>
</html>
- セッション変数のチェックで「ログイン必須ページ」を作れる
- ここを「在庫管理」「カート一覧」「掲示板」などに置き換えることで、既存プログラムを会員制にできる
- ログアウト後は
$_SESSIONがクリアされるので、再度アクセスしても login.php に飛ぶ
7.logout.php(ログアウト処理)
ログイン情報(セッション情報)を破棄するためのプログラムです。
- 最初に
common.phpを読み込む $_SESSION = [];でセッション変数を空にする- もしセッションIDが残っていれば、クッキーごと削除する
setcookie(session_name(), '', time() - 42000, '/');を使う
session_destroy();でサーバーのセッション情報を削除する- 最後に
header('Location: login.php');でログイン画面へ移動する
解答例
<?php
require 'common.php';
// セッションの中身を空にする
$_SESSION = [];
// セッションを完全に破棄
if (session_id() !== '' || isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time() - 42000, '/');
}
session_destroy();
// ログインページへリダイレクト
header('Location: login.php');
exit;
$_SESSION = [] … セッション変数の中身を空にするだけ(セッションIDは残る)
session_destroy() … サーバーに保存されているセッションデータを削除する
クッキー削除処理 … ブラウザに残っているセッションIDを強制的に無効化する
time() - 42000のように過去の時間を指定するとクッキーは削除される
これで完全にログアウトできる
style.cssの解答例
/* 共通レイアウト */
*{
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: sans-serif;
background: #f5f5f5;
margin: 0;
padding: 2rem;
}
main {
background: #fff;
max-width: 400px;
margin: 2rem auto;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
margin-bottom: 1.5rem;
font-size: 1.3rem;
}
/* フォーム関連 */
form div {
margin-bottom: 1rem;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 1rem;
box-sizing: border-box;
}
button {
width: 100%;
padding: 0.7rem;
border: none;
background: #007bff;
color: #fff;
border-radius: 4px;
font-size: 1rem;
cursor: pointer;
}
button:hover {
background: #0056b3;
}
/* メッセージ */
p {
text-align: center;
margin-top: 1rem;
}
a {
color: #007bff;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
応用ステップ:既存アプリにログインを追加してみよう
ここでは、php-login で学んだ会員登録・ログイン機能を、実際のアプリに組み込んでみます。
ここで作ったログインプログラムは、対象のフォルダにコピーして使ってみましょう。
課題1:在庫管理にログインを追加
stock.phpを会員専用ページにする- 冒頭で
common.phpを読み込む if (empty($_SESSION['member'])) { header('Location: login.php'); exit; }を追加して、ログイン必須にする- 更新処理を「ログインしている人だけ可能」にしてみる
ヒント:在庫管理にログインを追加
stock.phpの冒頭に以下を追加し、ログインしていない場合はlogin.phpに戻す
require 'common.php';
if (empty($_SESSION['member'])) {
header('Location: login.php');
exit;
}
これで在庫一覧や更新処理が「会員専用」になります。

課題2:ショッピングカートにログインを追加
cart.phpを会員専用ページにする- カート情報をセッションだけでなく「ユーザーID」と紐づける
- ログインしている人ごとにカートの中身が変わるようにしてみる

