PHP バリデーションの設定の学習の振り返り

# 下記の入力フォーム($pageFlag = 0)にバリデーションの設定をする

```php:form.php
<?php

// sessionを使えるようにする
session_start();

// クリックジャッキング対策
header('X-FRAME-OPTIONS:DENY');

// トークンが発行されているかの確認
if (!empty($_POST)) {
    echo '<pre>';
    var_dump($_POST);
    echo '</pre>';
}

$pageFlag = 0; // 入力画面のフラグ(Topのフォーム画面)

// $_POST['btn_confirm']が空でなければ $psteFlag = 1 に切り替える
if (!empty($_POST['btn_confirm'])) {
    $pageFlag = 1;
}

// $_POST['btn_submit']が空でなければ $psteFlag = 2 に切り替える
if (!empty($_POST['btn_submit'])) {
    $pageFlag = 2;
}

// サニタイズ htmlspecialchars関数
function h($str)
{
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
?>
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <!-- フォームに入力された確認画面の作成 -->
    <?php if ($pageFlag === 1) : ?>
        <!-- 発行されたトークンと送信されてきたトークンが一致するかどうか？ -->
        <?php if (isset($_SESSION['csrfToken']) && $_POST['csrf'] === $_SESSION['csrfToken']) : ?>
            <form method="POST" action="form.php">
                氏名
                <?php echo h($_POST['name']); ?> <!-- サニタイズ -->
                <br>
                メールアドレス
                <?php echo h($_POST['email']); ?> <!-- サニタイズ -->
                <br>
                ホームページ
                <?php echo h($_POST['url']); ?> <!-- サニタイズ -->
                <br>
                性別
                <?php
                echo $_POST['gender'] === '0' ? '男性' : '女性';
                ?>
                <br>
                年齢
                <?php
                if ($_POST['age'] === '1') {
                    echo '〜19歳';
                }
                if ($_POST['age'] === '2') {
                    echo '20歳〜29歳';
                }
                if ($_POST['age'] === '3') {
                    echo '30歳〜39歳';
                }
                if ($_POST['age'] === '4') {
                    echo '40歳〜49歳';
                }
                if ($_POST['age'] === '5') {
                    echo '50歳〜59歳';
                }
                if ($_POST['age'] === '6') {
                    echo '60歳〜';
                }
                ?>
                <br>
                お問い合わせ内容
                <br>
                <?php echo nl2br(h($_POST['contact'])); ?> <!-- サニタイズ -->
                <br>
                <!-- name属性の 'back' は $pageFlagの定義がないので 初期設定の '$pageFlag = 0;' になる -->
                <input type="submit" name="back" value="戻る">
                <!-- 完了ページへデータを送信する為のボタン($pageFlag = 2) -->
                <input type="submit" name="btn_submit" value="送信する">
                <!-- 各入力データの保持 hiddenの使用 -->
                <input type="hidden" name="name" value="<?php echo h($_POST['name']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="email" value="<?php echo h($_POST['email']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="url" value="<?php echo h($_POST['url']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="gender" value="<?php echo h($_POST['gender']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="age" value="<?php echo h($_POST['age']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="contact" value="<?php echo h($_POST['contact']); ?>"> <!-- サニタイズ -->
                <!-- トークンの保持 -->
                <input type="hidden" name="csrf" value="<?php echo h($_POST['csrf']); ?>"> <!-- サニタイズ -->
            </form>
            <?php else : ?>
                <p>不正なアクセスです。</p>
        <?php endif; ?>
    <?php endif; ?>

    <!-- 確認画面から送信後に遷移する完了画面の作成 -->
    <?php if ($pageFlag === 2) : ?>
        <?php if ($_POST['csrf'] === $_SESSION['csrfToken']) : ?>
            送信が完了しました。
            <!-- sessionトークンの削除 -->
            <?php unset($_SESSION['csrfToken']);  ?>
            <?php else : ?>
                <p>不正なアクセスです。</p>
                <?php unset($_SESSION['csrfToken']);  ?>
        <?php endif; ?>
    <?php endif; ?>

    <!-- トークンが発行されていないときはトークンを発行する -->
    <?php if ($pageFlag === 0) : ?>
        <?php if (!isset($_SESSION['csrfToken'])) {
            $csrfToken = bin2hex(random_bytes(32));
            $_SESSION['csrfToken'] = $csrfToken;
        }
        $token = $_SESSION['csrfToken'];
        ?>
        <!-- フォーム入力画面を作成する(TOP) -->
        <form method="POST" action="form.php">
            氏名
            <!-- もし $_POST['name']が空でなければ、入力データを表示 -->
            <input type="text" name="name" value="<?php if (!empty($_POST['name'])) {
                                                        echo h($_POST['name']);
                                                    } ?>"> <!-- サニタイズ -->
            <br>
            メールアドレス
            <!-- もし $_POST['email']が空でなければ、入力データを表示 -->
            <input type="email" name="email" value="<?php if (!empty($_POST['email'])) {
                                                        echo h($_POST['email']);
                                                    } ?>"> <!-- サニタイズ -->
            <br>
            ホームページ
            <input type="url" name="url" value="<?php if (!empty($_POST['url'])) {
                                                    echo h($_POST['url']);
                                                } ?>">
            <br>
            性別
            <!-- !emptyをしようすると0でもtrueになってしまう為にissetを使用する -->
            <input type="radio" name="gender" value="0" <?php if (isset($_POST['gender']) && $_POST['gender'] === '0') {
                                                            echo 'checked';
                                                        } ?>>男性
            <input type="radio" name="gender" value="1" <?php if (isset($_POST['gender']) && $_POST['gender'] === '1') {
                                                            echo 'checked';
                                                        } ?>>女性
            <br>
            年齢
            <select name="age">
                <option value="">選択してください</option>
                <option value="1" <?php if (isset($_POST['age']) && $_POST['age'] === '1') {
                                        echo 'selected';
                                    } ?>>〜19歳</option>
                <option value="2" <?php if (isset($_POST['age']) && $_POST['age'] === '2') {
                                        echo 'selected';
                                    } ?>>20歳〜29歳</option>
                <option value="3" <?php if (isset($_POST['age']) && $_POST['age'] === '3') {
                                        echo 'selected';
                                    } ?>>30歳〜39歳</option>
                <option value="4" <?php if (isset($_POST['age']) && $_POST['age'] === '4') {
                                        echo 'selected';
                                    } ?>>40歳〜49歳</option>
                <option value="5" <?php if (isset($_POST['age']) && $_POST['age'] === '5') {
                                        echo 'selected';
                                    } ?>>50歳〜59歳</option>
                <option value="6" <?php if (isset($_POST['age']) && $_POST['age'] === '6') {
                                        echo 'selected';
                                    } ?>>60歳〜</option>
            </select>
            <br>
            お問い合わせ内容
            <textarea name="contact"><?php if (!empty($_POST['contact'])) {
                                            echo h($_POST['contact']);
                                        } ?></textarea>
            <br>
            <input type="checkbox" name="caution" value="1">注意事項にチェックする
            <br>
            <!-- btn_confirm(確認ページ)に入力データが送信されるようにする -->
            <input type="submit" name="btn_confirm" value="確認する">
            <!-- トークンを保持 -->
            <input type="hidden" name="csrf" value="<?php echo h($token) ?>">
        </form>
    <?php endif; ?>
</body>

</html>
```

