画像添付 PHP メールフォーム をコピペで制作

画像添付 PHPメールフォーム ダウンロード

画像添付 PHP メールフォームのご紹介です。
機能性を増した物は下記をぜひためしてみてください。

Lemonform

メールフォームとは

メールフォームとは、Webサイトに設置されており、メールを介して申込みや投稿を行うためのものです。
広い意味では、Web上のテキスト投稿なども POST を通じた情報のやりとりと捉えることができます。
このやりとりは、Webサービスの信頼性や機能性を左右する重要な部分です。

フロントエンドは通常 HTML で構成され、バックエンドには PHP や Node.js+JavaScript フレームワークなどが使用されます。
なお、Node.js はサーバー自体を含むため、一般的なレンタルサーバーでは主に PHP が利用されています。

かつては .cgi 拡張子で Perl を使った CGI も多く見られました。

PHPメールフォームを作る

ここでは、PHP を用いたメールフォームを作成してみたいと思います。

構成は以下の3ファイルです:

  • index.php(入力画面)
  • confirmation.php(確認画面)
  • send.php(送信処理)

よく検索で見かける「PHP工房」さんのように1ファイルで完結する例もありますが、ここではセキュリティ対策として「CSRF 対策」や「セッション管理による再送信防止」を考慮し、3ファイル構成としています。

まず、index.php

<form action="confirmation.php" method="post" enctype="multipart/form-data">
    <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($token) ?>">

    <div class="form-group">
      <label for="name" class="required">名前(必須)</label>
      <input type="text" name="name" id="name" required placeholder="例: 檸檬">
    </div>

    <div class="form-group">
      <label for="kana">ふりがな</label>
      <input type="text" name="kana" id="kana" placeholder="例: れもん">
    </div>

    <div class="form-group">
      <label for="prefecture">住所(都道府県)</label>
      <select name="prefecture" id="prefecture">
        <option value="">選択してください</option>
        <?php
        $prefs = ['北海道','青森県','岩手県','宮城県','秋田県','山形県','福島県','茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県','新潟県','富山県','石川県','福井県','山梨県','長野県','岐阜県','静岡県','愛知県','三重県','滋賀県','京都府','大阪府','兵庫県','奈良県','和歌山県','鳥取県','島根県','岡山県','広島県','山口県','徳島県','香川県','愛媛県','高知県','福岡県','佐賀県','長崎県','熊本県','大分県','宮崎県','鹿児島県','沖縄県'];
        foreach ($prefs as $p) {
          echo '<option value="'.htmlspecialchars($p).'">'.htmlspecialchars($p).'</option>';
        }
        ?>
      </select>
    </div>

    <div class="form-group">
      <label for="email" class="required">メールアドレス(必須)</label>
      <input type="email" name="email" id="email" required placeholder="例: example@mail.com">
    </div>

<div class="form-group">
  <label for="file">画像添付①</label>
  <input type="file" name="file" id="file" accept="image/*">
</div>

<div class="form-group">
  <label for="file2">画像添付②</label>
  <input type="file" name="file2" id="file2" accept="image/*">
</div>

<div class="form-group">
  <label for="file3">画像添付③</label>
  <input type="file" name="file3" id="file3" accept="image/*">
</div>
    
    <div class="form-group submit-group">
      <button type="submit" class="submit-button">確認画面へ進む</button>
    </div>

  </form>

このフォームのポイントは以下の2点です:

method="post" enctype="multipart/form-data"

method=”post” および enctype=”multipart/form-data” を指定することで、画像ファイルの送信が可能になります。

<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($token) ?>">

csrf_token を hidden フィールドで送信し、外部からの不正なアクセスを防止します。
csrf_token を生成するため、ページ上部には以下の PHP を記述します

<?php
session_start();
session_regenerate_id(true);
$token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $token;
?>
<!DOCTYPE html>
<html lang="ja">
<head>

session_start() によりセッションを開始し、生成したトークンをセッションと hidden フィールドの両方に保存しています。

PHPメールフォーム 確認画面

confirmation.php

<?php
session_start();
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
  header('Location: index.php');
  exit;
}

// CSRFチェック
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== ($_SESSION['csrf_token'] ?? '')) {
  exit('不正なアクセスです。');
}

