【PHP入門】おみくじの抽選ロジックをサーバー側に移してみる

章: 1章

技術タグ: DevTools fetch GET JavaScript JSON Laragon PHP

今回やること

前回は、DevToolsを使って、ブラウザ側の中身が見えることを体験しました。

JavaScriptの処理やHTMLの表示は、ユーザーの手元で確認でき、一部は書き換えられます。

そこで今回は、おみくじの抽選ロジックをJavaScriptからPHPへ移します。

JavaScriptだけで結果を決めるのではなく、サーバー側であるPHPに抽選を担当させます。

前回まで:
JavaScriptがブラウザ側で抽選する

今回:
JavaScriptがPHPへリクエストする
PHPがサーバー側で抽選する
PHPが結果をJSONで返す
JavaScriptが結果を画面に表示する

ここから、ブラウザだけで動くアプリから、サーバーと通信するWebアプリへ進みます。


今回のゴール

今回のゴールは、以下です。

  • Laragon上でPHPが動く環境を用意する
  • HTML・CSS・JavaScript・PHPの4ファイル構成にする
  • PHP側でおみくじ結果をランダムに決める
  • PHPからJSON形式で結果を返す
  • JavaScriptのfetchでPHPへ通信する
  • 返ってきた結果を画面に表示する

今回のポイントは、JavaScriptから抽選ロジックを消すことです。

JavaScriptは結果を決める係ではなく、PHPへ結果を取りに行く係になります。


今回必要なもの

今回はPHPを使うので、Live Serverだけでは足りません。

Live ServerはHTML・CSS・JavaScriptの確認には便利ですが、PHPを実行するためのサーバーではありません。

今回はLaragonを使う前提で進めます。

  • VS Code
  • Google Chrome
  • Laragon
  • Chrome DevTools

Laragonを使うと、ローカルPC上でPHPやMySQLを動かせます。

今回はMySQLはまだ使いません。まずはPHPだけを動かします。


Laragonでプロジェクトを作る

Laragonの www フォルダの中に、今回のプロジェクトフォルダを作ります。

フォルダ名の例は以下です。

omikuji-php

Laragonの設定によって多少違いますが、たとえば以下のような場所になります。

C:\laragon\www\omikuji-app

この中に、今回使うファイルを作ります。

omikuji-app/
├─ index.html
├─ style.css
├─ script.js
└─ omikuji.php

Laragonを起動している状態なら、ブラウザで以下のようなURLからアクセスできます。

http://omikuji-app.test

環境によっては http://localhost/omikuji-app/ のようなURLになる場合もあります。omikuji-appフォルダを作ったら念のためLaragonを再起動しましょう。


ファイル構成

今回のファイル構成は以下です。

omikuji-php/
├─ index.html
├─ style.css
├─ script.js
└─ omikuji.php
ファイル役割
index.html画面の骨組みを作る
style.css見た目を整える
script.jsPHPへ通信して結果を表示する
omikuji.phpサーバー側でおみくじ結果を決める

第1話では、JavaScriptの中におみくじ結果の配列がありました。

今回は、その配列をPHP側へ移します。


index.htmlを用意する

まずはHTMLです。

第1話とほぼ同じですが、タイトルをPHP版にしておきます。

index.html に以下を書きます。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>PHP版おみくじアプリ</title>
  <link rel="stylesheet" href="style.css" />
  <script src="script.js" defer></script>
</head>
<body>
  <main class="omikuji">
    <h1 class="omikuji__title">PHP版おみくじアプリ</h1>

    <p id="result" class="omikuji__result">結果はここに表示されます</p>

    <button id="draw-button" class="omikuji__button" type="button">
      おみくじを引く
    </button>
  </main>
</body>
</html>

読み込み系は head にまとめ、JavaScriptには defer を付けています。

この方針なら、body には画面に表示する中身だけを書けます。


style.cssを用意する

CSSは第1話と同じで大丈夫です。

今回の主役はPHPと通信なので、見た目は作り込みません。

style.css に以下を書きます。

body {
  margin: 0;
  font-family: sans-serif;
  background-color: #f5f5f5;
  color: #222222;
}

.omikuji {
  max-width: 480px;
  margin-inline: auto;
  padding-block: 64px;
  padding-inline: 24px;
  text-align: center;
}