- まずは、氏名入力のバリデーションの設定をしてみて試して見る。

検証条件は先ず、 氏名は `必須`ということで設定。

1: バリデーション用の関数は、`validation.php`と言うファイルを作成し、そのファイルを
form.phpに読み込むようにしたい。
その場合は `form.php`に `requrie 'validation.php';`のようにファイルを読込むコードを書けば、`form.php`　に `validation.php` を読込むことができる。

```php:form.php
<?php

// sessionを使えるようにする
session_start();

// 1. 追加 validation.phpの読み込み
require 'validation.php';

// クリックジャッキング対策
header('X-FRAME-OPTIONS:DENY');

// トークンが発行されているかの確認
if (!empty($_POST)) {
    echo '<pre>';
    var_dump($_POST);
    echo '</pre>';
}

$pageFlag = 0; // 入力画面のフラグ(Topのフォーム画面)

// $_POST['btn_confirm']が空でなければ $psteFlag = 1 に切り替える
if (!empty($_POST['btn_confirm'])) {
    $pageFlag = 1;
}

// $_POST['btn_submit']が空でなければ $psteFlag = 2 に切り替える
if (!empty($_POST['btn_submit'])) {
    $pageFlag = 2;
}

// サニタイズ htmlspecialchars関数
function h($str)
{
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
?>
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <!-- フォームに入力された確認画面の作成 -->
    <?php if ($pageFlag === 1) : ?>
        <!-- 発行されたトークンと送信されてきたトークンが一致するかどうか？ -->
        <?php if (isset($_SESSION['csrfToken']) && $_POST['csrf'] === $_SESSION['csrfToken']) : ?>
            <form method="POST" action="form.php">
                氏名
                <?php echo h($_POST['name']); ?> <!-- サニタイズ -->
                <br>
                メールアドレス
                <?php echo h($_POST['email']); ?> <!-- サニタイズ -->
                <br>
                ホームページ
                <?php echo h($_POST['url']); ?> <!-- サニタイズ -->
                <br>
                性別
                <?php
                echo $_POST['gender'] === '0' ? '男性' : '女性';
                ?>
                <br>
                年齢
                <?php
                if ($_POST['age'] === '1') {
                    echo '〜19歳';
                }
                if ($_POST['age'] === '2') {
                    echo '20歳〜29歳';
                }
                if ($_POST['age'] === '3') {
                    echo '30歳〜39歳';
                }
                if ($_POST['age'] === '4') {
                    echo '40歳〜49歳';
                }
                if ($_POST['age'] === '5') {
                    echo '50歳〜59歳';
                }
                if ($_POST['age'] === '6') {
                    echo '60歳〜';
                }
                ?>
                <br>
                お問い合わせ内容
                <br>
                <?php echo nl2br(h($_POST['contact'])); ?> <!-- サニタイズ -->
                <br>
                <!-- name属性の 'back' は $pageFlagの定義がないので 初期設定の '$pageFlag = 0;' になる -->
                <input type="submit" name="back" value="戻る">
                <!-- 完了ページへデータを送信する為のボタン($pageFlag = 2) -->
                <input type="submit" name="btn_submit" value="送信する">
                <!-- 各入力データの保持 hiddenの使用 -->
                <input type="hidden" name="name" value="<?php echo h($_POST['name']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="email" value="<?php echo h($_POST['email']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="url" value="<?php echo h($_POST['url']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="gender" value="<?php echo h($_POST['gender']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="age" value="<?php echo h($_POST['age']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="contact" value="<?php echo h($_POST['contact']); ?>"> <!-- サニタイズ -->
                <!-- トークンの保持 -->
                <input type="hidden" name="csrf" value="<?php echo h($_POST['csrf']); ?>"> <!-- サニタイズ -->
            </form>
            <?php else : ?>
                <p>不正なアクセスです。</p>
        <?php endif; ?>
    <?php endif; ?>

    <!-- 確認画面から送信後に遷移する完了画面の作成 -->
    <?php if ($pageFlag === 2) : ?>
        <?php if ($_POST['csrf'] === $_SESSION['csrfToken']) : ?>
            送信が完了しました。
            <!-- sessionトークンの削除 -->
            <?php unset($_SESSION['csrfToken']);  ?>
            <?php else : ?>
                <p>不正なアクセスです。</p>
                <?php unset($_SESSION['csrfToken']);  ?>
        <?php endif; ?>
    <?php endif; ?>

    <!-- トークンが発行されていないときはトークンを発行する -->
    <?php if ($pageFlag === 0) : ?>
        <?php if (!isset($_SESSION['csrfToken'])) {
            $csrfToken = bin2hex(random_bytes(32));
            $_SESSION['csrfToken'] = $csrfToken;
        }
        $token = $_SESSION['csrfToken'];
        ?>
        <!-- フォーム入力画面を作成する(TOP) -->
        <form method="POST" action="form.php">
            氏名
            <!-- もし $_POST['name']が空でなければ、入力データを表示 -->
            <input type="text" name="name" value="<?php if (!empty($_POST['name'])) {
                                                        echo h($_POST['name']);
                                                    } ?>"> <!-- サニタイズ -->
            <br>
            メールアドレス
            <!-- もし $_POST['email']が空でなければ、入力データを表示 -->
            <input type="email" name="email" value="<?php if (!empty($_POST['email'])) {
                                                        echo h($_POST['email']);
                                                    } ?>"> <!-- サニタイズ -->
            <br>
            ホームページ
            <input type="url" name="url" value="<?php if (!empty($_POST['url'])) {
                                                    echo h($_POST['url']);
                                                } ?>">
            <br>
            性別
            <!-- !emptyをしようすると0でもtrueになってしまう為にissetを使用する -->
            <input type="radio" name="gender" value="0" <?php if (isset($_POST['gender']) && $_POST['gender'] === '0') {
                                                            echo 'checked';
                                                        } ?>>男性
            <input type="radio" name="gender" value="1" <?php if (isset($_POST['gender']) && $_POST['gender'] === '1') {
                                                            echo 'checked';
                                                        } ?>>女性
            <br>
            年齢
            <select name="age">
                <option value="">選択してください</option>
                <option value="1" <?php if (isset($_POST['age']) && $_POST['age'] === '1') {
                                        echo 'selected';
                                    } ?>>〜19歳</option>
                <option value="2" <?php if (isset($_POST['age']) && $_POST['age'] === '2') {
                                        echo 'selected';
                                    } ?>>20歳〜29歳</option>
                <option value="3" <?php if (isset($_POST['age']) && $_POST['age'] === '3') {
                                        echo 'selected';
                                    } ?>>30歳〜39歳</option>
                <option value="4" <?php if (isset($_POST['age']) && $_POST['age'] === '4') {
                                        echo 'selected';
                                    } ?>>40歳〜49歳</option>
                <option value="5" <?php if (isset($_POST['age']) && $_POST['age'] === '5') {
                                        echo 'selected';
                                    } ?>>50歳〜59歳</option>
                <option value="6" <?php if (isset($_POST['age']) && $_POST['age'] === '6') {
                                        echo 'selected';
                                    } ?>>60歳〜</option>
            </select>
            <br>
            お問い合わせ内容
            <textarea name="contact"><?php if (!empty($_POST['contact'])) {
                                            echo h($_POST['contact']);
                                        } ?></textarea>
            <br>
            <input type="checkbox" name="caution" value="1">注意事項にチェックする
            <br>
            <!-- btn_confirm(確認ページ)に入力データが送信されるようにする -->
            <input type="submit" name="btn_confirm" value="確認する">
            <!-- トークンを保持 -->
            <input type="hidden" name="csrf" value="<?php echo h($token) ?>">
        </form>
    <?php endif; ?>
</body>

</html>
```

