フォルダ構成
├── contact
│   ├── contact.php
│   └── style.css
└── libs
    ├── .htaccesss
    ├── functions.php
    ├── recaptchavars.php
    └── mailvars.php
contact.php
<?php 
//エスケープ処理やデータチェックを行う関数のファイルの読み込み
require '../libs/functions.php';

//reCAPTCHA 
require '../libs/recaptchavars.php';
// reCAPTCHA サイトキー
$siteKey = V3_SITEKEY;
// reCAPTCHA シークレットキー
$secretKey = V3_SECRETKEY;

//POSTされたデータを変数に格納
$name = isset( $_POST[ 'name' ] ) ? $_POST[ 'name' ] : NULL;
$email = isset( $_POST[ 'email' ] ) ? $_POST[ 'email' ] : NULL;
$subject = isset( $_POST[ 'subject' ] ) ? $_POST[ 'subject' ] : NULL;
$body = isset( $_POST[ 'body' ] ) ? $_POST[ 'body' ] : NULL;

//reCAPTCHA トークン
$token = isset( $_POST[ 'g-recaptcha-response' ] ) ? $_POST[ 'g-recaptcha-response' ] : NULL;
//reCAPTCHA アクション名 
$action = isset( $_POST[ 'action' ] ) ? $_POST[ 'action' ] : NULL;

//POSTされたデータを整形(前後にあるホワイトスペースを削除)
$name = trim( $name );
$email = trim( $email );
$subject = trim( $subject );
$body = trim( $body );

//reCAPTCHA の検証を通過したかどうかの真偽値
$result_status = false;