.omikuji__title {
  margin-block-start: 0;
  margin-block-end: 24px;
  font-size: 32px;
}

.omikuji__result {
  margin-block-start: 0;
  margin-block-end: 24px;
  padding-block: 24px;
  padding-inline: 16px;
  border: 2px solid #222222;
  border-radius: 12px;
  background-color: #ffffff;
  font-size: 24px;
  font-weight: bold;
}

.omikuji__button {
  padding-block: 12px;
  padding-inline: 24px;
  border-width: 0;
  border-radius: 999px;
  background-color: #222222;
  color: #ffffff;
  font-size: 16px;
  font-weight: bold;
  cursor: pointer;
}

.omikuji__button:hover {
  opacity: 0.8;
}

PHPでおみくじ結果を決める

ここからPHPです。

omikuji.php を作成し、以下を書きます。

<?php

/**
 * omikuji.php
 *
 * 役割:
 * - サーバー側でおみくじ結果をランダムに決める
 * - 結果をJSON形式でブラウザへ返す
 */

$fortunes = ['大吉', '中吉', '小吉', '吉', '凶'];

$random_index = array_rand($fortunes);
$selected_fortune = $fortunes[$random_index];

$response = [
    'fortune' => $selected_fortune,
];

header('Content-Type: application/json; charset=UTF-8');

echo json_encode($response, JSON_UNESCAPED_UNICODE);

このPHPファイルの役割は、サーバー側でおみくじ結果を決めて、JSON形式で返すことです。

JavaScriptではなく、PHP側に以下の配列を持たせています。

大吉
中吉
小吉
吉
凶

つまり、おみくじ結果の候補はブラウザ側のJavaScriptではなく、サーバー側のPHPに移りました。


PHPコードの中身を分解する

まず、結果候補を配列で用意しています。

$fortunes = ['大吉', '中吉', '小吉', '吉', '凶'];

次に、array_rand() で配列のキーをランダムに1つ選びます。

$random_index = array_rand($fortunes);

選ばれたキーを使って、実際のおみくじ結果を取り出します。

$selected_fortune = $fortunes[$random_index];

その結果を、ブラウザへ返すための配列に入れます。

$response = [
    'fortune' => $selected_fortune,
];

最後に、JSONとして返すためのヘッダーを指定し、json_encode() で出力します。

header('Content-Type: application/json; charset=UTF-8');

echo json_encode($response, JSON_UNESCAPED_UNICODE);

JSON_UNESCAPED_UNICODE を付けると、日本語が読みやすい形で返りやすくなります。


omikuji.phpだけをブラウザで開いてみる

まずはJavaScriptとつなぐ前に、PHPだけを確認します。

ブラウザで以下のようなURLを開きます。

http://omikuji-php.test/omikuji.php

うまくいけば、次のようなJSONが表示されます。

{"fortune":"大吉"}

更新するたびに、大吉中吉小吉 などが変われば成功です。

この時点で、PHP側で抽選できていることが確認できます。


JavaScriptからPHPへ通信する

次に、JavaScriptから omikuji.php へ通信します。

script.js を以下の内容にします。

const resultText = document.getElementById('result');
const drawButton = document.getElementById('draw-button');

/**
 * PHP側におみくじ結果を取りに行き、画面へ表示する
 *
 * @returns {Promise<void>}
 */
async function drawFortune() {
  resultText.textContent = '抽選中...';

  try {
    const response = await fetch('omikuji.php');

    if (!response.ok) {
      resultText.textContent = '通信に失敗しました';
      return;
    }

    const data = await response.json();

    resultText.textContent = data.fortune;
  } catch (error) {
    resultText.textContent = 'エラーが発生しました';
    console.error(error);
  }
}

drawButton.addEventListener('click', drawFortune);

今回から、JavaScriptの中にはおみくじ結果の配列がありません。

JavaScriptは、PHPへリクエストして、返ってきた結果を画面に表示するだけです。


fetchとは何か

fetch() は、JavaScriptからサーバーへ通信するための機能です。

今回のコードでは、次の部分でPHPへリクエストしています。

const response = await fetch('omikuji.php');

これは、JavaScriptから omikuji.php に対して「結果をください」とお願いしているようなものです。

PHPはサーバー側で抽選をして、JSONを返します。

JavaScriptは、そのJSONを受け取って画面に表示します。