# validation.phpを作成し、バリデーション関数を書く

2: 関数の引数には、 `$_POST` の連想配列が入ってくるようにする。
3: 氏名が空の場合は、 `$errors` の配列に値(エラー内容)を追加するようにする。
4: その `$errors`を `return` で `form.php` で　バリデーション関数の実行元に返すようにする。

```php:validation.php
<?php

// 2. $request には $POST の連想配列が入ってくる
function validation($request)
{
    // 5. $errorsにまとめて配列で入ってくるようにする
    // ローカル変数なので、外側からアクセスすることができないので form.phpにもエラーを入れる変数を作る必要かある
    $errors = [];

    // 3. 氏名が空の場合は $errors の配列に値を追加する
    if (empty($request['name'])) {
        $errors[] = '氏名は必須です';
    }

    return $errors; // 4. validation関数の呼び出し元に返す
}
```

# form.phpに `validation`関数を実行させるコードを記述

5: `validation`関数が `form.php`で実行され、検証にひっかかれば `$errors`
に値が返ってくるようにする。
6: `$errors` が空ならと言う条件を足して `$pageFlag = 1` に遷移するようにする。

```php:form.php
<?php

// sessionを使えるようにする
session_start();

// 1. 追加 validation.phpの読み込み
require 'validation.php';

// クリックジャッキング対策
header('X-FRAME-OPTIONS:DENY');

// トークンが発行されているかの確認
if (!empty($_POST)) {
    echo '<pre>';
    var_dump($_POST);
    echo '</pre>';
}

$pageFlag = 0; // 入力画面のフラグ(Topのフォーム画面)
// 5. 追加 validation関数が実行され、検証にひっかかれば $errors に値が返ってくる
$errors = validation($_POST);

// $_POST['btn_confirm']が空でなければ $psteFlag = 1 に切り替える
// 6. 編集 $errorsが空ならと言う条件を足して $pageFlag = 1 に遷移するようにする
if (!empty($_POST['btn_confirm']) && empty($errors)) {
    $pageFlag = 1;
}

// $_POST['btn_submit']が空でなければ $psteFlag = 2 に切り替える
if (!empty($_POST['btn_submit'])) {
    $pageFlag = 2;
}

// サニタイズ htmlspecialchars関数
function h($str)
{
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
?>
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <!-- フォームに入力された確認画面の作成 -->
    <?php if ($pageFlag === 1) : ?>
        <!-- 発行されたトークンと送信されてきたトークンが一致するかどうか？ -->
        <?php if (isset($_SESSION['csrfToken']) && $_POST['csrf'] === $_SESSION['csrfToken']) : ?>
            <form method="POST" action="form.php">
                氏名
                <?php echo h($_POST['name']); ?> <!-- サニタイズ -->
                <br>
                メールアドレス
                <?php echo h($_POST['email']); ?> <!-- サニタイズ -->
                <br>
                ホームページ
                <?php echo h($_POST['url']); ?> <!-- サニタイズ -->
                <br>
                性別
                <?php
                echo $_POST['gender'] === '0' ? '男性' : '女性';
                ?>
                <br>
                年齢
                <?php
                if ($_POST['age'] === '1') {
                    echo '〜19歳';
                }
                if ($_POST['age'] === '2') {
                    echo '20歳〜29歳';
                }
                if ($_POST['age'] === '3') {
                    echo '30歳〜39歳';
                }
                if ($_POST['age'] === '4') {
                    echo '40歳〜49歳';
                }
                if ($_POST['age'] === '5') {
                    echo '50歳〜59歳';
                }
                if ($_POST['age'] === '6') {
                    echo '60歳〜';
                }
                ?>
                <br>
                お問い合わせ内容
                <br>
                <?php echo nl2br(h($_POST['contact'])); ?> <!-- サニタイズ -->
                <br>
                <!-- name属性の 'back' は $pageFlagの定義がないので 初期設定の '$pageFlag = 0;' になる -->
                <input type="submit" name="back" value="戻る">
                <!-- 完了ページへデータを送信する為のボタン($pageFlag = 2) -->
                <input type="submit" name="btn_submit" value="送信する">
                <!-- 各入力データの保持 hiddenの使用 -->
                <input type="hidden" name="name" value="<?php echo h($_POST['name']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="email" value="<?php echo h($_POST['email']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="url" value="<?php echo h($_POST['url']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="gender" value="<?php echo h($_POST['gender']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="age" value="<?php echo h($_POST['age']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="contact" value="<?php echo h($_POST['contact']); ?>"> <!-- サニタイズ -->
                <!-- トークンの保持 -->
                <input type="hidden" name="csrf" value="<?php echo h($_POST['csrf']); ?>"> <!-- サニタイズ -->
            </form>
        <?php else : ?>
            <p>不正なアクセスです。</p>
        <?php endif; ?>
    <?php endif; ?>

    <!-- 確認画面から送信後に遷移する完了画面の作成 -->
    <?php if ($pageFlag === 2) : ?>
        <?php if ($_POST['csrf'] === $_SESSION['csrfToken']) : ?>
            送信が完了しました。
            <!-- sessionトークンの削除 -->
            <?php unset($_SESSION['csrfToken']);  ?>
            <?php else : ?>
                <p>不正なアクセスです。</p>
                <?php unset($_SESSION['csrfToken']);  ?>
        <?php endif; ?>
    <?php endif; ?>

    <!-- トークンが発行されていないときはトークンを発行する -->
    <?php if ($pageFlag === 0) : ?>
        <?php if (!isset($_SESSION['csrfToken'])) {
            $csrfToken = bin2hex(random_bytes(32));
            $_SESSION['csrfToken'] = $csrfToken;
        }
        $token = $_SESSION['csrfToken'];
        ?>
        <!-- フォーム入力画面を作成する(TOP) -->
        <form method="POST" action="form.php">
            氏名
            <!-- もし $_POST['name']が空でなければ、入力データを表示 -->
            <input type="text" name="name" value="<?php if (!empty($_POST['name'])) {
                                                        echo h($_POST['name']);
                                                    } ?>"> <!-- サニタイズ -->
            <br>
            メールアドレス
            <!-- もし $_POST['email']が空でなければ、入力データを表示 -->
            <input type="email" name="email" value="<?php if (!empty($_POST['email'])) {
                                                        echo h($_POST['email']);
                                                    } ?>"> <!-- サニタイズ -->
            <br>
            ホームページ
            <input type="url" name="url" value="<?php if (!empty($_POST['url'])) {
                                                    echo h($_POST['url']);
                                                } ?>">
            <br>
            性別
            <!-- !emptyをしようすると0でもtrueになってしまう為にissetを使用する -->
            <input type="radio" name="gender" value="0" <?php if (isset($_POST['gender']) && $_POST['gender'] === '0') {
                                                            echo 'checked';
                                                        } ?>>男性
            <input type="radio" name="gender" value="1" <?php if (isset($_POST['gender']) && $_POST['gender'] === '1') {
                                                            echo 'checked';
                                                        } ?>>女性
            <br>
            年齢
            <select name="age">
                <option value="">選択してください</option>
                <option value="1" <?php if (isset($_POST['age']) && $_POST['age'] === '1') {
                                        echo 'selected';
                                    } ?>>〜19歳</option>
                <option value="2" <?php if (isset($_POST['age']) && $_POST['age'] === '2') {
                                        echo 'selected';
                                    } ?>>20歳〜29歳</option>
                <option value="3" <?php if (isset($_POST['age']) && $_POST['age'] === '3') {
                                        echo 'selected';
                                    } ?>>30歳〜39歳</option>
                <option value="4" <?php if (isset($_POST['age']) && $_POST['age'] === '4') {
                                        echo 'selected';
                                    } ?>>40歳〜49歳</option>
                <option value="5" <?php if (isset($_POST['age']) && $_POST['age'] === '5') {
                                        echo 'selected';
                                    } ?>>50歳〜59歳</option>
                <option value="6" <?php if (isset($_POST['age']) && $_POST['age'] === '6') {
                                        echo 'selected';
                                    } ?>>60歳〜</option>
            </select>
            <br>
            お問い合わせ内容
            <textarea name="contact"><?php if (!empty($_POST['contact'])) {
                                            echo h($_POST['contact']);
                                        } ?></textarea>
            <br>
            <input type="checkbox" name="caution" value="1">注意事項にチェックする
            <br>
            <!-- btn_confirm(確認ページ)に入力データが送信されるようにする -->
            <input type="submit" name="btn_confirm" value="確認する">
            <!-- トークンを保持 -->
            <input type="hidden" name="csrf" value="<?php echo h($token) ?>">
        </form>
    <?php endif; ?>
</body>

</html>
```

