HTML・PHP・MySQLだけで作る間違い探しゲーム【③問題表示ページを作る】

# 3. 問題表示ページの実装
<details>
<summary><< 前の記事 [【②スタートページを作る】](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7)</summary>
&emsp;[2. スタートページの実装](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-スタートページの実装)
&emsp;&emsp;[2-1. セッション](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-1-セッション)
&emsp;&emsp;&emsp;[2-1-1. セッションの開始](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-1-1-セッションの開始)
&emsp;&emsp;&emsp;[2-1-2. セッションの放棄](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-1-2-セッションの放棄)
&emsp;&emsp;[2-2. バリデーション](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-バリデーション)
&emsp;&emsp;&emsp;[2-2-1. 無効な送信の拒否](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-1-無効な送信の拒否)
&emsp;&emsp;&emsp;[2-2-2. 名前のバリデーション](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-2-名前のバリデーション)
&emsp;&emsp;&emsp;&emsp;[2-2-2-1. 半角スペースのみ無効](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-2-1-半角スペースのみ無効)
&emsp;&emsp;&emsp;&emsp;[2-2-2-2. 文字数](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-2-2-文字数)
&emsp;&emsp;&emsp;&emsp;[2-2-2-3. 空白文字／制御文字無効](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-2-3-空白文字制御文字無効)
&emsp;&emsp;&emsp;[2-2-3. エラーメッセージの表示](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-3-エラーメッセージの表示)
&emsp;&emsp;&emsp;[2-2-4. 入力値の保持 - <input>タグ](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-4-入力値の保持---inputタグ)
&emsp;&emsp;[2-3. 問題表示ページへの遷移](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-3-問題表示ページへの遷移)
</details>
<details>
<summary>次の記事 >> [【④DB接続設定ファイルを作る】](https://qiita.com/_Taturon_/items/f8e464821892555e80aa)</summary>
&emsp;[4. DB接続設定](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-db接続設定)
&emsp;&emsp;[4-1. PDOオブジェクトの属性](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-1-pdoオブジェクトの属性)
&emsp;&emsp;&emsp;[4-1-1. フェッチ形式の指定](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-1-1-フェッチ形式の指定)
&emsp;&emsp;&emsp;[4-1-2. エラーモードの設定](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-1-2-エラーモードの設定)
&emsp;&emsp;&emsp;&emsp;[4-1-2-1. エラーモードの違いによるエラー文の違い](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-1-2-1-エラーモードの違いによるエラー文の違い)
&emsp;&emsp;&emsp;[4-1-3. エミュレーションの設定](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-1-3-エミュレーションの設定)
&emsp;&emsp;&emsp;&emsp;[4-1-3-1. プリペアドステートメント](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-1-3-1-プリペアドステートメント)
&emsp;&emsp;&emsp;&emsp;[4-1-3-2. エミュレーション](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-1-3-2-エミュレーション)
&emsp;&emsp;[4-2. 例外発生時の処理](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-2-例外発生時の処理)
&emsp;&emsp;&emsp;[4-2-1. HTTPヘッダの送信](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-2-1-httpヘッダの送信)
&emsp;&emsp;&emsp;[4-2-2. 処理の中断とエラーメッセージの表示](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-2-2-処理の中断とエラーメッセージの表示)
&emsp;&emsp;[4-3. DBの切断について](#4-3-dbの切断について)
</details>

ここではゲームのコアとなる問題を生成するページを実装します。

難易度「易しい」の問題例
![問題ページ_易しい.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598695/0c250e9d-493a-8094-b630-c772389e7ec4.png)
難易度「難しい」の問題例
![問題ページ_難しい.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598695/7029303b-bcd7-83c7-10fd-5db64de28ec6.png)

```php:find_the_mistake.php
<?php

// セッションの再開
session_start();

// 無効なアクセスの拒否
if (empty($_SESSION)) {
	header('Location:start.php');
	exit();
}

// リセット回数の計測開始
if (isset($_SESSION['count'])) {
	$_SESSION['count']++;
} else {
	$_SESSION['count'] = 0;
}

// ターゲット配列の設定
if ($_SESSION['difficulty'] === '難しい（漢字）') {
	$chars = [
		['猫', '描'],
		['犬', '大'],
		['幸', '辛'],
		['白', '臼'],
		['矢', '失'],
		['力', '刀'],
		['防', '妨'],
		['土', '士'],
		['卵', '卯'],
		['巨', '臣'],
		['寒', '塞'],
		['旅', '族'],
		['車', '東'],
		['釘', '針']
	];
} else {
	$chars = [
		['&#x1f415;', '&#x1f408;'],
		['&#x1f405;', '&#x1f406;'],
		['&#x1f98e;', '&#x1f40d;'],
		['&#x1f433;', '&#x1f42c;'],
		['&#x1f339;', '&#x1f337;'],
		['&#x1f34a;', '&#x1f34b;'],
		['&#x1f34e;', '&#x1f351;'],
		['&#x1f955;', '&#x1f336;'],
		['&#x1f96f;', '&#x1f95e;'],
		['&#x1f358;', '&#x1f359;'],
		['&#x1f341;', '&#x1f342;'],
		['&#x1f332;', '&#x1f333;'],
		['&#x1f47b;', '&#x1f47d;'],
		['&#x1f396;', '&#x1f3c5;']
	];
}

// ターゲット配列のシャッフル
shuffle($chars);
for ($i = 0; $i < count($chars); $i++) {
	shuffle($chars[$i]);
}

// 正解と選択対象配列を設定
$correct = $chars[0][0];
$_SESSION['correct'] = $correct;
for ($j = 0; $j <= 19; $j++) {
	for ($k = 0; $k <= 19; $k++) {
		$targets[$j][$k] = $chars[0][1];
	}
}

// 正解のターゲットを選択対象配列に1つだけ挿入
$key = range(0, 19);
$key1 = array_rand($key);
$key2 = array_rand($key);
$targets[$key1][$key2] = $correct;

// 開始時刻を記録
$_SESSION['start_time'] = microtime(true);

?>
<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta charset="UTF-8">
		<title>間違い探し</title>
	</head>
	<body>
		<h1><?= $correct; ?>を見つけよう！</h1>
		<p>
			<small>難易度: <?= $_SESSION['difficulty']; ?></small>
			<button type="button" onclick="location.href='find_the_mistake.php'">分かるか！（リセット）</button>
			<button type="button" onclick="location.href='start.php'">スタートページへ</button>
		</p>
		<form action="result.php" method="POST">
			<table>
				<?php foreach ($targets as $target): ?>
				<tr>
					<?php for ($l = 0; $l < count($target); $l++): ?>
					<td><input type="submit" name="answer" value="<?= $target[$l]; ?>"></td>
					<?php endfor; ?>
				</tr>
				<?php endforeach; ?>
			</table>
		</form>
	</body>
</html>
```

## 3-1. 無効なアクセスの拒否
```php:find_the_mistake.php
// 無効なアクセスの拒否
if (empty($_SESSION)) {
	header('Location:start.php');
	exit();
}
```
不正な画面遷移とエラーを防ぐために、
「**start.php**」で設定した**セッション変数がない場合**は、
スタートページにリダイレクトされるようにしています。

## 3-2. リセット回数の計測
このゲームでは、問題が難しい場合に**リセットできる機能**を実装しており、
リセットボタンが押されると問題生成ページが再描写されます。

この時、セッション変数に`count`をキーとしたリセット回数を記録させています。

セッション変数とすることで、結果ページでもリセット回数を取り扱えるようになります。

```php:find_the_mistake.php
// リセット回数の計測開始
if (isset($_SESSION['count'])) {
	$_SESSION['count']++;
} else {
	$_SESSION['count'] = 0;
}
```
- 既に設定されていた場合は**インクリメント**
- それ以外では**`0`を設定**

とすることでリセット回数が正しくカウントされるようにしています。

## 3-3. 問題用文字配列の設定
```php:find_the_mistake.php
// ターゲット配列の設定
if ($_SESSION['difficulty'] === '難しい（漢字）') {
	$chars = [
		['猫', '描'],
		['犬', '大'],
		['幸', '辛'],
		['白', '臼'],
		['矢', '失'],
		['力', '刀'],
		['防', '妨'],
		['土', '士'],
		['卵', '卯'],
		['巨', '臣'],
		['寒', '塞'],
		['旅', '族'],
		['車', '東'],
		['釘', '針']
	];
} else {
	$chars = [
		['&#x1f415;', '&#x1f408;'],
		['&#x1f405;', '&#x1f406;'],
		['&#x1f98e;', '&#x1f40d;'],
		['&#x1f433;', '&#x1f42c;'],
		['&#x1f339;', '&#x1f337;'],
		['&#x1f34a;', '&#x1f34b;'],
		['&#x1f34e;', '&#x1f351;'],
		['&#x1f955;', '&#x1f336;'],
		['&#x1f96f;', '&#x1f95e;'],
		['&#x1f358;', '&#x1f359;'],
		['&#x1f341;', '&#x1f342;'],
		['&#x1f332;', '&#x1f333;'],
		['&#x1f47b;', '&#x1f47d;'],
		['&#x1f396;', '&#x1f3c5;']
	];
}

// ターゲット配列のシャッフル
shuffle($chars);
for ($i = 0; $i < count($chars); $i++) {
	shuffle($chars[$i]);
}

// 正解と選択対象配列を設定
$correct = $chars[0][0];
$_SESSION['correct'] = $correct;
for ($j = 0; $j <= 19; $j++) {
	for ($k = 0; $k <= 19; $k++) {
		$targets[$j][$k] = $chars[0][1];
	}
}

// 正解のターゲットを選択対象配列に1つだけ挿入
$key = range(0, 19);
$key1 = array_rand($key);
$key2 = array_rand($key);
$targets[$key1][$key2] = $correct;
```
### 3-3-1. 文字ペア配列の選択
選択された難易度によって**漢字か絵文字かを分岐**させます。

```php:find_the_mistake.php
// ターゲット配列の設定
if ($_SESSION['difficulty'] === '難しい（漢字）') {
	$chars = [
		['猫', '描'],
		['犬', '大'],
		['幸', '辛'],
		['白', '臼'],
		['矢', '失'],
		['力', '刀'],
		['防', '妨'],
		['土', '士'],
		['卵', '卯'],
		['巨', '臣'],
		['寒', '塞'],
		['旅', '族'],
		['車', '東'],
		['釘', '針']
	];
} else {
	$chars = [
		['&#x1f415;', '&#x1f408;'],
		['&#x1f405;', '&#x1f406;'],
		['&#x1f98e;', '&#x1f40d;'],
		['&#x1f433;', '&#x1f42c;'],
		['&#x1f339;', '&#x1f337;'],
		['&#x1f34a;', '&#x1f34b;'],
		['&#x1f34e;', '&#x1f351;'],
		['&#x1f955;', '&#x1f336;'],
		['&#x1f96f;', '&#x1f95e;'],
		['&#x1f358;', '&#x1f359;'],
		['&#x1f341;', '&#x1f342;'],
		['&#x1f332;', '&#x1f333;'],
		['&#x1f47b;', '&#x1f47d;'],
		['&#x1f396;', '&#x1f3c5;']
	];
}
```
正解・不正解の**ペア関係を維持**するため[^3]に、
**多次元配列**として変数`$chars`に格納しています。

[^3]: 「間違い探し」なので単に「ランダムに文字を選択させる」だと簡単に見分けがつく文字同士が表示される可能性があり、ゲーム性が損なわれます。この為「似た文字のペア」を設定する必要があります。

絵文字は`&XXX;`という形式で記述されていますが、
これは**HTMLエンティティ**と呼ばれるもので、
これにより見えない文字や標準キーボードでは入力が難しい文字を**容易に取り扱えます**。

また、viエディタ上での**バグを防ぐ**こともできます[^4]。

[^4]: viやvimで直接絵文字を打つとカーソルの位置がおかしくなったり謎の空白が出現するなど、めちゃくちゃバグります。vimにはあまり詳しくないので、回避できる策があるなら教えて欲しいです...

MDN web docs「[Entity (エンティティ) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN](https://developer.mozilla.org/ja/docs/Glossary/Entity)」

<details>
<summary>今回用いた各エンティティとwebページ上で表示される絵文字の対応はこちら</summary>
`&#x1f415;` => 「&#x1f415;」と&emsp;`&#x1f408;` => 「&#x1f408;」
`&#x1f405;` => 「&#x1f405;」と&emsp;`&#x1f406;` => 「&#x1f406;」
`&#x1f98e;` => 「&#x1f98e;」と&emsp;`&#x1f40d;` => 「&#x1f40d;」
`&#x1f433;` => 「&#x1f433;」と&emsp;`&#x1f42c;` => 「&#x1f42c;」
`&#x1f339;` => 「&#x1f339;」と&emsp;`&#x1f337;` => 「&#x1f337;」
`&#x1f34a;` => 「&#x1f34a;」と&emsp;`&#x1f34b;` => 「&#x1f34b;」
`&#x1f34e;` => 「&#x1f34e;」と&emsp;`&#x1f351;` => 「&#x1f351;」
`&#x1f955;` => 「&#x1f955;」と&emsp;`&#x1f336;` => 「&#x1f336;」
`&#x1f96f;` => 「&#x1f96f;」と&emsp;`&#x1f95e;` => 「&#x1f95e;」
`&#x1f358;` => 「&#x1f358;」と&emsp;`&#x1f359;` => 「&#x1f359;」
`&#x1f341;` => 「&#x1f341;」と&emsp;`&#x1f342;` => 「&#x1f342;」
`&#x1f332;` => 「&#x1f332;」と&emsp;`&#x1f333;` => 「&#x1f333;」
`&#x1f47b;` => 「&#x1f47b;」と&emsp;`&#x1f47d;` => 「&#x1f47d;」
`&#x1f396;` => 「&#x1f396;」と&emsp;`&#x1f3c5;` => 「&#x1f3c5;」
</details>

絵文字のHTMLエンティティを調べるには下記サイトが参考になります。

Let's EMOJI「[Unicode 13.0 絵文字 (Unicode 13.0 Emoji) | Let&#039;s EMOJI](https://lets-emoji.com/unicode13-emoji/)」

### 3-3-2. 文字ペア配列及び文字ペアのシャッフル
難易度がどちらの場合でも、正解・不正解のペアは**14組ある**ので、
正解・不正解関係は保ったままでこれらを**シャッフル**し、問題に**ランダム性**を付与します。
これによりリセットする度に**異なる問題**が表示されるようになります。

```php:find_the_mistake.php
// ターゲット配列のシャッフル
shuffle($chars);
for ($i = 0; $i < count($chars); $i++) {
	shuffle($chars[$i]);
}
```
配列のシャッフルには`shuffle()`が使えます。

`shuffle()`は「シャッフルされた配列」ではなく、
「**シャッフルが成功したかどうかの真偽値**」を返しますので、
変数に格納する必要はありません。

PHPマニュアル「[PHP: shuffle - Manual](https://www.php.net/manual/ja/function.shuffle)」

まず、文字ペアの多次元配列を格納した`$chars`をシャッフルし、
「**14組の文字ペアの並び**（=多次元配列の第1層）」をランダムにします。

続いて、`for`構文を用いて再帰的にシャッフルすることで、
「**文字ペアの中での並び**（=多次元配列の第2層）」もランダムにします[^5]。

[^5]: 多次元配列に対して`shuffle()`を用いた場合、**最上位層のみ**シャッフルされます。再帰的にシャッフルするには別途`for`構文や`foreach`構文などの繰り返し処理を用いる必要があります。

こうすることで「ペアの中でいつもどちらかが正解／不正解」となってしまうのを防ぎ、
ゲーム性を保てます。

正解・不正解のペアが**14組**あり、ペアの中でも**正解・不正解が入れ替わる**ことから、
各難易度毎に`14 × 2 = 28`通りの問題を生成できます（ゲーム全体では56通り）。

最初に変数`$chars`に格納した文字ペアの多次元配列にペアを加えることで、
更にパターンを増やすこともできます。

### 3-3-3. 正解文字と不正解文字配列の設定
ランダムにシャッフルされた文字ペア配列から**1文字だけ選択して**正解の文字とします。
次いで、「**正解の文字とペアになっている文字**」を用いて**不正解文字の配列**を生成します。

```php:find_the_mistake.php
// 正解と選択対象配列を設定
$correct = $chars[0][0];
$_SESSION['correct'] = $correct;
for ($j = 0; $j <= 19; $j++) {
	for ($k = 0; $k <= 19; $k++) {
		$targets[$j][$k] = $chars[0][1];
	}
}
```
#### 3-3-3-1. 正解文字の設定
`$correct`に**正解とする文字**を代入しています。
文字ペアの多次元配列を再帰的にシャッフルしたので、
`$chars[0][0]`には、
「**28（`14 × 2`）文字からランダムに選択された1文字**」
が格納されています。

結果ページを生成するファイルで正解／不正解の判定を行うので、
**セッション変数**にも`correct`をキーとして正解文字を格納しています。

#### 3-3-3-2. 不正解文字配列の設定
次いで、**正解文字と対になる不正解文字**を、
`$targets`に`20 × 20`の多次元配列として格納しています。

対になる文字は`$chars`の正解文字と**同じ階層にある異なるキーの値**のはずなので、
今回のコードでは`$chars[0][1]`が該当します。

この不正解文字を、変数`$j`,`$k`に`0 ~ 19`の範囲で**2重の繰り返し処理**を回す中で、
`$targets`に対し**キーを`[$j][$k]`とした値**とすることで、多次元配列を生成しています[^6]。

これで正解文字と不正解文字配列の設定が完了しました。

[^6]: 文字ペアの多次元配列のシャッフル時に`for`構文で`$i`を使用したので、今回はアルファベット順に`$j`, `$k`としています。`$i`と`$j`, 実際は、`$k`のどちらかは同じでも問題ありません（`for`構文の最初の式で初期化される為。`$j`と`$k`は同時に使用するので分ける必要あり）が、ややこしいので筆者はこのやり方を好んでいます。

### 3-3-4. 問題用文字配列の生成
不正解文字配列の**ランダムな1つの値のみ**を正解文字に置き換えることで、
問題用の文字配列を作り出します。

```php:find_the_mistake.php
// 正解のターゲットを選択対象配列に1つだけ挿入
$key = range(0, 19);
$key1 = array_rand($key);
$key2 = array_rand($key);
$targets[$key1][$key2] = $correct;
```
まず、`range()`を用いて値が`0 ~ 19`の範囲の**整数**を値としてもつ配列を生成し、
`$key`に格納しています。

その後、`array_rand()`によって`$key`の配列からランダムに**キー**を取り出します。
これを2回繰り返してそれぞれ`$key1`と`$key2`に格納することで、
`1 ~ 19`の範囲内のランダムな整数を選択しました。

後はこの`$key1`と`$key2`を不正解文字配列`$targets`の**キーとして指定**することで、
「**多次元配列の中のランダムな1つの値**」を指定しています[^7]。

[^7]: `array_rand()`は**ランダムなキー**を返すことに注意してください。今回の場合、`$key1`と`$key2`に格納されているのは`$key`の**値ではなくキー**です。`$key`の配列のキーと値が全く同じである為ややこしいですが、キーにインデックス番号以外が用いられた配列を扱う際は注意が必要です。

PHPマニュアル「[PHP: range - Manual](https://www.php.net/manual/ja/function.range.php)」
PHPマニュアル「[PHP: array_rand - Manual](https://www.php.net/manual/ja/function.array-rand.php)」

これでようやく問題用の文字配列が完成しました！

## 3-4. 開始時刻の記録
回答時間を計測する為に問題が表示される直前に開始時刻を記録します。

```php:find_the_mistake.php
// 開始時刻を記録
$_SESSION['start_time'] = microtime(true);
```
選択された文字の正解／不正解判定と同様に、[結果ページで判定する](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-2-回答時間の算出)ので、
セッション変数として保存しています。

`microtime()`は**現在のタイムスタンプをマイクロ秒まで返す**関数で、

- 引数を指定しない場合は`msec sec`形式の文字列
- `true`を指定した場合は`float`型のマイクロ秒

を、それぞれ返します。

PHPマニュアル「[PHP: microtime - Manual](https://www.php.net/manual/ja/function.microtime.php)」

結果ページでは引き算で**回答時間を算出する**ので、
計算しやすいタイムスタンプ形式となるように引数には`true`を指定しています。

## 3-5. 選択肢の描写
問題用の文字配列を、繰り返し処理によって描写し、20×20のボタンを表示させます。

```php:find_the_mistake.php
		<form action="result.php" method="POST">
			<table>
				<?php foreach ($targets as $target): ?>
				<tr>
					<?php for ($l = 0; $l < count($target); $l++): ?>
					<td><input type="submit" name="answer" value="<?= $target[$l]; ?>"></td>
					<?php endfor; ?>
				</tr>
				<?php endforeach; ?>
			</table>
		</form>
```
`<form>`タグの中で`submit`タイプの`<input>`タグを繰り返し表示させることで、
どのボタンを押しても「**result.php**」へ回答が`POST`されるようになっています。

不正解用文字配列を生成させた時と同様に、
**2重の繰り返し処理**によって多次元配列の値を表示させます。

まず`<?php foreach ($targets as $target): ?>`によって
第1層の繰り返し処理します。
繰り返しの対象を`<tr>`タグで囲んでおり、**ボタン群の行の繰り返し**に相当します。

上記`foreach`構文の中で更に繰り返し処理
`<?php for ($l = 0; $l < count($target); $l++): ?>`
を実行することで、第2層を表示させます[^8]。
こちらは`<td>`タグで囲っており、**各行内の列の繰り返し**に相当します。

[^8]: 脚注4.と同じように、カウンター変数はアルファベット順に`$l`としています。

これらの繰り返しにより、
「20個の`<td>`タグ内の`submit`ボタン」
が
「それぞれ20個の`<tr>`タグで囲われた」
計400個のボタン群を表示させることができます。

これで問題を表示させることができました！
次は結果ページを実装する前に、DB接続設定ファイルを作成します。

<details>
<summary><< 前の記事 [【②スタートページを作る】](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7)</summary>
&emsp;[2. スタートページの実装](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-スタートページの実装)
&emsp;&emsp;[2-1. セッション](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-1-セッション)
&emsp;&emsp;&emsp;[2-1-1. セッションの開始](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-1-1-セッションの開始)
&emsp;&emsp;&emsp;[2-1-2. セッションの放棄](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-1-2-セッションの放棄)
&emsp;&emsp;[2-2. バリデーション](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-バリデーション)
&emsp;&emsp;&emsp;[2-2-1. 無効な送信の拒否](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-1-無効な送信の拒否)
&emsp;&emsp;&emsp;[2-2-2. 名前のバリデーション](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-2-名前のバリデーション)
&emsp;&emsp;&emsp;&emsp;[2-2-2-1. 半角スペースのみ無効](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-2-1-半角スペースのみ無効)
&emsp;&emsp;&emsp;&emsp;[2-2-2-2. 文字数](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-2-2-文字数)
&emsp;&emsp;&emsp;&emsp;[2-2-2-3. 空白文字／制御文字無効](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-2-3-空白文字制御文字無効)
&emsp;&emsp;&emsp;[2-2-3. エラーメッセージの表示](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-3-エラーメッセージの表示)
&emsp;&emsp;&emsp;[2-2-4. 入力値の保持 - <input>タグ](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-2-4-入力値の保持---inputタグ)
&emsp;&emsp;[2-3. 問題表示ページへの遷移](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7#2-3-問題表示ページへの遷移)
</details>
<details>
<summary>次の記事 >> [【④DB接続設定ファイルを作る】](https://qiita.com/_Taturon_/items/f8e464821892555e80aa)</summary>
&emsp;[4. DB接続設定](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-db接続設定)
&emsp;&emsp;[4-1. PDOオブジェクトの属性](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-1-pdoオブジェクトの属性)
&emsp;&emsp;&emsp;[4-1-1. フェッチ形式の指定](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-1-1-フェッチ形式の指定)
&emsp;&emsp;&emsp;[4-1-2. エラーモードの設定](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-1-2-エラーモードの設定)
&emsp;&emsp;&emsp;&emsp;[4-1-2-1. エラーモードの違いによるエラー文の違い](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-1-2-1-エラーモードの違いによるエラー文の違い)
&emsp;&emsp;&emsp;[4-1-3. エミュレーションの設定](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-1-3-エミュレーションの設定)
&emsp;&emsp;&emsp;&emsp;[4-1-3-1. プリペアドステートメント](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-1-3-1-プリペアドステートメント)
&emsp;&emsp;&emsp;&emsp;[4-1-3-2. エミュレーション](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-1-3-2-エミュレーション)
&emsp;&emsp;[4-2. 例外発生時の処理](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-2-例外発生時の処理)
&emsp;&emsp;&emsp;[4-2-1. HTTPヘッダの送信](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-2-1-httpヘッダの送信)
&emsp;&emsp;&emsp;[4-2-2. 処理の中断とエラーメッセージの表示](https://qiita.com/_Taturon_/items/f8e464821892555e80aa#4-2-2-処理の中断とエラーメッセージの表示)
&emsp;&emsp;[4-3. DBの切断について](#4-3-dbの切断について)
</details>