【PHP】③お問い合わせフォーム バリデーション処理

こんなツイートをしました。今回はもう少し詳しくバリデーションについて説明します。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">PHPのバリデーションとは<br><br>フォームに入力されたデータはサーバーに送信されます。そこでPHP等の言語を使ってデータを処理することになる、送信データが正しい形式のデータであるかどうかや想定した文字数の範囲か、適切なデータ型なのかをチェックするのがバリデーション機能です。<a href="https://twitter.com/hashtag/php?src=hash&amp;ref_src=twsrc%5Etfw">#php</a></p>&mdash; Tomoya (@div_tomo) <a href="https://twitter.com/div_tomo/status/1693281825500901776?ref_src=twsrc%5Etfw">August 20, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

この記事では、バリデーション処理の記事を作成しています。

#### お問い合わせフォームの実装についての記事
【PHP】①お問い合わせフォーム 実装

https://qiita.com/div_tomo/private/f3603b8775a9e2fa72b8

#### セキュリティ対策についての記事
【PHP】②お問い合わせフォーム セキュリティ対策

https://qiita.com/div_tomo/items/0d75a8f02a3ebeb45062

※[【PHP】①お問い合わせフォーム 実装 ] と [【PHP】②お問い合わせフォーム セキュリティ対策] も同時に実施すると理解度が深まります。

# 手順

## 入力画面にお問い合わせ内容を追加

input.phpファイルの入力画面に「お問い合わせ内容を追加します。」

`<!-- 追加 -->`と記載されているお問い合わせ内容のコードをコピペで問題ないので貼り付けておきましょう。

> form ＞ input.php
> 

```php
<!-- 入力画面 -->
    <?php if($pageFlag === 0) : ?>

        <?php
        if(!isset($_SESSION['csrfToken']))
        {
            $csrfToken = bin2hex(random_bytes(32)); 
            $_SESSION['csrfToken'] = $csrfToken;
        }
        $token = $_SESSION['csrfToken'];
        ?>

        <form method="POST" action="input.php">

            氏名<br>
            <input type="text" name="name" value="<?php if(!empty($_POST['name'])){ echo hsc($_POST['name']) ; } ?>"><br>
            メールアドレス<br>
            <input type="email" name="email" value="<?php if(!empty($_POST['email'])){ echo hsc($_POST['email']) ; } ?>"><br>
            <!-- 追加 -->
            お問い合わせ内容<br>
            <textarea name="contact">
                <?php if(!empty($_POST['contact'])){ echo hsc($_POST['contact']) ; } ?>
            </textarea><br>

            <input type="submit" name="btn_confirm" value="確認する">

            <input type="hidden" name="csrf" value="<?php echo $token; ?>">

        </form>

    <?php endif; ?>
```

### 説明

お問い合わせ内容は一行では無い可能性があるので textareaタグ を使って複数行入力できるように設定しています。

textareaタグ の場合バリューという値が無いので、タグの中に書いた値「`<textarea name="contact">ここがバリュー</textarea>`」がバリューになります。

お問い合わせ内容にも戻るボタンを押した時に値を持たせたままにしたいので `<?php if(!empty($_POST['contact'])){ echo hsc($_POST['contact']) ; } ?>` を書いてあげます。

```php
お問い合わせ内容<br>
<textarea name="contact">
    <?php if(!empty($_POST['contact'])){ echo hsc($_POST['contact']) ; } ?>
</textarea><br>
```

## 確認画面にお問い合わせ内容を追加

次にinput.phpファイルの確認画面にも「お問い合わせ内容を追加します。」

`<!-- 追加 -->`と記載されているお問い合わせ内容のコードをコピペで問題ないので貼り付けておきましょう。

> form ＞ input.php
> 