# 入力画面にバリデーションエラーを表示する

7: `!empty($errors) だけの条件だと、
入力する前は未入力(空)なのでエラーとして表示されてしまうので
確認ボタンも押されたらという条件足すようにする(エラーが空ではなく、`btn_confirm`の中身も空ではなければエラーを表示させる)。

```php:form.php
<?php

// sessionを使えるようにする
session_start();

// 1. 追加 validation.phpの読み込み
require 'validation.php';

// クリックジャッキング対策
header('X-FRAME-OPTIONS:DENY');

// トークンが発行されているかの確認
if (!empty($_POST)) {
    echo '<pre>';
    var_dump($_POST);
    echo '</pre>';
}

$pageFlag = 0; // 入力画面のフラグ(Topのフォーム画面)
// 5. 追加 validation関数が実行され、検証にひっかかれば $errors に値が返ってくる
$errors = validation($_POST);

// $_POST['btn_confirm']が空でなければ $psteFlag = 1 に切り替える
// 6. 編集 $errorsが空ならと言う条件を足して $pageFlag = 1 に遷移するようにする
if (!empty($_POST['btn_confirm']) && empty($errors)) {
    $pageFlag = 1;
}

// $_POST['btn_submit']が空でなければ $psteFlag = 2 に切り替える
if (!empty($_POST['btn_submit'])) {
    $pageFlag = 2;
}