//reCAPTCHA トークンが設定されていれば以下を実行
if (isset($_POST['g-recaptcha-response'])) {

  //POSTされたデータをチェック  
  $_POST = checkInput( $_POST ); 
  
  // トークンとアクション名が取得できれば
  if ( $token && $action) {

    //cURL セッションを初期化(API のレスポンスの取得)
    $ch = curl_init();
    // curl_setopt() により転送時のオプションを設定
    //URL の指定
    curl_setopt($ch, CURLOPT_URL,"https://www.google.com/recaptcha/api/siteverify");
    //HTTP POST メソッドを使う
    curl_setopt($ch, CURLOPT_POST, true );
    //API パラメータの指定
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array(
      'secret' => $secretKey, 
      'response' => $token
    )));
    //curl_execの返り値を文字列にする
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    //転送を実行してレスポンスを $api_response に格納
    $api_response = curl_exec($ch);
    //セッションを終了
    curl_close($ch);

    //レスポンスの $json(JSON形式)をデコード
    $rc_result = json_decode( $api_response );

    //判定
    if ( $rc_result->success && $rc_result->action === $action && $rc_result->score >= 0.5) {
      //success が true でアクション名が一致し、スコアが 0.5 以上の場合は合格
      $result_status = true;
    } else {
      // 上記以外の場合は 不合格
      $result_status = false;
      // $result_status が false の場合、エラーの配列に追加(78行目)することでメールを送信させない(111行目)
    }
  }
  
  //エラーメッセージを保存する配列の初期化
  $error = array();
  
  //reCAPTCHA 検証
  if(!$result_status) {
    $error['recaptcha'] = 'reCAPTCHA 検証が失敗しました。';
  }
  
  //値の検証
  if ( $name == '' ) {
    $error['name'] = '*お名前は必須項目です。';
    //制御文字でないことと文字数をチェック
  } else if ( preg_match( '/\A[[:^cntrl:]]{1,30}\z/u', $name ) == 0 ) {
    $error['name'] = '*お名前は30文字以内でお願いします。';
  }
  if ( $email == '' ) {
    $error['email'] = '*メールアドレスは必須です。';
  } else { //メールアドレスを正規表現でチェック
    $pattern = '/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/uiD';
    if ( !preg_match( $pattern, $email ) ) {
      $error['email'] = '*メールアドレスの形式が正しくありません。';
    }
  }
  if ( $subject == '' ) {
    $error['subject'] = '*件名は必須項目です。';
    //制御文字でないことと文字数をチェック
  } else if ( preg_match( '/\A[[:^cntrl:]]{1,100}\z/u', $subject ) == 0 ) {
    $error['subject'] = '*件名は100文字以内でお願いします。';
  }
  if ( $body == '' ) {
    $error['body'] = '*内容は必須項目です。';
    //制御文字(タブ、復帰、改行を除く)でないことと文字数をチェック
  } else if ( preg_match( '/\A[\r\n\t[:^cntrl:]]{1,1050}\z/u', $body ) == 0 ) {
    $error['body'] = '*内容は1000文字以内でお願いします。';
  }
  
  //エラーがなく且つ POST でのリクエストの場合
  if (empty($error) && $_SERVER['REQUEST_METHOD']==='POST') {
    //メールアドレス等を記述したファイルの読み込み
    require '../libs/mailvars.php'; 

    //メール本文の組み立て
    $mail_body = 'コンタクトページからのお問い合わせ' . "\n\n";
    $mail_body .=  "お名前: " .h($name) . "\n";
    $mail_body .=  "Email: " . h($email) . "\n"  ;
    $mail_body .=  "<お問い合わせ内容>" . "\n" . h($body);

    //--------sendmail------------

    //メールの宛先(名前<メールアドレス> の形式)。値は mailvars.php に記載
    $mailTo = mb_encode_mimeheader(MAIL_TO_NAME) ."<" . MAIL_TO. ">";

    //Return-Pathに指定するメールアドレス
    $returnMail = MAIL_RETURN_PATH; //
    //mbstringの日本語設定
    mb_language( 'ja' );
    mb_internal_encoding( 'UTF-8' );

    // 送信者情報(From ヘッダー)の設定
    $header = "From: " . mb_encode_mimeheader($name) ."<" . $email. ">\n";
    $header .= "Cc: " . mb_encode_mimeheader(MAIL_CC_NAME) ."<" . MAIL_CC.">\n";
    $header .= "Bcc: <" . MAIL_BCC.">";

    //メールの送信
    //メールの送信結果を変数に代入 
    if ( ini_get( 'safe_mode' ) ) {
      //セーフモードがOnの場合は第5引数が使えない
      $result = mb_send_mail( $mailTo, $subject, $mail_body, $header );
    } else {
      $result = mb_send_mail( $mailTo, $subject, $mail_body, $header, '-f' . $returnMail );
    }
    
    //メール送信の結果判定
    if ( $result ) {
      $_POST = array(); //空の配列を代入し、すべてのPOST変数を消去
      //変数の値も初期化
      $name = '';
      $email = '';
      $subject = '';
      $body = '';
      
      //再読み込みによる二重送信の防止
      $params = '?result='. $result;
      //reCAPTCHA 検証結果確認用パラメータ
      $params .= '&success=' . $rc_result->success  .'&action=' . $rc_result->action .'&score=' . $rc_result->score;
      $url = (empty($_SERVER['HTTPS']) ? 'http://' : 'https://').$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']; 
      header('Location:' . $url . $params);
      exit;
    } 
  }
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>コンタクトフォーム</title>
<link href="../bootstrap.min.css" rel="stylesheet">
<link href="style.css" rel="stylesheet">
</head>
<body>
<div class="container">
  <!-- ここから reCAPTCHA 検証結果確認用(テスト用)  -->
  <div style="color:blue; margin: 30px 0;">
    <!-- reCAPTCHA 検証を通過した場合  -->
    <?php if (isset($_GET['success'])) echo 'success: '.$_GET['success'].'<br>' ?>
    <?php if (isset($_GET['action'])) echo 'action: '.$_GET['action'].'<br>' ?>
    <?php if (isset($_GET['score'])) echo 'score: '.$_GET['score'].'<br>' ?>
  </div>
  <!-- ここまで reCAPTCHA 検証結果確認用 -->
  <h2 class="">お問い合わせフォーム</h2>
  <?php  if ( isset($_GET['result']) && $_GET['result'] ) : // 送信が成功した場合?>
  <h4>送信完了!</h4>
  <p>送信完了いたしました。</p>
  <hr>
  <?php elseif (isset($result) && !$result ): // 送信が失敗した場合 ?>
  <h4>送信失敗</h4>
  <p>申し訳ございませんが、送信に失敗しました。</p>
  <p>しばらくしてもう一度お試しになるか、メールにてご連絡ください。</p>
  <p>メール:<a href="mailto:info@example.com">Contact</a></p>
  <hr>
  <?php endif; ?>
  <p>以下のフォームからお問い合わせください。</p>
  <form id="form" method="post">
    <div class="form-group">
      <label for="name">お名前(必須) 
        <span class="error"><?php if ( isset( $error['name'] ) ) echo h( $error['name'] ); ?></span>
      </label>
      <input type="text" class="form-control" id="name" name="name" placeholder="氏名" value="<?php echo h($name); ?>">
    </div>
    <div class="form-group">
      <label for="email">Email(必須) 
        <span class="error"><?php if ( isset( $error['email'] ) ) echo h( $error['email'] ); ?></span>
      </label>
      <input type="text" class="form-control" id="email" name="email" placeholder="Email アドレス" value="<?php echo h($email); ?>">
    </div>
    <div class="form-group">
      <label for="subject">件名(必須) 
        <span class="error"><?php if ( isset( $error['subject'] ) ) echo h( $error['subject'] ); ?></span> 
      </label>
      <input type="text" class="form-control" id="subject" name="subject" placeholder="件名" value="<?php echo h($subject); ?>">
    </div>
    <div class="form-group">
      <label for="body">お問い合わせ内容(必須) 
        <span class="error"><?php if ( isset( $error['body'] ) ) echo h( $error['body'] ); ?></span>
      </label>
      <textarea class="form-control" id="body" name="body" placeholder="お問い合わせ内容(1000文字まで)をお書きください" rows="3"><?php echo h($body); ?></textarea>
    </div>
    <button name="submitted" type="submit" class="btn btn-primary">送信</button>
  </form>
