Newer
Older
hello-programmer-world / src / pages / php / 040-work.mdx
@h.sakamoto h.sakamoto 16 hours ago 9 KB quiz
---
layout: "@/layouts/MarkdownLayout.astro"
---

import Toc from "../../components/Toc.astro";
import Details from "@/components/Details.astro";

export const title = "クイズを作ってみよう";

import DockerLink from "@/components/DockerLink.astro";

# {title}

これまで学んだPHPの知識を使って、クイズアプリを作ってみましょう。

## TOC

## 演習の目的

この演習では、以下のPHPの機能を実践的に学びます。

- フォームデータの受け取り(`$_POST`)
- 条件分岐(`if`文)
- 変数の使い方
- `htmlspecialchars()`によるセキュリティ対策

## 課題

`/workspace/php/`ディレクトリにテンプレートファイルが用意されています。  
これを編集して、オリジナルのクイズを作成してください。

### 必要なファイル

- `quiz.php` - クイズ問題を表示するPHPファイル(**TODOを実装してください**)
- `submit.php` - 答えを判定するPHPファイル(**TODOを実装してください**)

### 実装する内容

#### quiz.phpの実装(4つのTODO)

##### TODO 1: 問題と選択肢を定義する

変数を使って問題文と選択肢を定義します。

<Details summary="ヒント1: どうやって定義する?">

文字列の変数と、配列を使います。

```php
$question = "ここに問題文を書く";
$options = ["選択肢1", "選択肢2", "選択肢3"];
```

</Details>

<Details summary="ヒント2: サンプルコード">

```php
// 問題文
$question = "日本の首都はどこ?";

// 選択肢(配列)
$options = ["東京", "大阪", "京都"];
```

自分の好きな問題に変更してください。

</Details>

##### TODO 2: 正解を定義する

正解の選択肢を変数に保存します。

<Details summary="ヒント1: どの値を設定する?">

正解の選択肢の文字列をそのまま設定します。  
例えば、「東京」が正解なら `$correctAnswer = "東京";` です。

</Details>

<Details summary="ヒント2: サンプルコード">

```php
$correctAnswer = "東京";
```

TODO 1で定義した選択肢の中から、正解のものを設定してください。

</Details>

##### TODO 3: 問題を表示する

定義した問題文をHTMLに埋め込みます。

<Details summary="ヒント1: どうやって表示する?">

`<?php echo ... ?>`を使って、変数の内容を出力します。

</Details>

<Details summary="ヒント2: サンプルコード">

```php
<p>問題: <?php echo htmlspecialchars($question); ?></p>
```

`htmlspecialchars()`は、特殊文字を安全に表示するための関数です。

</Details>

##### TODO 4: 選択肢を表示する

配列に入れた選択肢を、繰り返し処理で表示します。

<Details summary="ヒント1: どうやって繰り返す?">

`foreach`を使って、配列の要素を1つずつ取り出して表示します。

</Details>

<Details summary="ヒント2: サンプルコード">

```php
<?php foreach ($options as $index => $option): ?>
  <div>
    <label>
      <input type="radio" name="answer" value="<?php echo htmlspecialchars($option); ?>" <?php echo $index === 0 ? 'required' : ''; ?>>
      <?php echo htmlspecialchars($option); ?>
    </label>
  </div>
<?php endforeach; ?>
```

- `$index`は0から始まる番号、`$option`は選択肢の文字列です
- `value`属性に選択肢の文字列を設定することで、submit.phpで判定できます
- 最初の選択肢だけ`required`を付けています

</Details>

#### submit.phpの実装(4つのTODO)

##### TODO 1: フォームから送信されたかチェックする

直接このページにアクセスされた場合にエラーを表示します。

<Details summary="ヒント1: どうやってチェックする?">

`$_SERVER["REQUEST_METHOD"]`という特別な変数を使います。  
フォームが`method="post"`で送信された場合、この値は`"POST"`になります。

</Details>

<Details summary="ヒント2: サンプルコード">

```php
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
  echo '<h1>エラー</h1>';
  echo '<p>不正なアクセスです。</p>';
  exit;  // ここで処理を終了
}
```

`!==`は「等しくない」という意味です。POSTメソッド以外でアクセスされた場合はエラーを表示して終了します。

</Details>

##### TODO 2: 送信された答えを取得する

フォームから送信されたユーザーの答えを変数に保存します。

<Details summary="ヒント1: どうやって取得する?">

`$_POST`という特別な配列を使います。  
`quiz.php`のinputタグの`name="answer"`に対応して、`$_POST["answer"]`で値を取得できます。