// サニタイズ htmlspecialchars関数
function h($str)
{
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
?>
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <!-- フォームに入力された確認画面の作成 -->
    <?php if ($pageFlag === 1) : ?>
        <!-- 発行されたトークンと送信されてきたトークンが一致するかどうか？ -->
        <?php if (isset($_SESSION['csrfToken']) && $_POST['csrf'] === $_SESSION['csrfToken']) : ?>
            <form method="POST" action="form.php">
                氏名
                <?php echo h($_POST['name']); ?> <!-- サニタイズ -->
                <br>
                メールアドレス
                <?php echo h($_POST['email']); ?> <!-- サニタイズ -->
                <br>
                ホームページ
                <?php echo h($_POST['url']); ?> <!-- サニタイズ -->
                <br>
                性別
                <?php
                echo $_POST['gender'] === '0' ? '男性' : '女性';
                ?>
                <br>
                年齢
                <?php
                if ($_POST['age'] === '1') {
                    echo '〜19歳';
                }
                if ($_POST['age'] === '2') {
                    echo '20歳〜29歳';
                }
                if ($_POST['age'] === '3') {
                    echo '30歳〜39歳';
                }
                if ($_POST['age'] === '4') {
                    echo '40歳〜49歳';
                }
                if ($_POST['age'] === '5') {
                    echo '50歳〜59歳';
                }
                if ($_POST['age'] === '6') {
                    echo '60歳〜';
                }
                ?>
                <br>
                お問い合わせ内容
                <br>
                <?php echo nl2br(h($_POST['contact'])); ?> <!-- サニタイズ -->
                <br>
                <!-- name属性の 'back' は $pageFlagの定義がないので 初期設定の '$pageFlag = 0;' になる -->
                <input type="submit" name="back" value="戻る">
                <!-- 完了ページへデータを送信する為のボタン($pageFlag = 2) -->
                <input type="submit" name="btn_submit" value="送信する">
                <!-- 各入力データの保持 hiddenの使用 -->
                <input type="hidden" name="name" value="<?php echo h($_POST['name']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="email" value="<?php echo h($_POST['email']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="url" value="<?php echo h($_POST['url']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="gender" value="<?php echo h($_POST['gender']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="age" value="<?php echo h($_POST['age']); ?>"> <!-- サニタイズ -->
                <input type="hidden" name="contact" value="<?php echo h($_POST['contact']); ?>"> <!-- サニタイズ -->
                <!-- トークンの保持 -->
                <input type="hidden" name="csrf" value="<?php echo h($_POST['csrf']); ?>"> <!-- サニタイズ -->
            </form>
        <?php else : ?>
            <p>不正なアクセスです。</p>
        <?php endif; ?>
    <?php endif; ?>

    <!-- 確認画面から送信後に遷移する完了画面の作成 -->
    <?php if ($pageFlag === 2) : ?>
        <?php if ($_POST['csrf'] === $_SESSION['csrfToken']) : ?>
            送信が完了しました。
            <!-- sessionトークンの削除 -->
            <?php unset($_SESSION['csrfToken']);  ?>
            <?php else : ?>
                <p>不正なアクセスです。</p>
                <?php unset($_SESSION['csrfToken']);  ?>
        <?php endif; ?>
    <?php endif; ?>

    <!-- トークンが発行されていないときはトークンを発行する -->
    <?php if ($pageFlag === 0) : ?>
        <?php if (!isset($_SESSION['csrfToken'])) {
            $csrfToken = bin2hex(random_bytes(32));
            $_SESSION['csrfToken'] = $csrfToken;
        }
        $token = $_SESSION['csrfToken'];
        ?>

        <!-- 7. !$empty($errors) だけだと、入力する前は未入力(空)なのでエラーとして表示されてしまうので確認ボタンも押されたらという条件も合わせるようにする -->
        <!-- エラーが空ではなくかつ、btn_confirmも空ではなかったらバリデーションエラーが表示される -->
        <?php if (!empty($errors) && !empty($_POST['btn_confirm'])) : ?>
            <!-- バリデーションエラーの表示 -->
            <?php echo '<ul>'; ?>
            <?php
            foreach ($errors as $error) {
                echo '<li>' . $error . '</li>';
            }
            ?>
            <?php echo '</ul>'; ?>
        <?php endif; ?>
        <!-- フォーム入力画面を作成する(TOP) -->
        <form method="POST" action="form.php">
            氏名
            <!-- もし $_POST['name']が空でなければ、入力データを表示 -->
            <input type="text" name="name" value="<?php if (!empty($_POST['name'])) {
                                                        echo h($_POST['name']);
                                                    } ?>"> <!-- サニタイズ -->
            <br>
            メールアドレス
            <!-- もし $_POST['email']が空でなければ、入力データを表示 -->
            <input type="email" name="email" value="<?php if (!empty($_POST['email'])) {
                                                        echo h($_POST['email']);
                                                    } ?>"> <!-- サニタイズ -->
            <br>
            ホームページ
            <input type="url" name="url" value="<?php if (!empty($_POST['url'])) {
                                                    echo h($_POST['url']);
                                                } ?>">
            <br>
            性別
            <!-- !emptyをしようすると0でもtrueになってしまう為にissetを使用する -->
            <input type="radio" name="gender" value="0" <?php if (isset($_POST['gender']) && $_POST['gender'] === '0') {
                                                            echo 'checked';
                                                        } ?>>男性
            <input type="radio" name="gender" value="1" <?php if (isset($_POST['gender']) && $_POST['gender'] === '1') {
                                                            echo 'checked';
                                                        } ?>>女性
            <br>
            年齢
            <select name="age">
                <option value="">選択してください</option>
                <option value="1" <?php if (isset($_POST['age']) && $_POST['age'] === '1') {
                                        echo 'selected';
                                    } ?>>〜19歳</option>
                <option value="2" <?php if (isset($_POST['age']) && $_POST['age'] === '2') {
                                        echo 'selected';
                                    } ?>>20歳〜29歳</option>
                <option value="3" <?php if (isset($_POST['age']) && $_POST['age'] === '3') {
                                        echo 'selected';
                                    } ?>>30歳〜39歳</option>
                <option value="4" <?php if (isset($_POST['age']) && $_POST['age'] === '4') {
                                        echo 'selected';
                                    } ?>>40歳〜49歳</option>
                <option value="5" <?php if (isset($_POST['age']) && $_POST['age'] === '5') {
                                        echo 'selected';
                                    } ?>>50歳〜59歳</option>
                <option value="6" <?php if (isset($_POST['age']) && $_POST['age'] === '6') {
                                        echo 'selected';
                                    } ?>>60歳〜</option>
            </select>
            <br>
            お問い合わせ内容
            <textarea name="contact"><?php if (!empty($_POST['contact'])) {
                                            echo h($_POST['contact']);
                                        } ?></textarea>
            <br>
            <input type="checkbox" name="caution" value="1">注意事項にチェックする
            <br>
            <!-- btn_confirm(確認ページ)に入力データが送信されるようにする -->
            <input type="submit" name="btn_confirm" value="確認する">
            <!-- トークンを保持 -->
            <input type="hidden" name="csrf" value="<?php echo h($token) ?>">
        </form>
    <?php endif; ?>