// サニタイズ関数
function h($str) {
  return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

// ファイル処理用
$image_fields = ['file', 'file2', 'file3'];
$saved_images = [];
$image_previews = [];

$tmp_dir = __DIR__ . '/tmp/';
if (!is_dir($tmp_dir)) {
  mkdir($tmp_dir, 0755, true);
}

foreach ($image_fields as $field) {
  if (!isset($_FILES[$field]) || $_FILES[$field]['error'] === UPLOAD_ERR_NO_FILE) continue;
  if ($_FILES[$field]['error'] !== UPLOAD_ERR_OK) continue;

  $mime = mime_content_type($_FILES[$field]['tmp_name']);
  $allowed = ['image/jpeg', 'image/png', 'image/gif'];
  if (!in_array($mime, $allowed)) continue;

  $ext = pathinfo($_FILES[$field]['name'], PATHINFO_EXTENSION);
  $filename = uniqid($field . '_') . '.' . $ext;
  $destination = $tmp_dir . $filename;

  if (is_uploaded_file($_FILES[$field]['tmp_name'])) {
    move_uploaded_file($_FILES[$field]['tmp_name'], $destination);
    $saved_images[$field] = $filename;
    $image_previews[] = './tmp/' . $filename;
  }
}

// フォームデータ保持
$_SESSION['form_data'] = [
  'name' => $_POST['name'] ?? '',
  'kana' => $_POST['kana'] ?? '',
  'prefecture' => $_POST['prefecture'] ?? '',
  'email' => $_POST['email'] ?? '',
  'images' => $saved_images
];
?>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>確認画面</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    .data-group { margin-bottom: 15px; }
    .buttons { margin-top: 30px; }
    img.preview { max-width: 200px; margin-top: 10px; margin-right: 10px; }
  </style>
</head>
<body>

  <h1>入力内容の確認</h1>

  <div class="data-group"><strong>名前:</strong><?= h($_POST['name']) ?></div>
  <div class="data-group"><strong>ふりがな:</strong><?= h($_POST['kana']) ?></div>
  <div class="data-group"><strong>都道府県:</strong><?= h($_POST['prefecture']) ?></div>
  <div class="data-group"><strong>メール:</strong><?= h($_POST['email']) ?></div>

  <?php if (!empty($image_previews)): ?>
    <div class="data-group">
      <strong>添付画像:</strong><br>
      <?php foreach ($image_previews as $url): ?>
        <img src="<?= h($url) ?>" alt="画像" class="preview">
      <?php endforeach; ?>
    </div>
  <?php endif; ?>

  <form action="send.php" method="post">
    <input type="hidden" name="csrf_token" value="<?= h($_POST['csrf_token']) ?>">
    <input type="hidden" name="name" value="<?= h($_POST['name']) ?>">
    <input type="hidden" name="kana" value="<?= h($_POST['kana']) ?>">
    <input type="hidden" name="prefecture" value="<?= h($_POST['prefecture']) ?>">
    <input type="hidden" name="email" value="<?= h($_POST['email']) ?>">

    <?php foreach ($saved_images as $key => $filename): ?>
      <input type="hidden" name="uploaded_images[<?= h($key) ?>]" value="<?= h($filename) ?>">
    <?php endforeach; ?>

    <div class="buttons">
      <button type="submit">この内容で送信する</button>
    </div>
  </form>

  <form action="index.php" method="get">
    <div class="buttons">
      <button type="submit">戻って修正する</button>
    </div>
  </form>

</body>
</html>

index.php から POST で送られてきた内容を受け取り、確認画面を表示します。
今回は画像添付フォームなので、この段階で画像がサーバーに一時保存され、確認画面でプレビュー表示されます。

確認後、「送信」ボタンを押すと send.php によって処理されます。

PHPメールフォーム 送信

send.php

<?php
session_start();

// CSRFチェック
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['csrf_token']) || $_POST['csrf_token'] !== ($_SESSION['csrf_token'] ?? '')) {
  exit('不正な送信です。');
}

// 入力データ取得
$name = $_POST['name'] ?? '';
$kana = $_POST['kana'] ?? '';
$prefecture = $_POST['prefecture'] ?? '';
$email = $_POST['email'] ?? '';
$uploaded_images = $_POST['uploaded_images'] ?? [];

$tmp_dir = __DIR__ . '/tmp/';