</Details>

<Details summary="ヒント2: サンプルコード">

```php
$userAnswer = $_POST["answer"] ?? "";
```

`??`は「左側の値が存在しない場合は右側の値を使う」という意味です。  
これにより、値が送信されなかった場合でもエラーにならず、空文字列が入ります。

</Details>

##### TODO 3: 正解を設定する

正解の値を変数に保存します。

<Details summary="ヒント1: どの値を設定する?">

`quiz.php`で定義した正解と同じ文字列を設定します。  
例えば、`quiz.php`で`$correctAnswer = "東京";`と定義したなら、ここでも`"東京"`です。

</Details>

<Details summary="ヒント2: サンプルコード">

```php
$correctAnswer = "東京";
```

`quiz.php`のTODO 2で設定した値と同じにしてください。

</Details>

##### TODO 4: 正誤判定をする

ユーザーの答えと正解を比較して、結果を表示します。

<Details summary="ヒント1: どうやって比較する?">

`if`文を使って、`$userAnswer`と`$correctAnswer`が等しいかをチェックします。  
等しい場合は正解、そうでない場合は不正解のメッセージを表示します。

</Details>

<Details summary="ヒント2: サンプルコード">

```php
if ($userAnswer === $correctAnswer) {
  echo '<h1>正解です!</h1>';
  echo '<p>よくできました。</p>';
} else {
  echo '<h1>不正解です</h1>';
  echo '<p>残念!もう一度考えてみましょう。</p>';
}
```

`===`は「完全に等しい」という意味です。メッセージの内容は自由に変更できます。

</Details>

## 確認方法

作成したクイズは以下の方法で確認できます。

<DockerLink href="workspace/php/quiz.php" />

1. リンクをクリックしてクイズページを開く
2. 問題を読んで答えを選択する
3. 「答えを送信」ボタンをクリック
4. 正誤判定の結果が表示されることを確認する

## 発展課題

基本的なクイズができたら、複数の問題からランダムに1問を出題するように機能を作り変えてみましょう。

ただし、ここで考えなければ行けないポイントがあります。  
**ランダムに選んだ問題の正解を、どうやって判定すればよいでしょうか?**

<Details summary="ヒント1: 何が問題?">

quiz.phpで問題をランダムに選ぶと、submit.phpでは「どの問題が出題されたか」が分かりません。  
正解の値が分からないと判定できませんね。

</Details>

<Details summary="ヒント2: hidden fieldを使う方法は?">

正解をhidden fieldでsubmit.phpに送る方法も考えられます:

```html
<input type="hidden" name="correct_answer" value="東京">
```

しかし、この方法には問題があります。  
ブラウザの開発者ツールでHTMLを見ると、正解が丸見えになってしまいます!

</Details>

<Details summary="ヒント3: 解決方法">

前のページで学んだ**Session**を使えば解決できます!

- quiz.phpで正解をSessionに保存する
- submit.phpでSessionから正解を取り出す

これなら、ブラウザ側には正解が送られないので安全です。

</Details>

<Details summary="実装のヒント">

**quiz.phpの変更:**

```php
<?php
session_start();

// 複数の問題を定義
$questions = [
  [
    "question" => "日本の首都はどこ?",
    "options" => ["東京", "大阪", "京都"],
    "answer" => "東京"
  ],
  [
    "question" => "1 + 1 は?",
    "options" => ["1", "2", "3"],
    "answer" => "2"
  ],
  // 問題を追加
];

// ランダムに1問選択
$randomIndex = array_rand($questions);
$selectedQuestion = $questions[$randomIndex];

// Sessionに正解を保存
$_SESSION["correct_answer"] = $selectedQuestion["answer"];

// 問題と選択肢を変数に設定
$question = $selectedQuestion["question"];
$options = $selectedQuestion["options"];
?>
```

**submit.phpの変更(TODO 3):**

```php
// Sessionから正解を取得
$correctAnswer = $_SESSION["correct_answer"] ?? "";
```

これで完成です!

</Details>

## まとめ

この演習では、PHPの基本的な機能を使ってインタラクティブなウェブアプリケーションを作成しました。

- 変数と配列の使い方
- HTMLへのデータ埋め込み
- フォームからのデータ受け取り
- 条件分岐による処理の切り替え
- エラーハンドリング
- (発展)状態管理

これらはウェブアプリケーション開発の基礎となる重要な概念です。  
ぜひ自分なりにアレンジして、さまざまなクイズを作ってみてください。