</body>

</html>
```

# その他のバリデーションの追加

8: 氏名の入力文字数の制限を追加 (20文字以内)


9: メールアドレス (必須/形式) メールアドレスだったら　true, 違えば false を返す PHPの標準関数 filter_var を使ってみる。
```php:sample.php
<?php
filter_var(filterをかけたい値, email形式がどうかの判定, 省略可);
?>
```
10: ホームページ (形式) URLだったら ture, 違えば false を返す。 PHP標準関数 filter_var を使ってみる。
ホームページは持っていない人もいるので入力があれば検証されるようにする。
```php:sample.php
<?php
filter_var(filterをかけたい値, url形式がどうかの判定, 省略可);
?>
```
11: 性別 (必須) ※ 0 の場合でも __`empty`__ を使用すると __`true`__ になるため、 __`isset`__ を使う。


12: 年齢 (必須) ※ age の value属性には 0 の値を定義してないので __`empty`__ が使える。
age の value属性の値は 1〜6であるが、万が一 6 より大きい値を入力されることがあるかもしれないのでその検証もする。


13: お問い合わせ内容 (必須/200文字以内)


14: 注意事項: (必須)

```php:validation.php
<?php

// 2. $request には $POST の連想配列が入ってくる
function validation($request)
{
    // 5. $errorsにまとめて配列で入ってくるようにする
    // ローカル変数なので、外側からアクセスすることができないので form.phpにもエラーを入れる変数を作る必要かある
    $errors = [];

    // 3. 氏名が空の場合は $errors の配列に値を追加する 
    // 8. 氏名の入力文字数の制限 (20文字以内)
    if (empty($request['name']) || 20 < mb_strlen($request['name'])) {
        $errors[] = '「氏名」は必須です。20文字以内で入力してください。';
    }

    // 9. メールアドレス (必須/形式) メールアドレスだったら true, 違えば false を返す。
    // PHPの標準関数 filter_var を使ってみる。
    if (empty($request['email']) || !filter_var($request['email'], FILTER_VALIDATE_EMAIL)) {
        $errors[] = '「メールアドレス」は必須です。正しい形式で入力してください。';
    }

    // 10. ホームページ (形式) URLだったら true, 違えば false を返す。
    // PHPの標準関数 filter_var を使ってみる。
    if (!empty($request['url'])) {
        if (!filter_var($request['url'], FILTER_VALIDATE_URL)) {
            $errors[] = '「ホームページ」は正しい形式で入力してください。';
        }
    }

    // 11. 性別 (必須) isset を使用する
    if (!isset($request['gender'])) {
        $errors[] = '「性別」は必須です。';
    }

    // 12. 年齢 (必須) age の value属性には '0'は設定していない。6より大きい値は検証にひっかかるようにする。
    if (empty($request['age']) || 6 < $request['age']) {
        $errors[] = '「年齢」は必須です。';
    }

    // 13. お問い合わせ内容 (必須/200文字以内)
    if (empty($request['contact']) || 200 < mb_strlen($request['contact'])) {
        $errors[] = '「お問い合わせ内容」は必須です。200文字以内で入力してください。';
    }

    // 14. 注意事項 (必須)
    if (empty($request['caution'])) {
        $errors[] = '「注意事項」をご確認ください。';
    }

    return $errors; // 4. validation関数の呼び出し元に返す
}
```

以上、PHP バリデーション設定の学習の振り返りでした。