[PHP]バリデーションをかけたフォームのコードを読んでいく

##はじめに

この記事は、バリデーションについて知識を定着させるためにコードを読んでいく記事です。
個人的なアウトプットになります。

####バリデーション
フォームでバリデーションをかけることで、文字数を制限したり、アドレスを一つしか入力できないようにします。

##コードを読む
今回は、

- 名前
- メールアドレス
- ホームページ
- 性別
- 年齢
- お問い合わせ内容
- 注意事項

に、
バリデーションがかかったコードを読んでいきます。

sample.phpとvalidation.phpの二つがありますが、validation.phpにバリデーションを記述してまとめています。
コード内のコメントで読んでいきます。

間違いがありましたら、ご指摘いただけると幸いです。


```sample.php
<?php

session_start();

//requireで外部ファイルであるvalidation.phpを読み込んでいます。
require 'validation.php';

header("X-FRAME-OPTIONS: DENY");

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

$page_flag = 0;
$error = validation($_POST);
//validation.phpのvalidation関数内の$errorはローカル変数なので使えないので、ここでも$errorを定義している。

//btn_confirmが空じゃ無くて、かつ$errorがからの場合処理
if (!empty($_POST['button_confirm']) && empty($error)) {
  $page_flag = 1;
}

if (!empty($_POST['button_submit'])) {
  $page_flag = 2;
}
?>

<!DOCTYPE html>
<meta charset="UTF-8">
<head></head>
<body>


<?php if($page_flag === 1): ?>
<?php if ($_POST['csrf'] === $_SESSION['csrf_token']): ?>

<form method="POST" action="input.php">
名前
<?php echo h($_POST['your_name']); ?>
<br>
メールアドレス
<?php echo h($_POST['email']); ?>
<br>
ホームページ
<?php echo h($_POST['url']); ?>
<br>
性別
<?php 
if ($_POST['gender'] === '0') {
  echo '男性';
}
if ($_POST['gender'] === '1') {
  echo '女性';
}
?>
<br>
年齢
<?php
if ($_POST['age'] === '1') {
  echo '~19';
} elseif ($_POST['age'] === '2') {
  echo '20~29';
} elseif ($_POST['age'] === '3') {
  echo '30~39';
} elseif ($_POST['age'] === '4') {
  echo '40~49';
} elseif ($_POST['age'] === '5') {
  echo '50~';
} 
?>
<br>
お問い合わせ内容
<?php echo h($_POST['contact']); ?>
<br>

<input type="submit" name="back" value="戻る">
<input type="submit" name="button_submit" value="送信する">
<input type="hidden" name="your_name" value="<?php echo h($_POST['your_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 endif; ?>
<?php endif; ?>

<?php if($page_flag === 2): ?>
<?php if ($_POST['csrf'] === $_SESSION['csrf_token']): ?>

送信が完了しました。

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


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

<!-- buttonn_confirmが押されていて'$error'にエラー文が入っていたら処理してエラー分を表示する。'!empty($error)'単体だと最初に画面表示されているときにもエラー分が出てしまうので注意。 -->
<?php if (!empty($_POST['button_confirm']) && !empty($error)): ?>
<ul>
<?php foreach ($error as $value): ?>
<li><?php echo $value; ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>


<form method="POST" action="sample.php">
名前
<input type="text" name="your_name" value="<?php echo h($_POST['your_name']); ?>"></input>
<br>
メールアドレス
<input type="email"  name="email" value="<?php echo h($_POST['email']); ?>"></input>
<br>
ホームページ
<input type="url" name="url" value="<?php echo h($_POST['url']); ?>">
<br>
性別
<input type="radio" name="gender" value="0">男性
<input type="radio" name="gender" value="1">女性
<br>
年齢
<select name="age">
  <option value="">選択してください</option>
  <option value="1">~19</option>
  <option value="2">20~29</option>
  <option value="3">30~39</option>
  <option value="4">40~49</option>
  <option value="5">50~</option>
</select>
<br>
お問い合わせ内容
<textarea name="contact"></textarea>
<br>

<input type="checkbox" name="caution" value="1">注意事項にチェックする
<br>

<input type="submit" name="button_confirm" value="確認する">
<input type="hidden" name="csrf" value="<?php echo h($token); ?>">
</form>

<?php endif; ?>
```

```validation.php
<?php

function validation($data) {
  //空の配列を生成
  $error = [];
//名前が未入力、または15文字以上の場合、$errorの配列にエラー分を追加している。
  if (empty($data['your_name']) || 15 < mb_strlen($data['your_name'])) {
    $error[] = '氏名は、15字以内で入力してください。';
  }
//!filter_varで指定したフィルタでデータをフィルタリングしています。今回は、フィルタにFILTER_VALIDATE_EMAILを指定しているのでメールアドレスかどうかフィルタリングしています。
  if (empty($data['email']) || !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
    $error[] = 'メールアドレスは、正しい形式で入力してください。';
  }
//URLは空でもOKなため、まず!emptyで空でなかったら処理をするようにする。その処理の中に!filter_varにFILTER_VALIDATE_URLのフィルタを設定して、正しいURLの形式かフィルタリングしている。
  if (!empty($data['url'])) {
    if (!filter_var($data['url'], FILTER_VALIDATE_URL)) {
      $error[] = 'URLは、正しい形式で入力してください。';
    } 
  }

//emptyだと0でも通ってしまうのでissetを使う。今回の場合は、セットされていなければ処理。
  if (!isset($data['gender'])) {
    $error[] = '性別は必ず入力してください。';
  }
//年齢の項目が未入力、またはvalueで送られてきた値が5以上の場合、エラー文を追加している。
  if (empty($data['age']) || 5 < $data['age']) {
    $error[] = '年齢は必ず入力してください。';
  }
//お問い合わせ内容が未入力または、300文字以上の場合、エラー文を追加している。
  if (empty($data['contact']) || 300 < mb_strlen($data['contact'])) {
    $error[] = 'お問い合わせ内容は、300字以内で入力してください。';
  }
//注意事項の項目をチェックするとvalueで'1'が送られてくる。つまり'1'以外が送られてきた場合エラー文を追加する。
  if ($data['caution'] !== '1') {
    $error[] = '注意事項をご確認ください。';
  }

  return $error;

}

?>
```

##終わりに

この記事を読んでいただきありがとうございました。