</div>
<script src="https://www.google.com/recaptcha/api.js?render=<?php echo $siteKey; ?>"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script><!-- jQuery の読み込み -->  
<script>
jQuery(function($){
  $("form#form").submit(function(event){
    var that = $(this);
    event.preventDefault();
    var action_name = 'contact'; //アクション名 
    grecaptcha.ready(function() {
      grecaptcha.execute('<?php echo $siteKey; ?>', { action: action_name }).then(function(token) {
        //input 要素を生成して値にトークンを設定
        that.prepend('<input type="hidden" name="g-recaptcha-response" value="' + token + '">');
        //input 要素を生成して値にアクション名を設定
        that.prepend('<input type="hidden" name="action" value="' + action_name + '">');
        //unbind で一度 submit のイベントハンドラを削除してから submit() を実行
        that.unbind('submit').submit(); 
      });;
    });
  });
})
</script>
</body>
</html>
functions.php
<?php
//エスケープ処理を行う関数
function h($var) {
  if(is_array($var)){
    //$varが配列の場合、h()関数をそれぞれの要素について呼び出す(再帰)
    return array_map('h', $var);
  }else{
    return htmlspecialchars($var, ENT_QUOTES, 'UTF-8');
  }
}

//入力値に不正なデータがないかなどをチェックする関数
function checkInput($var){
  if(is_array($var)){
    return array_map('checkInput', $var);
  }else{
    //NULLバイト攻撃対策
    if(preg_match('/\0/', $var)){  
      die('不正な入力です。');
    }
    //文字エンコードのチェック
    if(!mb_check_encoding($var, 'UTF-8')){ 
      die('不正な入力です。');
    }
    //改行、タブ以外の制御文字のチェック
    if(preg_match('/\A[\r\n\t[:^cntrl:]]*\z/u', $var) === 0){  
      die('不正な入力です。制御文字は使用できません。');
    }
    return $var;
  }
}
mailvars.php
<?php
//メールの宛先(To)のメールアドレス
define('MAIL_TO', "xxxx@xxxxx.com");
//メールの宛先(To)の名前  
define('MAIL_TO_NAME', "xxx xxx xxx");
//Cc の名前
define('MAIL_CC', 'xxxx@xxxxxx.com');
//Cc の名前
define('MAIL_CC_NAME', 'xxxxxx');
//Bcc
define('MAIL_BCC', 'xxxxx@xxxxx.com');
//Return-Pathに指定するメールアドレス
define('MAIL_RETURN_PATH', 'xxxxx@xxxxx.com');
//自動返信の返信先名前
define('AUTO_REPLY_NAME', 'xxxx xxxx xxxx');
recaptchavars.php
<?php
// reCAPTCHA v3 サイトキー
define('V3_SITEKEY', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
// reCAPTCHA v3 シークレットキー
define('V3_SECRETKEY', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx');
.htaccess
deny from all
style.css
@charset "utf-8";
.container {
  width: 100%;
  max-width: 760px;
  margin: 50px auto;
}
/* input 要素 */
#name, #email, #subject, #email_check, #tel {
  max-width:400px;
}
#body {
  max-width: 640px;
}
/* エラー表示 */
p.error, span.error {
  color: red;
}
/* フォーム要素(Bootstrap4 のスタイルを上書き) */
.form-control {
  border-radius: 0px;
  background-color: #fdfdfd;
  font-size: 14px;
}
.form-control:focus {
  border-color: #aadbe8;
  outline: 0;
  -webkit-box-shadow: inset 0 0px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.4);
  box-shadow: inset 0 0px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.4);
  background-color:#fff;
}
/* Google Chrome, Safari, Opera 15+, Android, iOS */
::-webkit-input-placeholder {
  font-size: 13px; 
}
/* Firefox 18- */
:-moz-placeholder {
  font-size: 13px; }
/* Firefox 19+ */
::-moz-placeholder {
  font-size: 13px; }
/* IE 10+ */
:-ms-input-placeholder {
  font-size: 13px; }
::placeholder{ 
  font-size: 13px;
}
textarea.form-control {
  height: 200px;
}
#send {
  margin-top: 30px;
}
/* この他に Bootstrap4 のCSSも読み込む */