// メール本文
$body = "以下の内容でお問い合わせがありました。\n\n";
$body .= "名前: {$name}\n";
$body .= "ふりがな: {$kana}\n";
$body .= "都道府県: {$prefecture}\n";
$body .= "メール: {$email}\n\n";
$body .= "送信元IP: {$_SERVER['REMOTE_ADDR']}\n";
$body .= "送信日時: " . date('Y-m-d H:i:s') . "\n";

// メールヘッダ
$to = ''; // 管理者宛先
$from = ''; // 自ドメイン
$subject = '【お問い合わせ】フォームからの送信';
$headers = "From: {$from}\r\n";
$headers .= "Reply-To: {$email}\r\n";

// 添付メール送信関数
function send_mail_with_attachments($to, $subject, $message, $headers, $files = []) {
  $boundary = "==Multipart_" . md5(uniqid());
  $headers .= "MIME-Version: 1.0\r\n";
  $headers .= "Content-Type: multipart/mixed; boundary=\"{$boundary}\"\r\n";

  $body = "--{$boundary}\r\n";
  $body .= "Content-Type: text/plain; charset=\"UTF-8\"\r\n";
  $body .= "Content-Transfer-Encoding: 7bit\r\n\r\n";
  $body .= $message . "\r\n\r\n";

  foreach ($files as $filepath) {
    if (!file_exists($filepath)) continue;
    $filename = basename($filepath);
    $mime = mime_content_type($filepath);
    $content = chunk_split(base64_encode(file_get_contents($filepath)));

    $body .= "--{$boundary}\r\n";
    $body .= "Content-Type: {$mime}; name=\"{$filename}\"\r\n";
    $body .= "Content-Disposition: attachment; filename=\"{$filename}\"\r\n";
    $body .= "Content-Transfer-Encoding: base64\r\n\r\n";
    $body .= $content . "\r\n\r\n";
  }

  $body .= "--{$boundary}--";

  return mail($to, $subject, $body, $headers);
}

// 添付ファイルのパス配列を作成
$files = [];
foreach ($uploaded_images as $img) {
  $filepath = $tmp_dir . basename($img);
  if (file_exists($filepath)) {
    $files[] = $filepath;
  }
}

// 送信
$sent = send_mail_with_attachments($to, $subject, $body, $headers, $files);


if (!empty($_SESSION['form_data']['images'])) {
  $tmp_dir = __DIR__ . '/tmp/';
  foreach ($_SESSION['form_data']['images'] as $filename) {
    $path = $tmp_dir . basename($filename);
    if (file_exists($path)) unlink($path);
  }
  unset($_SESSION['form_data']['images']);
}

// セッション破棄
unset($_SESSION['form_data']);

// 完了画面表示
if ($sent) {
  echo '<p>送信が完了しました。ありがとうございました。</p>';
} else {
  echo '<p>送信に失敗しました。</p>';
}
?>
<p><a href="index.php">戻る</a></p>

このファイルでは、確認画面で「送信」ボタンが押されたあと、最終的な送信処理を行います。

  • CSRFトークンを検証し、不正アクセスを防止
  • 入力された情報と画像ファイルを取得
  • 管理者宛にメールを送信(画像は添付)
  • 送信後、サーバーに一時保存された画像を削除
  • セッションを破棄して再送信を防止

設定も同じファイルになっています。
送信先メールアドレスを設定し、サーバーにアップすれば動くはず。

送信した後、セッションで画像を識別しサーバーから削除します。
送信後セッションも破棄されるので、戻っても再送信はできません。

このメールフォームはサンプル

このサンプルフォームはシンプルな構造で設計されています。そのため

  • 自動返信メール機能 は含まれていません
  • フォームのフィールドを変更する際、POST処理側のコードも都度修正が必要です
  • 入力バリデーションやスパム対策など、セキュリティ対策は最低限です
  • WordPressなどのCMSと組み合わせると、セッション処理が競合して正しく動作しない可能性があります

またセキュリティ面もバリデーションチェックなども不十分です。

よりよい自分設置型 画像添付フォームをお探しの方へ

上記のような不便さや制限を解消するために開発されたのが lemonform です。

  • JavaScript による入力補助機能
  • 有料版では 動画ファイルの送信 にも対応
  • 容量制限なども設定可能
  • スマホ対応やスパム対策など、機能を搭載

ぜひお試しください。

Lemonform