PHPを使って様々なプログラムにログイン機能を追加

学習目的

ここでは、既存のプログラム(在庫管理・ショッピングカート・掲示板・予約フォームなど)に 「新規会員登録」+「ログイン機能」 をあとから追加できるようになることを目指します。

  • メインとなるプログラムを「会員制」に切り替える練習を通じて、認証とセッション管理の基礎を理解する。
  • 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
);
  • usernameUNIQUE制約 をつけて、同じユーザー名は登録できないようにする
  • 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(登録処理)

  1. common.php を読み込む
    • DB接続・セッション開始が使えるようになる
  2. POSTデータを受け取る
    • $_POST['username']$_POST['password']trim() で前後の空白を削除して変数に入れる
  3. バリデーションチェック
    • もし空なら exit('エラーメッセージ'); で処理を止める
    • (発展として「文字数チェック」や「パスワードの複雑性チェック」も考えてみよう)
  4. パスワードをハッシュ化
    • password_hash($password, PASSWORD_DEFAULT) を使う
    • 元のパスワードは保存せず、ハッシュ化された値だけを保存する
  5. SQLを準備して実行
    • $stmt = $pdo->prepare("INSERT INTO members (username, password) VALUES (?, ?)");
    • $stmt->execute([$username, $hashed]);
  6. エラーハンドリング
    • try ... catch で囲む
    • $e->getCode() === '23000' の場合は「ユーザー名が既に存在する」と表示する
    • それ以外は $e->getMessage() を表示
  7. 成功したらメッセージを出す
    • 「登録完了!ログインはこちら」と案内リンクを表示
解答例
<?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 (ログインするためのフォーム)

  1. common.php を読み込む(セッションを使うため)
  2. formactionauth.php にする
  3. ユーザー名は name="username"、パスワードは name="password" にする
  4. 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の解説

このプログラムでは、ログインフォームに入力された内容を 送信する前にチェック しています。

  1. DOM要素を取得const username = document.getElementById('username'); const password = document.getElementById('password'); const errorUsername = document.getElementById('errorUsername'); const errorPassword = document.getElementById('errorPassword');
    • getElementById を使って、HTMLの入力欄やエラー表示用の <p> を取得しています。
  2. blurイベントの設定username.addEventListener('blur', function() { ... });
    • blur は「入力欄からフォーカスが外れたとき」に実行されます。
    • 入力が空の場合はエラーメッセージを表示し、入力されていればエラーを隠します。
  3. classListで表示切り替えerrorUsername.classList.remove('hidden'); errorUsername.classList.add('show');
    • CSSのクラスを切り替えて、エラーメッセージを フェードイン / フェードアウト させています。
    • .hidden → 非表示、.show → 表示。

5.auth.php(認証処理)

  1. common.php を読み込んでセッションを使えるようにする
  2. $_POST['username']$_POST['password']trim() して変数に入れる
  3. どちらかが空なら exit('エラーメッセージ'); で処理を終了
  4. SQLで SELECT * FROM members WHERE username = ? を実行し、ユーザー情報を取得
  5. ユーザーが存在し、かつ password_verify() でパスワード一致ならログイン成功
    • if ($user && password_verify($password, $user['password']))
  6. 成功したら $_SESSION['member'] にユーザー名を保存し、member.php にリダイレクト
  7. 失敗したら「エラーメッセージ」を出し、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(会員専用ページ)

このページは、各プログラム(在庫処理・カート・掲示板など)でメインとなるページです。

  1. 最初に common.php を読み込んでセッションを利用できるようにする
  2. if (empty($_SESSION['member'])) { ... } を使って、ログインしていなければ login.php に強制移動させる
  3. ログイン中なら「ようこそ ○○さん」とユーザー名を表示する
  4. ページの最後に 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(ログアウト処理)

ログイン情報(セッション情報)を破棄するためのプログラムです。

  1. 最初に common.php を読み込む
  2. $_SESSION = []; でセッション変数を空にする
  3. もしセッションIDが残っていれば、クッキーごと削除する
    • setcookie(session_name(), '', time() - 42000, '/'); を使う
  4. session_destroy(); でサーバーのセッション情報を削除する
  5. 最後に 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;
}

これで在庫一覧や更新処理が「会員専用」になります。

Webtraining - Webトレは、 初心...
PHPを使った在庫テーブルの更新とページネーション | Webtraining - Webトレは、 初心者から実務までのWeb... 学習目標 データベースに登録された在庫データを一覧表示する ページネーション(ページ分割) を実装して、10件ずつ表示する 各行の在庫数をフォームで更新できるようにす...

課題2:ショッピングカートにログインを追加

  • cart.php を会員専用ページにする
  • カート情報をセッションだけでなく「ユーザーID」と紐づける
  • ログインしている人ごとにカートの中身が変わるようにしてみる
Webtraining - Webトレは、 初心...
PHPで作る在庫と連動するショッピングカート | Webtraining - Webトレは、 初心者から実務までのWeb学習ト... 学習目標 データベースの商品データを一覧表示する 在庫数が0の商品は購入できないようにする カートに入れると在庫数が減る(UPDATE文) セッションを使ってユーザーごと...