PHPで作るメールフォーム

# メールフォーム作成

以下のサイト様からソースコードを一部拝借しています。

- **[webに生きる人+α - PHPのmb_send_mail()を使った時にgmailだけメールが届かない](http://www.web4life.jp/archives/108)**
- **[Allied ArchitectsEngineer BlogRSS - PHPでJIS以外の文字を文字化けせずに日本語メールを送る方法](http://tech.aainc.co.jp/archives/3570)**
- **[VeryPosi - PHP応用 コンタクトフォームの作成 入力チェックの追加](http://blog.veryposi.info/programing/php/php-appli-contactform-2/)**

3番目のエントリのPHPロジック部分は全然ダメなのですが、サッと作れるデザインとしてはなかなか気に入ったので…ｗ

## ソースコード

sendmailに渡す **`-f`** (**Return-Path**) は適切に指定してください。

```php
<?php
 
/**
 * HTML特殊文字をエスケープする関数
 */
function h($str) {
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
 
/**
 * RuntimeExceptionを生成する関数
 * http://qiita.com/mpyw/items/6bd99ff62571c02feaa1
 */
function e($msg, Exception &$previous = null) {
    return new RuntimeException($msg, 0, $previous);
}
 
/**
 * 例外スタックを配列に変換する関数
 * http://qiita.com/mpyw/items/6bd99ff62571c02feaa1
 */
function exception_to_array(Exception $e) {
    do {
        $msgs[] = $e->getMessage();
    } while ($e = $e->getPrevious());
    return array_reverse($msgs);
}

/* 変数の初期化 */
foreach (array('name', 'email', 'contents', 'token', 'confirm', 'execute') as $v) {
    $$v = trim(filter_input(INPUT_POST, $v));
}

/* セッションの初期化 */
session_name('ContactForm');
@session_start();
if (!isset($_SESSION['token'])) {
    $_SESSION['token'] = array();
}

/* 「確認」か「送信」のときのみ実行 */
if ($confirm or $execute) {
    try {
        // トークンをチェック
        if (!isset($_SESSION['token'][$token])) {
            throw e('フォームの有効期限が切れています。', $e);
        }
        // トークンを消費させる
        unset($_SESSION['token'][$token]);
        // 各項目チェック
        if ($name === '') {
            $e = e('名前を入力してください。', $e);
        }
        if ($email === '') {
            $e = e('メールアドレスを入力してください。', $e);
        } elseif (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
            $e = e('メールアドレスが不正です。', $e);
        }
        // 例外がここまでに1つでも発生していればスローする
        if (!empty($e)) {
            throw $e;
        }
        // 「送信」のとき
        if (
            $execute and
            !mb_internal_encoding('utf-8') ||
            !mail(
                'info@example.com',
                mb_encode_mimeheader('フォームからの送信', 'ISO-2022-JP-MS'),
                mb_convert_encoding($contents, 'ISO-2022-JP-MS'),
                implode("\r\n", array(
                    'Content-Type: text/plain; charset=ISO-2022-JP',
                    'From: ' . mb_encode_mimeheader($name, 'ISO-2022-JP-MS')
                             . ' <' . $email . '>',
                )),
                '-f info@example.com'
            )
        ) {
            throw e('メール送信でエラーが発生しました。', $e);
        }
    } catch (Exception $e) {
        // 最初の画面に戻す
        $confirm = $execute = '';
    }
}

/* 「最初」か「確認」のときのみ実行 */
if (!$execute) {
    // 値をダミーにしてトークンをキー部分に生成(最大10個まで保持)
    $_SESSION['token'] = array_slice(
        array($token = sha1(mt_rand()) => true) + $_SESSION['token'],
        0,
        10
    );
}
 
// ヘッダー送信
header('Content-Type: application/xhtml+xml; charset=utf-8');
 
?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja">
  <head>
    <title>Contact Form</title>
    <style type="text/css"><![CDATA[
      h1 {
        color: red;
        font-size: 18pt;
      }
      #contact-form th {
        background-color: #9bdbea;
        padding: 10px 20px;
      }
      #contact-form td {
        background-color: #f7f7ef;
        padding: 10px 20px;
      }
      #contact-form td input {
        width: 400px;
      }
      #contact-form td textarea {
        width: 400px;
      }
      form {
        display: inline;
      }
      #errmsg {
        background-color:#E7D3D6;
        border:3px solid #A55952;
        color:#944121;
        font-size:12px;
        margin:10px;
        padding:10px;
        text-align:left;
        width:400px;
      }
    ]]></style>
  </head>
  <body>
    <h1>コンタクトフォーム</h1>
<?php if (!empty($e)): ?>
    <div id="errmsg">
      <ul>
<?php foreach (exception_to_array($e) as $msg): ?>
        <li><?=$msg?></li>
<?php endforeach; ?>
      </ul>
    </div>
<?php endif; ?>
<?php if ($execute): ?>
    <p>送信しました。ご意見ありがとうございました。<br /><a href="">戻る</a></p>
<?php elseif ($confirm): ?>
    <p>この内容で送信しますか？</p>
    <p>
      <form action="" method="post">
        <table id="contact-form" border="1" cellpadding="0" cellspacing="0">
          <tr>
            <th>名前(必須)</th>
            <td><?=h($name)?></td>
          </tr>
          <tr>
            <th>メールアドレス(必須)</th>
            <td><?=h($email)?></td>
          </tr>
          <tr>
            <th>内容</th>
            <td><pre><?=
              $contents !== '' ?
              h($contents) :
              '<span style="color:red;">【未入力】</span>'
            ?></pre></td>
          </tr>
          <tr>
            <th colspan="2">
              <input type="hidden" name="name" value="<?=h($name)?>" />
              <input type="hidden" name="email" value="<?=h($email)?>" />
              <input type="hidden" name="contents" value="<?=h($contents)?>" />
              <input type="hidden" name="token" value="<?=h($token)?>" />
              <input type="submit" name="execute" value="送信" />
              <input type="submit" value="修正" />
            </th>
          </tr>
        </table>
      </form>
    </p>
<?php else: ?>
    <p>
      <form action="" method="post">
        <table id="contact-form" border="1" cellpadding="0" cellspacing="0">
          <tr>
            <th>名前(必須)</th>
            <td><input type="text" name="name" value="<?=h($name)?>" /></td>
          </tr>
          <tr>
            <th>メールアドレス(必須)</th>
            <td><input type="text" name="email" value="<?=h($email)?>" /></td>
          </tr>
          <tr>
            <th>内容</th>
            <td><textarea name="contents" rows="10"><?=h($contents)?></textarea></td>
          </tr>
          <tr>
            <th colspan="2"><input type="submit" name="confirm" value="確認" /></th>
          </tr>
        </table>
        <div><input type="hidden" name="token" value="<?=h($token)?>" /></div>
      </form>
    </p>
<?php endif; ?>
  </body>
</html>
```