```php
<!-- 確認画面 -->
    <?php if($pageFlag === 1) : ?>
        <?php if($_POST['csrf'] === $_SESSION['csrfToken']) : ?>
            <form method="POST" action="input.php">

                氏名
                <?php echo hsc($_POST['name']) ;?>
                <br>
                メールアドレス
                <?php echo hsc($_POST['email']) ;?>
                <br>
                <!-- 追加 -->
                お問い合わせ内容
                <?php echo hsc($_POST['contact']) ;?>
                <br>

                <input type="submit" name="back" value="戻る">
                <input type="submit" name="btn_submit" value="送信する">

                <input type="hidden" name="name" value="<?php echo hsc($_POST['name']) ;?>">
                <input type="hidden" name="email" value="<?php echo hsc($_POST['email']) ;?>">
                <input type="hidden" name="contact" value="<?php echo hsc($_POST['contact']) ;?>"> <!-- 追加 -->
                <input type="hidden" name="csrf" value="<?php echo hsc($_POST['csrf']) ;?>">

            </form>
        <?php endif; ?>
    <?php endif; ?>
```

### 説明

`<?php echo hsc($_POST['contact']) ;?>` で入力された値を表示

```php
お問い合わせ内容
<?php echo hsc($_POST['contact']) ;?>
<br>
```

戻るボタンを押しても値を残しておけるような処理を記載しています。

```php
<input type="hidden" name="contact" value="<?php echo hsc($_POST['contact']) ;?>">
```

## バリデーション処理

今回は入力画面にエラーメッセージを表示する方法でバリデーションの実装をしていきます。

バリデーションのコードが少し長くなるのでバリデーションのファイルを分けて作成していくことにします。

### バリデーションフォルダ作成

まず**formフォルダ**の中に**validation.phpファイル**を追加で作ってあげます。

```php
from
 |
 |----> input.php
        validation.php //追加
```

### バリデーション処理を書く

関数を作ってバリデーションをかけていく処理を記載していきます。

> from ＞ validation.php
> 

```php
<?php

    function validation($request) //$requestには$_POSTの連想配列が入る
    {

        $errors = []; //ローカル変数なのでinput.phpにも$errorsを記載してあげる必要がある

        if(empty($request['name']) || 20 < mb_strlen($request['name'])) //nameが空だったら$errorsにエラー文の値を追加してあげる
        {
            $errors[] = '氏名は必須です。[20文字いないで入力してください]';
        }

        if(empty($request['email']) || !filter_var($request['email'], FILTER_VALIDATE_EMAIL)) //一つ目の引数がフィルターをかけたい値　//二つ目の引数でメールアドレスかどうか
        {
            $errors[] = 'メールアドレスは必須です。正しい形式で入力してください。';
        }

        if(empty($request['contact'])) 
        {
            $errors[] = 'お問い合わせ内容は必須です。';
        }

        return $errors; //$errorsを返す
    }

?>
```

### 説明

```php
function validation($request) //$requestには$_POSTの連想配列が入る
```

まず `function` で `validation()` 関数を作ります。

引数の `$request` には `$_POST` の値が入ります。

```php
$errors = [];
```

エラー内容をまとめて保管するために `$error = []` という配列を作っています。

```php
if(empty($request['name'] || || 20 < mb_strlen($request['name'])) //nameが空だったら$errorsにエラー文の値を追加してあげる
{
     $errors[] = '氏名は必須です。[20文字いないで入力してください]';
}
```

`name` が空だったら `$errors` に`'氏名は必須です。[20文字いないで入力してください]'` という値を追加する処理

もしくは

`name` が２０文字より多かったら`$errors` に`'氏名は必須です。[20文字いないで入力してください]'` という値を追加する処理

```php
if(empty($request['email']) || !filter_var($request['email'], FILTER_VALIDATE_EMAIL)) //filter_varの一つ目の引数がフィルターをかけたい値　//二つ目の引数でメールアドレスかどうか
        {
            $errors[] = 'メールアドレスは必須です。正しい形式で入力してください。'
        }
```

`email` が空だったら `$errors` に`'メールアドレスは必須です。正しい形式で入力してください。'` という値を追加する処理

もしくは

`email` が正しい形式じゃなかったら `$errors` に`'メールアドレスは必須です。正しい形式で入力してください。'` という値を追加する処理

```php
if(empty($request['contact'])) //nameが空だったら$errorsにエラー文の値を追加してあげる
{
      $errors[] = 'お問い合わせ内容は必須です。';
}

return $errors;
```

`contact` が空だったら `$errors` に`お問い合わせ内容は必須です。` という値を追加する処理

`return` で `$errors` を返してあげます。

### input.php で関数を使用する

▼長いので`Ctrl+F`で「**追加**」と検索して調べると追加された箇所を確認できます

> from > input
> 