JavaScript
↓ fetch
omikuji.php
↓ JSON
JavaScript
↓
画面に表示

response.json()でJSONを読む

PHPから返ってくるデータはJSON形式です。

JavaScript側では、次のコードでJSONを読み取ります。

const data = await response.json();

今回PHPから返ってくるJSONは、たとえば次のような形です。

{"fortune":"大吉"}

そのため、JavaScriptでは data.fortune で結果を取り出せます。

resultText.textContent = data.fortune;

これで、PHPが決めた結果を画面へ表示できます。


エラー処理も少しだけ入れておく

通信は、必ず成功するとは限りません。

ファイル名を間違えたり、PHP側でエラーが出たりすると、通信に失敗します。

そのため、今回は最低限のエラー処理も入れています。

if (!response.ok) {
  resultText.textContent = '通信に失敗しました';
  return;
}

さらに、予期しないエラーに備えて try...catch も使っています。

try {
  // 通信処理
} catch (error) {
  resultText.textContent = 'エラーが発生しました';
  console.error(error);
}

最初は少し難しく見えるかもしれませんが、通信するコードではエラー時の逃げ道を作っておくのが大事です。


今回の流れを整理する

今回の処理の流れはこうです。

1. ユーザーがボタンを押す
2. JavaScriptのdrawFortune()が動く
3. fetchでomikuji.phpへ通信する
4. PHPがおみくじ結果をランダムに決める
5. PHPがJSONで結果を返す
6. JavaScriptがJSONを読む
7. 画面に結果を表示する

第1話では、JavaScriptが抽選も表示も担当していました。

今回からは、役割が分かれました。

担当役割
JavaScriptボタンクリックを受け取り、PHPへ通信し、結果を画面に表示する
PHPサーバー側でおみくじ結果を決め、JSONで返す

この役割分担が、Webシステム開発の入口です。


DevToolsのNetworkで通信を見てみる

ボタンを押したら、DevToolsのNetworkタブも見てみましょう。

Networkタブを開いた状態でおみくじボタンを押すと、omikuji.php への通信が見えるはずです。

ここでは、次のことを確認できます。

  • JavaScriptがomikuji.phpへリクエストしている
  • PHPからJSONが返ってきている
  • 返ってきたJSONの中にfortuneが入っている

今回の時点では、Networkを完璧に理解しなくて大丈夫です。

まずは「ボタンを押すと、裏側でPHPに通信している」という事実を目で確認できれば十分です。


今回わかったこと

今回は、おみくじの抽選ロジックをJavaScriptからPHPへ移しました。

学んだことは以下です。

  • Live ServerだけではPHPは動かない
  • PHPを動かすにはLaragonなどのローカル環境が必要
  • PHP側で配列からランダムに結果を選べる
  • PHPはJSON形式で結果を返せる
  • JavaScriptはfetchでPHPへ通信できる
  • JavaScriptは返ってきたJSONを読んで画面に表示できる
  • 重要なロジックはブラウザ側ではなくサーバー側に置く考え方がある

これで、ブラウザだけで動くアプリから、サーバーと通信するWebアプリへ一歩進みました。


ただし、これで完璧に安全というわけではない

今回、抽選ロジックをPHPへ移したことで、JavaScript側におみくじ結果の配列を置かなくなりました。

これは前回より良い構成です。

しかし、これだけで完璧に安全というわけではありません。

本気のキャンペーンやログインが絡む処理では、さらに次のような対策も必要になります。

  • 同じ人が何度も引けないようにする
  • 結果をデータベースに保存する
  • 不正な連続アクセスを防ぐ
  • ユーザーごとの状態を管理する
  • サーバー側で最終判定する

今回の目的は、まず「重要な処理をサーバー側へ移す」という考え方を体験することです。

一歩ずつ進めば大丈夫です。


次回予告

次回は、DevToolsのNetworkタブを使って、今回の通信をもう少し詳しく観察します。

ボタンを押した時に、ブラウザからPHPへどんなリクエストが飛び、PHPからどんなレスポンスが返ってくるのかを見ます。

次回のテーマは、以下です。

【DevTools Network入門】fetch通信でPHPから返るJSONを見てみる

Webアプリは、画面だけでなく通信も見ると一気に理解が進みます。

次回は、ブラウザとサーバーの会話を覗いていきます。