PHPで作る今日のおみくじ(セッション)

PHPとデータベースを使って、今日のおみくじを作成します。

完成イメージ

フォルダ構成とファイル名

  • 作成フォルダ:php-practice-omikuji作成ファイル:omikuko_index.phpomikuji.php
  • 使用CSSファイル(任意):style.cssは任意で作成

おみくじの最初の画面

  • omikuji_index.phpstyle.css を作成
  • ボタンのリンク先は omikuji.php
  • デザインは自由、CSSは任意で追加
omikuji_index.php の例
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>今日のおみくじ</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <h1>今日のおみくじ</h1>
    <p>運勢を占ってみましょう!</p>
    <a href="omikuji.php" class="btn">おみくじを引く</a>
</body>

</html>

CSVファイルを作成

下記の内容をエクセルで作成し、omikuji_data.csv として保存。
1行目はカラム名にします)

id,fortune,lucky_item,color_code,color_name,image
1,大吉!最高の一日です。思い切った行動が吉。新しい挑戦にツキがあります。,赤い小物,#ff0000,赤,omikuji_daikichi.png
2,中吉。落ち着いて行動すれば良い方向に進みます。人との協力がカギ。,お茶,#228b22,緑,omikuji_chuukichi.png
3,小吉。無理せずコツコツ積み上げましょう。地道な努力が結果につながります。,本,#0000ff,青,omikuji_syoukichi.png
4,吉。身近な人との会話にヒントあり。ちょっとした雑談から運気が上がります。,メモ帳,#ffa500,オレンジ,omikuji_kichi.png
5,末吉。小さな喜びが見つかる日。あまり欲張らずマイペースで。,鉛筆,#8a2be2,紫,omikuji_suekichi.png
6,凶。焦らず慎重に進めると回避できます。無理は禁物。,水,#555555,グレー,omikuji_kyou.png
7,大凶。今日はゆっくり休んで充電する日。重要な決断は先送りに。,お守り,#000000,黒,omikuji_daikyou.png

phpMyAdminのインポート

  • phpMyAdminは、CSVファイルをデータベースのテーブルとしてインポートすることが可能です。
  • phpMyAdmin で omikuji テーブルを作成(またはインポート時に作成)
  • インポート画面で上記CSVを選択
  • 「最初の行をカラム名として使用」 にチェック
  • インポート後、主キーを設定
  • 構造タブ → id列の「PRIMARY KEY」をクリック
  • またはSQLタブで次を実行
ALTER TABLE omikuji ADD PRIMARY KEY(id);

この方法だと簡単にテーブルが自動で作られますが、カラムの型や主キー、AUTO_INCREMENTなどの制約は自動で設定されません。本来は先にテーブル設計をして、CREATE TABLE で作成し、データのみをインポートする方がおすすめです。

おみくじプログラムを作成

作成したデータベーステーブルを使っておみくじプログラムを以下の工程で考えてみましょう。表示させたい画像はあらかじめフォルダに、データベーステーブルの画像カラムにある名前に合わせておきましょう。

完成イメージ

  • common.php を作成し、PDOでデータベース接続
  • omikuji.php で次の処理を実装 (ランダムに 1〜7 の数字を取得)
    • $rand = random_int(1, 7);
  • 取得した数字を ID として SELECT
  • データベースから1件取得して $row に格納
  • HTMLに出力し、背景色を $row['color_code'] に設定
解答例
<?php
require 'common.php';

// STEP 1: ランダムに 1〜7 の数字を取得
$rand = random_int(1, 7);