```php
<?php

session_start();

require 'validation.php'; //追加

$errors = validation($_POST); //追加

$pageFlag = 0;
// 入力画面の値が空では無かったら$pageFlagを１に変更
if(!empty($_POST['btn_confirm']) && empty($errors)){ // 追加でエラーが空だったら処理を実行
    $pageFlag = 1;
}
// 送信画面の値が空では無かったら$pageFlagを２に変更
if(!empty($_POST['btn_submit'])){
    $pageFlag = 2;
}

function hsc($str)
{
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

?>

<!DOCTYPE html>
<meta charset="utf-8">
<head></head>
<body>
    
    <!-- 入力画面 -->
    <?php if($pageFlag === 0) : ?>

        <?php
        if(!isset($_SESSION['csrfToken']))
        {
            $csrfToken = bin2hex(random_bytes(32)); 
            $_SESSION['csrfToken'] = $csrfToken;
        }
        $token = $_SESSION['csrfToken'];
        ?>

        <form method="POST" action="input.php">

            <!-- エラーがあったら処理を実行
            初めて入力画面を表示する時にも値が空なのでエラーを表示してしまうので
            追加で確認ボタンが押されたらという条件も追加 -->
            <?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; ?>

            氏名<br>
            <input type="text" name="name" value="<?php if(!empty($_POST['name'])){ echo hsc($_POST['name']) ; } ?>"><br>
            メールアドレス<br>
            <input type="email" name="email" value="<?php if(!empty($_POST['email'])){ echo hsc($_POST['email']) ; } ?>"><br>
            お問い合わせ内容<br>
            <textarea name="contact"><?php if(!empty($_POST['contact'])){ echo hsc($_POST['contact']) ; } ?></textarea><br>

            <input type="submit" name="btn_confirm" value="確認する">

            <input type="hidden" name="csrf" value="<?php echo $token; ?>">

        </form>

    <?php endif; ?>
    
    <!-- 確認画面 -->
    <?php if($pageFlag === 1) : ?>
        <?php if($_POST['csrf'] === $_SESSION['csrfToken']) : ?>
            <form method="POST" action="input.php">

                氏名
                <?php echo hsc($_POST['name']) ;?>
                <br>
                メールアドレス
                <?php echo hsc($_POST['email']) ;?>
                <br>
                お問い合わせ内容
                <?php echo hsc($_POST['contact']) ;?>
                <br>

                <input type="submit" name="back" value="戻る">
                <input type="submit" name="btn_submit" value="送信する">

                <input type="hidden" name="name" value="<?php echo hsc($_POST['name']) ;?>">
                <input type="hidden" name="email" value="<?php echo hsc($_POST['email']) ;?>">
                <input type="hidden" name="contact" value="<?php echo hsc($_POST['contact']) ;?>">
                <input type="hidden" name="csrf" value="<?php echo hsc($_POST['csrf']) ;?>">

            </form>
        <?php endif; ?>
    <?php endif; ?>
    
    <!-- 完了画面 -->
    <?php if($pageFlag === 2) : ?>
        <?php if($_POST['csrf'] === $_SESSION['csrfToken']) : ?>
            <p>送信が完了しました。</p>

            <?php unset($_SESSION['csrfToken']); ?>
        <?php endif; ?>
    <?php endif; ?>

</body>
</html>
```

### 説明

```php
<?php

session_start();

require 'validation.php'; //追加

$errors = validation($_POST); //追加
```

`session_start();` の下に  `require` で `validation.php` を追加して関数を使用できるようにする

`$errors = validation($_POST);` ここファイルに記載した関数を使用して `$errors` に格納

```php
if(!empty($_POST['btn_confirm']) && empty($errors)){
```

`&& empty($errors)` エラーが空だったら処理を実行という処理を追加で記載

```php
<!-- エラーがあったら処理を実行
初めて入力画面を表示する時にも値が空なのでエラーを表示してしまうので
追加で確認ボタンが押されたらという条件も追加 -->
<?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; ?>
```

`!empty($errors)` エラーがあったら

かつ

`!empty($_POST['btn_confirm']))` 確認ボタンが押されたら実行

```php
foreach($errors as $error)
{
      echo '<li>' . $error . '</li>' ;
 }
```

foreach で配列を１つ１つ取り出している