HTML・PHP・MySQLだけで作る間違い探しゲーム【①ゲームの概要】

# 1. 間違い探しゲームの概要

20×20のボタンの中から指定された文字が表示されたボタンを見つける、シンプルな**間違い探しゲーム**です！~~解くのが簡単とは言っていない~~

以下の6つの記事で解説しています。

<details>
<summary>HTML・PHP・MySQLだけで作る間違い探しゲーム【①ゲームの概要】（本記事）</summary>
[HTML・PHP・MySQLだけで作る間違い探しゲーム【①ゲームの概要】](https://qiita.com/_Taturon_/items/5a8fb0f179f7ede6a60c)<br>
&emsp;[1. 間違い探しゲームの概要](#1-間違い探しゲームの概要)
&emsp;&emsp;[1-1. ゲームの全体像](#1-1-ゲームの全体像)
&emsp;&emsp;[1-2. 開発環境及びファイルの全体像](#1-2-開発環境及びファイルの全体像)
&emsp;&emsp;&emsp;[1-2-1. 開発・動作確認済み環境](#1-2-1-開発動作確認済み環境)
&emsp;&emsp;&emsp;[1-2-2. 作成するファイル](#1-2-2-作成するファイル)
&emsp;&emsp;&emsp;&emsp;[1-2-2-1. メインファイル](#1-2-2-1-メインファイル)
&emsp;&emsp;&emsp;&emsp;[1-2-2-2. サブファイル](#1-2-2-2-サブファイル)
&emsp;&emsp;[1-3. ソースコード](#1-3-ソースコード)
&emsp;&emsp;&emsp;[1-3-1. メインファイルのソースコード](#1-3-1-メインファイルのソースコード)
&emsp;&emsp;&emsp;[1-3-2. サブファイルのソースコード](#1-3-2-サブファイルのソースコード)
&emsp;&emsp;[1-4. 参考にさせて頂いた記事・サイトの一覧](#1-4-参考にさせて頂いた記事サイトの一覧)
</details>
<details>
<summary>HTML・PHP・MySQLだけで作る間違い探しゲーム【②スタートページを作る】</summary>
[HTML・PHP・MySQLだけで作る間違い探しゲーム【②スタートページを作る】](https://qiita.com/_Taturon_/items/eb7a2a72e6366c51b4f7)<br>
&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>HTML・PHP・MySQLだけで作る間違い探しゲーム【③問題表示ページを作る】</summary>
[HTML・PHP・MySQLだけで作る間違い探しゲーム【③問題表示ページを作る】](https://qiita.com/_Taturon_/items/cb1cee8a3035f476ccf2)<br>
&emsp;[3. 問題表示ページの実装](https://qiita.com/_Taturon_/items/cb1cee8a3035f476ccf2#3-問題表示ページの実装)
&emsp;&emsp;[3-1. 無効なアクセスの拒否](https://qiita.com/_Taturon_/items/cb1cee8a3035f476ccf2#3-1-無効なアクセスの拒否)
&emsp;&emsp;[3-2. リセット回数の計測](https://qiita.com/_Taturon_/items/cb1cee8a3035f476ccf2#3-2-リセット回数の計測)
&emsp;&emsp;[3-3. 問題用文字配列の設定](https://qiita.com/_Taturon_/items/cb1cee8a3035f476ccf2#3-3-問題用文字配列の設定)
&emsp;&emsp;&emsp;[3-3-1. 文字ペア配列の選択](https://qiita.com/_Taturon_/items/cb1cee8a3035f476ccf2#3-3-1-文字ペア配列の選択)
&emsp;&emsp;&emsp;[3-3-2. 文字ペア配列及び文字ペアのシャッフル](https://qiita.com/_Taturon_/items/cb1cee8a3035f476ccf2#3-3-2-文字ペア配列及び文字ペアのシャッフル)
&emsp;&emsp;&emsp;[3-3-3. 正解文字と不正解文字配列の設定](https://qiita.com/_Taturon_/items/cb1cee8a3035f476ccf2#3-3-3-正解文字と不正解文字配列の設定)
&emsp;&emsp;&emsp;&emsp;[3-3-3-1. 正解文字の設定](https://qiita.com/_Taturon_/items/cb1cee8a3035f476ccf2#3-3-3-1-正解文字の設定)
&emsp;&emsp;&emsp;&emsp;[3-3-3-2. 不正解文字配列の設定](https://qiita.com/_Taturon_/items/cb1cee8a3035f476ccf2#3-3-3-2-不正解文字配列の設定)
&emsp;&emsp;&emsp;[3-3-4. 問題用文字配列の生成](https://qiita.com/_Taturon_/items/cb1cee8a3035f476ccf2#3-3-4-問題用文字配列の生成)
&emsp;&emsp;[3-4. 開始時刻の記録](https://qiita.com/_Taturon_/items/cb1cee8a3035f476ccf2#3-4-開始時刻の記録)
&emsp;&emsp;[3-5. 選択肢の描写](https://qiita.com/_Taturon_/items/cb1cee8a3035f476ccf2#3-5-選択肢の描写)
</details>
<details>
<summary>HTML・PHP・MySQLだけで作る間違い探しゲーム【④DB接続設定ファイルを作る】</summary>
[HTML・PHP・MySQLだけで作る間違い探しゲーム【④DB接続設定ファイルを作る】](https://qiita.com/_Taturon_/items/f8e464821892555e80aa)<br>
&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>
<details>
<summary>HTML・PHP・MySQLだけで作る間違い探しゲーム【⑤結果表示ページを作る】</summary>
[HTML・PHP・MySQLだけで作る間違い探しゲーム【⑤結果表示ページを作る】](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5)<br>
&emsp;[5. 結果表示ページ](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-結果表示ページ)
&emsp;&emsp;[5-1. 無効なアクセスの拒否](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-1-無効なアクセスの拒否)
&emsp;&emsp;[5-2. 回答時間の算出](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-2-回答時間の算出)
&emsp;&emsp;[5-3. 各変数への格納](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-3-各変数への格納)
&emsp;&emsp;[5-4. 保存する回答時間とカウント数の上限設定](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-4-保存する回答時間とカウント数の上限設定)
&emsp;&emsp;[5-5. 正解／不正解による表示メッセージの分岐](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-5-正解不正解による表示メッセージの分岐)
&emsp;&emsp;[5-6. ランキングへの登録](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-6-ランキングへの登録)
&emsp;&emsp;&emsp;[5-6-1. ランキングテーブルの構成](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-6-1-ランキングテーブルの構成)
&emsp;&emsp;&emsp;[5-6-2. ランキングへの登録](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-6-2-ランキングへの登録)
</details>
<details>
<summary>HTML・PHP・MySQLだけで作る間違い探しゲーム【⑥ランキングページを作る】</summary>
[HTML・PHP・MySQLだけで作る間違い探しゲーム【⑥ランキングページを作る】](https://qiita.com/_Taturon_/items/5e6da2f18b341fcf744b)<br>
&emsp;[6. ランキング表示ページ](https://qiita.com/_Taturon_/items/5e6da2f18b341fcf744b#6-ランキング表示ページ)
&emsp;&emsp;[6-1. 表示切り替え機能](https://qiita.com/_Taturon_/items/5e6da2f18b341fcf744b#6-1-表示切り替え機能)
&emsp;&emsp;[6-2. 入力値の保持 - <select>タグ](https://qiita.com/_Taturon_/items/5e6da2f18b341fcf744b#6-2-入力値の保持---selectタグ)
&emsp;&emsp;[6-3. ランキングの表示](https://qiita.com/_Taturon_/items/5e6da2f18b341fcf744b#6-3-ランキングの表示)
&emsp;&emsp;&emsp;[6-3-1. テーブルの表示](https://qiita.com/_Taturon_/items/5e6da2f18b341fcf744b#6-3-1-テーブルの表示)
&emsp;&emsp;&emsp;[6-3-2. テーブルデザイン](https://qiita.com/_Taturon_/items/5e6da2f18b341fcf744b#6-3-2-テーブルデザイン)
</details>

まず、どのようなものなのか概要を説明します。
(サーバー構築やDB構築に関しては本記事群では扱いません)

## 1-1. ゲームの全体像

初期画面にアクセスすると、

- **名前**の入力欄
- **難易度**の選択ラジオボタン
- **ランキングへの登録認否**の選択ラジオボタン

が表示されます。
![スタート画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598695/77f0d5b8-f260-ba0e-084d-e53e4510d38c.png)

後で解説しますが、名前入力欄には

- **空白文字のみ無効**
- **１０文字以内**
- **名前の前後に空白文字／制御文字無効**

のバリデーションを入れており、引っかかった場合はエラーが表示されます。
![スタート画面_エラーメッセージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598695/8a63129d-2be5-1a24-db0e-61b8d342520e.png)
適切な名前を入力し、「問題に挑戦!」をクリックすると選択した難易度に応じた問題が表示されます。
![問題ページ_易しい.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)
正解・不正解を問わず、ボタンをクリックすると結果表示ページへ遷移します。
結果には

- **正解か不正解か**
- **回答に要した時間**
- **難易度**
- **リセット回数**

が表示されます。

![結果ページ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598695/b622d5b8-b88c-f350-6451-3e33e4cae47d.png)
結果表示ページの「ランキングページへ」をクリックすると回答時間ランキング表示画面へ遷移します。
![ランキングページ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598695/9915304b-efef-710f-f4b2-ec402398f2cb.png)
ランキングは難易度ごとに切り替えることもできます。

## 1-2. 開発環境及びファイルの全体像

### 1-2-1. 開発・動作確認済み環境

- PHP 7.1.29 (cli)
- MySQL Ver 14.14 Distrib 5.6.44

### 1-2-2. 作成するファイル
メインとなる**5つ**と補助的な**2つ**の合計**7つ**です。そこまで数が多くないのとファイル名のみ（相対パス）で参照できるようにする為、全て**同一ディレクトリ内に配置**します。

#### 1-2-2-1. メインファイル
- **start.php**
    - プレイヤーに名前/難易度/ランキング登録認否を選択してもらうページを生成するファイルです。 
- **find_the_mistake.php**
    - 問題を作成・表示するファイルです。
    - 20×20の文字が書かれたボタンを出現させ、その中に1つだけ正解を紛れ込ませます。
- **db_connect.php**
    - DB接続設定用のファイルです。
    - 下記result.phpとranking.phpで使用します。
- **result.php**
    - 回答が正解していたかどうかを示す結果画面です。
    - 回答時間とリセット回数をカウントする機能を実装し、その結果も示します。
- **ranking.php**
    - ランキングを表示するファイルです。
    - ここでは難易度別にランキングを表示切り替えできる機能も実装します。

#### 1-2-2-2. サブファイル
- **create_ranking_table.sql**
    - ランキングデータを保存するテーブルを作成するSQL文を記述したSQLファイルです。
- **style.css**
    - ランキングページのテーブルの見た目を整えるCSSファイルです。

## 1-3. ソースコード
GitHubのソースコードは[こちら](https://github.com/Taturon/Find_the_Mistake)からどうぞ。
### 1-3-1. メインファイルのソースコード
```php:start.php
<?php

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

// 送信されたデータの検証
if (isset($_POST['name'], $_POST['difficulty'], $_POST['permission'])) {

	// 変数への代入
	$name = $_POST['name'];
	$difficulty = $_POST['difficulty'];
	$permission = $_POST['permission'];

	// 名前のバリデーション
	if (empty(trim($name))) {
		$error_msg = '名前に空白は無効です';
	} elseif (mb_strlen($name, 'UTF-8') > 10) {
		$error_msg = '名前は10字以内にしてください';
	} elseif ($name !== preg_replace('/\A[\p{C}\p{Z}]++|[\p{C}\p{Z}]++\z/u', '', $name)) {
		$error_msg = '名前の前後に空白文字や制御文字を含めないで下さい';
	}

	// セッション変数への格納と問題表示ページへの遷移
	if (empty($error_msg)) {
		$_SESSION['name'] = $name;
		$_SESSION['difficulty'] = $difficulty;
		$_SESSION['permission'] = $permission;
		header('Location:find_the_mistake.php');
		exit();
	}
}

// セッションの放棄
$_SESSION = [];
setcookie(session_name(), '', time() - 1, '/');
session_destroy();

?>
<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta charset="UTF-8">
		<title>間違い探し</title>
	</head>
	<body>
		<h1>間違い探し</h1>
		<p>20×20個のボタンが表示されるので、指定されたボタンを見つけ出して押しましょう！</p>
		<?php if (isset($error_msg)): ?>
		<hr>
		<ul>
			<li><?= $error_msg; ?></li>
		</ul>
		<hr>
		<?php endif; ?>
		<form method="POST">
			<label>名前
				<small> (10字以内)</small><br>
				<input type="text" name="name" required value="<?php if (isset($name)) echo $name;?>">
			</label>
			<p>
				<span>難易度</span><br>
				<label>
					<input type="radio" name="difficulty" value="易しい（絵文字）" required <?php if (empty($difficulty) || isset($difficulty) && $difficulty === '易しい（絵文字）') echo 'checked';?>>
					易しい（絵文字）
				</label>
				<label>
					<input type="radio" name="difficulty" value="難しい（漢字）" <?php if (isset($difficulty) && $difficulty === '難しい（漢字）') echo 'checked';?>>
					難しい（漢字）
				</label>
			</p>
			<p>
				<span>ランキングへの登録<small>（正解だった場合のみ）</small></span><br>
				<label>
					<input type="radio" name="permission" value="許可しない" required <?php if (empty($permission) || isset($permission) && $permission === '許可しない') echo 'checked';?>>
					許可しない
				</label>
				<label>
					<input type="radio" name="permission" value="許可する" <?php if (isset($permission) && $permission === '許可する') echo 'checked';?>>
					許可する
				</label>
			</p>
			<input type="submit" value="問題に挑戦!（時間計測が開始されます）">
		</form>
		<p>
			<button type="button" onclick="location.href='ranking.php'">ランキングページへ</button>
		</p>
	</body>
</html>
```

```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>
```

```php:db_connect.php
<?php

// 定数定義
const PDO_DSN = 'mysql:host=localhost;dbname=[FILTERED];charset=utf8mb4';
const USERNAME = '[FILTERED]';
const PASSWORD = '[FILTERED]';

// DB接続
try {
	$dbh = new PDO(PDO_DSN, USERNAME, PASSWORD, [
		PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
		PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
		PDO::ATTR_EMULATE_PREPARES => false,
	]);
} catch (PDOException $e) {
	header('Content-Type: text/plain; charset=UTF-8', true, 500);
	exit('DB接続に失敗しました' . PHP_EOL . $e->getMessage() . PHP_EOL);
}
```

```php:result.php
<?php

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

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

// 回答時間を算出
$end_time = microtime(true);
$start_time = $_SESSION['start_time'];
$time = sprintf('%05.2f', $end_time - $start_time) . '秒';

// 回答を変数に格納
$answer = $_POST['answer'];

// セッション変数を変数に格納
$name = $_SESSION['name'];
$difficulty = $_SESSION['difficulty'];
$permission = $_SESSION['permission'];
$correct = $_SESSION['correct'];
$count = sprintf('%02d', $_SESSION['count']) . '回';

// セッションの放棄
$_SESSION = [];
setcookie(session_name(), '', time() - 1, '/');
session_destroy();

// 回答時間が100秒以上の場合は値を上書き
if ($time > 100) {
	$time = '100秒以上';
}

// リセット回数が100回以上の場合は値を上書き
if ($count > 100) {
	$count = '100回以上';
}

// 正解・不正解によるメッセージの分岐
if (html_entity_decode($correct) === $answer) {
	$result = '正解です！';
} else {
	$result = '不正解です。。。';
}

// 正解かつ許可されていた場合のみDBに登録
if ($result === '正解です！' && $permission === '許可する') {

	// db接続
	require_once('db_connect.php');

	// 新規登録処理
	$sql = 'INSERT INTO rankings (name, difficulty, time, reset) VALUES (?, ?, ?, ?)';
	$stmt = $dbh->prepare($sql);
	$stmt->execute([$name, $difficulty, $time, $count]);
}

?>
<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta charset="UTF-8">
		<title>間違い探し</title>
	</head>
	<body>
		<h1><small>結果は...<?= $result ?></small></h1>
		<h2><small>難易度: <?= $difficulty; ?></small></h2>
		<h2><small>回答時間: <?= $time; ?></small></h2>
		<h2><small>リセット回数: <?= $count; ?></small></h2>
		<button type="button" onclick="location.href='start.php'">スタートページへ</button>
		<button type="button" onclick="location.href='ranking.php'">ランキングページへ</button>
	</body>
</html>
```

```php:ranking.php
<?php

// 難易度がPOSTされている場合は変数に格納
if (isset($_POST['show_method'])) {
	$select = $_POST['show_method'];
} else {
	$select = 'all';
}

// db接続
require_once('db_connect.php');

// 選択された表示形式によってSQL文を分岐
$sql = 'SELECT * FROM rankings ';
switch ($select) {
	case 'difficult':
		$sql .= "WHERE difficulty = '難しい（漢字）' ORDER BY time LIMIT 10";
		break;
	case 'easy':
		$sql .= "WHERE difficulty = '易しい（絵文字）' ORDER BY time LIMIT 10";
		break;
	case 'all':
		$sql .= 'ORDER BY time LIMIT 10';
		break;
}
$stmt = $dbh->query($sql);
$players = $stmt->fetchAll();

?>
<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta charset="UTF-8">
		<link rel="stylesheet" href="style.css">
		<title>間違い探し ランキング</title>
	</head>
	<body>
		<h1>回答時間ランキング</h1>
		<form method="POST">
			<select name="show_method" size="1">
				<option value="all" <?php if ($select === 'all') echo 'selected' ?>>全て</option>
				<option value="easy" <?php if ($select === 'easy') echo 'selected' ?>>易しい</option>
				<option value="difficult" <?php if ($select === 'difficult') echo 'selected' ?>>難しい</option>
			</select>
			<input type="submit" value="表示">
		</form>
		<div class="table">
			<table class="s-tbl">
				<thead>
					<tr>
						<th>順位</th>
						<th>名前</th>
						<th>難易度</th>
						<th>回答時間</th>
						<th>リセット回数</th>
					</tr>
				</thead>
				<tbody>
					<?php foreach ($players as $key => $player): ?>
					<tr>
						<td><?= ++$key; ?></td>
						<td><?= $player['name']; ?></td>
						<td><?= $player['difficulty']; ?></td>
						<td><?= $player['time']; ?></td>
						<td><?= $player['reset']; ?></td>
					</tr>
					<?php endforeach; ?>
				</tbody>
			</table>
		</div>
		<p>
			<button type="button" onclick="location.href='start.php'">スタートページへ</button>
		</p>
	</body>
</html>
```
### 1-3-2. サブファイルのソースコード
```sql:create_rankings_table.sql
CREATE TABLE `rankings` (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `difficulty` varchar(10) NOT NULL,
  `time` varchar(10) NOT NULL,
  `reset` varchar(15) NOT NULL,
  PRIMARY KEY(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
```

```css:style.css
.table {
  margin-top: 10px;
}
.s-tbl {
  border-collapse: collapse;
}
.s-tbl th, .s-tbl td {
  border: 1px solid #000;
  padding: 0.5em;
}
.s-tbl tr:nth-child(even) {
  background: #eee;
}
.s-tbl tr:hover {
  background: #ffffe0;
}
```

## 1-4. 参考にさせて頂いた記事・サイトの一覧
**もちろん、公式マニュアル！ RTFM！**
PHPマニュアル「[PHP: PHP マニュアル - Manual](https://www.php.net/manual/ja/index.php)」

**PDOによるDB接続に関して体系的に書かれており非常に勉強になりました**
Qiita「[PHPでデータベースに接続するときのまとめ - Qiita](https://qiita.com/mpyw/items/b00b72c5c95aac573b71)」 by @mpyw さん

**クラスの基礎から例外処理までとても丁寧に解説されておりとても参考になりました**
Qiita「[【PHP超入門】クラス～例外処理～PDOの基礎 - Qiita](https://qiita.com/7968/items/6f089fec8dde676abb5b)」 by @7968 さん

**エミュレータのON／OFFに関する疑問が解決されました**
Teratail「[\`PDO::ATTR_EMULATE_PREPARES => false`は必要か？](https://teratail.com/questions/233051)」

**フロントに関する事なら外せないドキュメント**
MDN web docs「[開発者向けのウェブ技術 | MDN](https://developer.mozilla.org/ja/docs/Web)」

**ずっと疑問に思っていたことが解消されました**
Teratail「[なぜsession_startより前に何も出力があってはいけない？](https://teratail.com/questions/94655)」

**正規表現について新たな知見を得られました**
Qiita「[【PHP】マルチバイト(全角スペース等)対応のtrim処理 - Qiita](https://qiita.com/fallout/items/a13cebb07015d421fde3)」 by @fallout さん

**ReDoSに関してとても勉強になりました**
Qiita「[正規表現の落とし穴（ReDoS - Regular Expressions DoS） - Qiita](https://qiita.com/prograti/items/9b54cf82a08302a5d2c7)」 by @prograti さん

**絵文字のHTMLエンティティを調べるのに活用させて頂きました**
Let's EMOJI「[Unicode 13.0 絵文字 (Unicode 13.0 Emoji) | Let&#039;s EMOJI](https://lets-emoji.com/unicode13-emoji/)」

**sprintfに関してとても詳細に解説されています**
Let'sプログラミング ～初心者の方を対象としたプログラミングの総合学習サイト～
「[指定の形式にフォーマット(sprintf) - 文字列関数 - PHP関数](https://www.javadrive.jp/phpfunc/string/index7.html)」

**文字コードと照合順序に関しとても勉強になりました**
Qiita「[寿司ビール問題①　初心者→中級者へのSTEP20/25 - Qiita](https://qiita.com/kamohicokamo/items/3cc05f63a90148525caf)」 by @kamohicokamo さん

**この記事のおかげで命名の迷いがなくなりました**
Qiita「[データベースオブジェクトの命名規約 - Qiita](https://qiita.com/genzouw/items/35022fa96c120e67c637)」 by @genzouw さん

**テーブルのデザインの参考にさせていただきました**
いつか誰かの役に立つかもしれないweb制作屋の備忘録
「[css tableで背景色を交互に変える方法](https://blog.raizzenet.com/how-to-change-bg-color-for-table/)」

どの記事・ページもとても参考になりました！
この場を借りて**感謝を申し上げます**&#x1f647;

<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>