// STEP 2: データベースから該当の行を1件取得
$sql = $pdo->prepare('SELECT * FROM omikuji WHERE id=?');
$sql->execute([$rand]);
$row = $sql->fetch(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>おみくじ結果</title>
    <link rel="stylesheet" href="style.css">
</head>

<body style="background-color: <?= h($row['color_code']); ?>;">

    <?php if (!empty($row)): ?>
        <div class="card">
            <!-- おみくじ画像 -->
            <img src="img/<?= h($row['image']) ?>" alt="<?= h($row['fortune']) ?>">

            <!-- 運勢 -->
            <h2><?= h($row['fortune']) ?></h2>

            <!-- ラッキーアイテム -->
            <p>ラッキーアイテム:<?= h($row['lucky_item']) ?></p>

            <!-- ラッキーカラー -->
            <p>
                ラッキーカラー:
                <span style="color: <?= h($row['color_code']); ?>">
                    <?= h($row['color_name']); ?>
                </span>
            </p>

            <!-- ボタン -->
            <p><a href="omikuji_index.php">もう一度引く</a></p>
        </div>
    <?php else: ?>
        <p>データが見つかりません。</p>
        <p><a href="omikuji_index.php">戻る</a></p>
    <?php endif; ?>
</body>

</html>

ステップアップ!確率を変えてみよう!

今のままだと random_int(1, 7) なので全ての結果が 同じ確率 で出ます。
大吉を出にくくしたり、大凶をもっとレアにしたりしてみましょう!

ヒント

100までの乱数を取得し、if文を使って確率を変えてみましょう。

// 1〜100の乱数を取得
$rand = random_int(1, 100);

// 乱数の範囲ごとに出すIDを変えてみましょう
// 例:1〜10 → 大吉(10%)
//     11〜30 → 中吉(20%)
//     31〜55 → 小吉(25%)
//     56〜75 → 吉(20%)
//     76〜85 → 末吉(10%)
//     86〜95 → 凶(10%)
//     96〜100 → 大凶(5%)

// $id に入った値をもとにデータベースから1件取得
$sql = $pdo->prepare('SELECT * FROM omikuji WHERE id=?');
$sql->execute([$id]);
$row = $sql->fetch(PDO::FETCH_ASSOC);
解答例
<?php
require 'common.php';

//ランダムに 1〜100 の数字を取得
$rand = random_int(1, 100);

//確率を変える
if ($rand <= 10) {
    $id = 1; // 大吉
} elseif ($rand <= 30) {
    $id = 2; // 中吉
} elseif ($rand <= 55) {
    $id = 3; // 小吉
} elseif ($rand <= 75) {
    $id = 4; // 吉
} elseif ($rand <= 85) {
    $id = 5; // 末吉
} elseif ($rand <= 95) {
    $id = 6; // 凶
} else {
    $id = 7; // 大凶
}

// データベースから該当の行を1件取得
$sql = $pdo->prepare('SELECT * FROM omikuji WHERE id=?');
$sql->execute([$id]);
$row = $sql->fetch(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>おみくじ結果</title>
    <link rel="stylesheet" href="style.css">
</head>

<body style="background-color: <?= h($row['color_code']); ?>;">

    <?php if (!empty($row)): ?>
        <div class="card">
            <!-- おみくじ画像 -->
            <img src="img/<?= h($row['image']) ?>" alt="<?= h($row['fortune']) ?>">

            <!-- 運勢 -->
            <h2><?= h($row['fortune']) ?></h2>

            <!-- ラッキーアイテム -->
            <p>ラッキーアイテム:<?= h($row['lucky_item']) ?></p>

            <!-- ラッキーカラー -->
            <p>
                ラッキーカラー:
                <span style="color: <?= h($row['color_code']); ?>">
                    <?= h($row['color_name']); ?>
                </span>
            </p>

            <!-- ボタン -->
            <p><a href="omikuji_kakuritu.php">もう一度引く</a></p>
        </div>
    <?php else: ?>
        <p>データが見つかりません。</p>
        <p><a href="omikuji_kakuritu.php">戻る</a></p>
    <?php endif; ?>
</body>

</html>

ステップアップ!1日1回までに変更

今のままだと、ページを読み込むたびに毎回おみくじを引き直しています。
「1日1回だけ結果を固定する」 機能を追加してみましょう。

ヒント

Cookieを使えば、前回の結果を保存できます。保存した結果と「今日の日付」を比べて、同じ日なら同じ結果を表示しましょう

  1. リセット機能をつけるとしたら
    • URLに ?reset=1 が付いていたらCookieを削除して再読み込み
    • もう一度新しい結果を引けるようにする
    • time() - 3600 は「現在時刻から1時間前」を指定(実際は「過去ならいつでもOK」)
  2. Cookieのチェック
    • Cookieに「結果のID」と「日付」が入っていたら取り出す
    • 日付が date('Y-m-d') と同じなら、そのIDを使う
  3. 新しい結果を作る
    • Cookieがなかった場合、いつも通り random_int() で結果を作る
    • 作った結果をCookieに保存する(結果IDと今日の日付)
    • time() + 86400は現在の時刻 + 86400秒 → 「今から1日後」
//リセット機能
if (isset($_GET['reset'])) {
    // Cookieを削除
    setcookie('omikuji_id', '', time() - 3600);
    setcookie('omikuji_date', '', time() - 3600);
    header('Location: omikuji_result.php');
    exit;
}

//Cookieがあれば今日かどうかチェック
if (isset($_COOKIE['omikuji_id'], $_COOKIE['omikuji_date']) &&
    $_COOKIE['omikuji_date'] === date('Y-m-d')) {
    // 今日なら保存されたIDを使う
    $id = (int)$_COOKIE['omikuji_id'];
}

// Cookieがなければ新しい結果を作る
if (!$id) {
    $id = random_int(1, 7);
    // Cookieに保存
    setcookie('omikuji_id', $id, time() + 86400);
    setcookie('omikuji_date', date('Y-m-d'), time() + 86400);
}


なぜ$_SESSIONではないの?

今回は「おみくじの結果をユーザーの端末に1日保持したい」ため、セッションではなくクッキーを使っています。

  • セッションの有効期限は、基本的にブラウザを閉じると破棄されます(設定次第で延長可能)。
  • クッキーなら有効期限を 24 時間に設定できるので、翌日まで同じ結果を表示できます。

クッキーの許可が必要?

よく「クッキーはブラウザで許可が必要」と言われますが、今回のようなファーストパーティークッキーであれば特別な許可ダイアログは不要です。

1stパーティークッキーと3rdパーティークッキーの違い

種類どんなもの?ブラウザの扱い
1stパーティークッキー今見ているサイト自身が発行するクッキーほとんどのブラウザで標準で許可されている
3rdパーティークッキー広告タグや別ドメインのスクリプトが発行するクッキーChromeやSafariがデフォルトでブロックする方向

同意バナーが必要になるケース
クッキー同意バナーが必要なのは、主に以下のようなケースです。

  • 個人情報や行動履歴を使ってトラッキングする場合
  • 広告目的や解析目的でデータを外部に送信する場合

今回のおみくじは、ただ「今日引いた結果」を保存するだけなので、同意バナーは不要です。

解答例
<?php
require 'common.php';

// === リセット処理 ===
if (isset($_GET['reset'])) {
    // Cookieを削除
    setcookie('omikuji_id', '', time() - 3600);
    setcookie('omikuji_date', '', time() - 3600);
    header('Location: omikuji_result.php');
    exit;
}

$id = null;

// === Cookieがあれば今日かどうかチェック ===
if (
    isset($_COOKIE['omikuji_id'], $_COOKIE['omikuji_date']) &&
    $_COOKIE['omikuji_date'] === date('Y-m-d')
) {

    // 今日なら保存されたIDを使う
    $id = (int)$_COOKIE['omikuji_id'];
}

// === 新しい結果を引く必要がある場合 ===
if (!$id) {
    $id = random_int(1, 7);

    // Cookieに結果と日付を保存(24時間有効)
    setcookie('omikuji_id', $id, time() + 86400);
    setcookie('omikuji_date', date('Y-m-d'), time() + 86400);
}

// データベースから該当の行を取得
$sql = $pdo->prepare('SELECT * FROM omikuji WHERE id=?');
$sql->execute([$id]);
$row = $sql->fetch(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>今日のおみくじ</title>
    <link rel="stylesheet" href="style.css">
</head>

<body style="background-color: <?= h($row['color_code']); ?>; text-align:center;">
    <div class="card">
        <img src="img/<?= h($row['image']) ?>" alt="<?= h($row['fortune']) ?>">
        <h2><?= h($row['fortune']) ?></h2>
        <p>ラッキーアイテム:<?= h($row['lucky_item']) ?></p>
        <p>ラッキーカラー:<span style="color:<?= h($row['color_code']); ?>"><?= h($row['color_name']); ?></span></p>
        <p><a href="omikuji_result.php">もう一度見る</a></p>
        <p><a href="?reset=1" onclick="return confirm('今日の結果をリセットしますか?');">今日の結果をリセット</a></p>
    </div>
</body>

</html>

そもそも $_COOKIE とは?

  • PHPには スーパーグローバル変数 と呼ばれる特別な変数があります
    (例:$_GET, $_POST, $_SESSION, $_COOKIE
  • $_COOKIE は、ブラウザに保存されているCookieの値を読むための配列です
  • 書き込みは setcookie() という専用の関数を使います

Cookieの流れ(簡単図解)

  1. サーバーから送る
    • setcookie('キー', '値', 有効期限);
    • ブラウザに「キー=値」を保存してもらう
  2. 次のリクエストで返ってくる
    • ブラウザが次のアクセス時にそのCookieをサーバーへ送信
    • PHPで $_COOKIE['キー'] で読み取れるようになる

セッションと違うところ

仕組み保存場所使い方有効期限
セッションサーバー側session_start() が必要ブラウザを閉じるまで or 明示的に破棄
Cookieブラウザ側(ユーザーPC)setcookie() で送るだけ指定した秒数だけ有効(例:1日)
  • セッションはサーバー側の一時保存
  • Cookieはブラウザ側に直接保存session_start() は不要
  • なので「cookie_start()」みたいな関数はありません
    $_COOKIE はいつでも直接使えます
<?php
// Cookieを書き込む
setcookie('test', 'Hello Cookie', time() + 3600); // 1時間有効

// 読み出し
if (isset($_COOKIE['test'])) {
    echo "Cookieの中身:" . $_COOKIE['test'];
} else {
    echo "Cookieはまだありません。";
}

1回目のアクセスでは表示されず、ページを再読み込みすると出る → これがCookieの仕組みを実感するポイントです。

ただし、Cookieはユーザー側に保存されるので書き換え可能(セキュリティ注意)重要なデータ(ログイン状態や会員IDなど)はCookieに直接入れず、セッションやDBで管理するのが基本です。

style.css
body {
font-family: sans-serif;
text-align: center;
padding: 3rem;
}
.button {
display: inline-block;
padding: 1rem 2rem;
background: #ff6b6b;
color: white;
text-decoration: none;
border-radius: 8px;
font-size: 1.5rem;
}

.button:hover {
background: #ff4c4c;
}

.card {
background: #fff;
display: inline-block;
padding: 2rem;
border-radius: 10px;
max-width: 400px;
}

img {
max-width: 200px;
display: block;
margin: 0 auto 1rem;
}