สร้าง PHP Contact Form แบบไม่ใช้ฐานข้อมูล

โดย CyberMAN


PHP · Contact Form · No Database

สร้าง PHP Contact Form
แบบไม่ใช้ฐานข้อมูล

Lightweight Script ที่ทุกเว็บไซต์ควรมี — รับข้อมูลผู้ใช้แล้วส่ง Email แจ้งเตือนได้ทันที

ทำไมต้องมี Contact Form?

ทุกเว็บไซต์ที่ต้องการรับ Feedback หรือสอบถามจากผู้ใช้ จำเป็นต้องมีหน้า "ติดต่อเรา" วิธีดั้งเดิมคือให้แสดง Email Address ตรงๆ แต่มีปัญหาเรื่อง Spam Bot มาดูดเอาไปใช้ แนวทางที่ดีกว่าคือใช้ PHP Contact Form — ผู้ใช้กรอกข้อมูลในหน้าเว็บ แล้วให้ PHP จัดการส่ง Email ให้เบื้องหลัง

บทความนี้จะสอนสร้าง Contact Form แบบ ไม่พึ่งฐานข้อมูล โค้ดเบาสุด ติดตั้งง่าย เหมาะสำหรับเว็บไซต์ขนาดเล็กหรือ Landing Page และยังเป็นพื้นฐานที่จะต่อยอดเข้า CodeIgniter 4 หรือ Laravel ได้ในภายหลัง

สิ่งที่จะได้เรียนรู้: HTML Form → PHP $_POST → Validation → mail() function → แสดง Success/Error Message

Step 1 — สร้าง HTML Form

เริ่มด้วยไฟล์ contact.html (หรือ contact.php) ที่มี Form รับชื่อ, Email และข้อความ สังเกต attribute สำคัญ 2 ตัวบน <form>:

  • method="POST" — ซ่อนข้อมูลใน Request Body ไม่โชว์ใน URL
  • action="send.php" — ส่งข้อมูลไปประมวลผลที่ไฟล์นี้
contact.php
<!DOCTYPE html>
<html lang="th">
<head>
  <meta charset="UTF-8">
  <title>ติดต่อเรา</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
</head>
<body class="bg-light py-5">
<div class="container" style="max-width:560px">
  <h2 class="mb-4">ติดต่อเรา</h2>

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

    <div class="mb-3">
      <label class="form-label">ชื่อ-นามสกุล</label>
      <input type="text" name="name"
             class="form-control"
             placeholder="กรอกชื่อของคุณ"
             required>
    </div>

    <div class="mb-3">
      <label class="form-label">อีเมล</label>
      <input type="email" name="email"
             class="form-control"
             placeholder="example@email.com"
             required>
    </div>

    <div class="mb-3">
      <label class="form-label">ข้อความ</label>
      <textarea name="message"
                class="form-control"
                rows="5"
                placeholder="พิมพ์ข้อความของคุณ..."
                required></textarea>
    </div>

    <button type="submit" class="btn btn-primary w-100">
      ส่งข้อความ
    </button>

  </form>
</div>
</body></html>

Step 2 — สร้าง PHP Handler (send.php)

นี่คือหัวใจหลักของระบบ ไฟล์ send.php จะรับค่าจาก $_POST, Validate ข้อมูล, แล้วใช้ฟังก์ชัน mail() ส่ง Email ไปยังกล่องจดหมายของเรา

send.php
<?php
// ป้องกัน Direct Access (ต้องมาจาก POST เท่านั้น)
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    header('Location: contact.php');
    exit();
}

// 1. รับค่าและทำความสะอาดข้อมูล (Sanitize)
$name    = trim(htmlspecialchars(strip_tags($_POST['name'])));
$email   = trim(filter_var($_POST['email'], FILTER_SANITIZE_EMAIL));
$message = trim(htmlspecialchars(strip_tags($_POST['message'])));

// 2. Validate ข้อมูล
$errors = [];

if (empty($name)) {
    $errors[] = 'กรุณากรอกชื่อ';
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $errors[] = 'รูปแบบอีเมลไม่ถูกต้อง';
}
if (strlen($message) < 10) {
    $errors[] = 'ข้อความต้องมีอย่างน้อย 10 ตัวอักษร';
}

// 3. ถ้า Validate ไม่ผ่าน — แสดง Error แล้วหยุด
if (!empty($errors)) {
    foreach ($errors as $err) {
        echo "<p style='color:red'>" . $err . "</p>";
    }
    exit();
}

// 4. เตรียม Email
$to      = 'your@email.com';  // เปลี่ยนเป็น Email ของคุณ
$subject = '[Contact Form] ข้อความจาก ' . $name;
$body    = "ชื่อ: {$name}\n"
          . "อีเมล: {$email}\n\n"
          . "ข้อความ:\n{$message}";

$headers = implode("\r\n", [
    'From: noreply@yoursite.com',
    'Reply-To: ' . $email,
    'Content-Type: text/plain; charset=UTF-8',
    'X-Mailer: PHP/' . phpversion(),
]);

// 5. ส่ง Email
if (mail($to, $subject, $body, $headers)) {
    echo '<p style="color:green">✅ ส่งข้อความสำเร็จ! เราจะติดต่อกลับเร็วๆ นี้</p>';
} else {
    echo '<p style="color:red">❌ เกิดข้อผิดพลาด ไม่สามารถส่งได้ กรุณาลองใหม่</p>';
}
?>
⚠️ หมายเหตุ: ฟังก์ชัน mail() ต้องมีการตั้งค่า SMTP บน Server ให้ถูกต้อง บน Localhost (XAMPP/Laragon) อาจต้องใช้ PHPMailer + Gmail SMTP แทน — จะอธิบายใน Step 4

Step 3 — Security: ป้องกัน XSS และ Header Injection

Contact Form ที่ไม่ได้ป้องกันเลยมีความเสี่ยง 2 อย่างหลัก:

XSS (Cross-Site Scripting)

ผู้ไม่หวังดีอาจส่ง <script>alert('hacked')</script> เข้ามาในช่องข้อความ แก้ด้วยการใช้ htmlspecialchars() และ strip_tags() ก่อนนำไปแสดงผล

Email Header Injection

ถ้าเอา $email ที่รับมาใส่ใน Header โดยตรงโดยไม่ Sanitize อาจถูก Inject Header เพิ่มได้ โค้ดข้างบนใช้ FILTER_SANITIZE_EMAIL เพื่อตัดอักขระที่ไม่ถูกต้องออก

ตัวอย่าง Sanitize
// ✅ Safe — ใช้ filter functions ก่อนเสมอ
$name  = trim(htmlspecialchars(strip_tags($_POST['name'])));
$email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);

// ❌ Unsafe — อย่าทำแบบนี้
$name  = $_POST['name'];    // ไม่ได้ Sanitize เลย
$email = $_POST['email'];   // เสี่ยง Header Injection

Step 4 — ใช้ PHPMailer แทน mail() (แนะนำ)

ฟังก์ชัน mail() แบบ Built-in ใช้ได้บน Shared Hosting ทั่วไป แต่ถ้าต้องการส่งผ่าน Gmail SMTP หรือ Service อื่นเช่น SendGrid, Mailgun ควรใช้ PHPMailer ซึ่งติดตั้งผ่าน Composer ได้เลย

terminal
composer require phpmailer/phpmailer
send_smtp.php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require 'vendor/autoload.php';

$mail = new PHPMailer(true);

try {
    // SMTP Settings
    $mail->isSMTP();
    $mail->Host       = 'smtp.gmail.com';
    $mail->SMTPAuth   = true;
    $mail->Username   = 'your@gmail.com';
    $mail->Password   = 'app-password-here'; // App Password จาก Google
    $mail->SMTPSecure = 'tls';
    $mail->Port       = 587;
    $mail->CharSet    = 'UTF-8';

    // ผู้ส่ง/ผู้รับ
    $mail->setFrom('noreply@yoursite.com', 'My Website');
    $mail->addAddress('your@email.com');
    $mail->addReplyTo($email, $name);

    // เนื้อหา Email
    $mail->Subject = '[Contact] ข้อความจาก ' . $name;
    $mail->Body    = "ชื่อ: {$name}\nEmail: {$email}\n\n{$message}";

    $mail->send();
    echo '✅ ส่งสำเร็จแล้ว!';

} catch (Exception $e) {
    echo "❌ Error: {$mail->ErrorInfo}";
}

เปรียบเทียบ mail() vs PHPMailer

Featuremail() Built-inPHPMailer
ติดตั้งไม่ต้องติดตั้ง มีใน PHP ทันทีต้องติดตั้งผ่าน Composer
SMTP Auth❌ ไม่รองรับโดยตรง✅ รองรับเต็มรูปแบบ
Gmail/SendGrid❌ ต้องตั้งค่า Server เอง✅ ใช้งานได้ทันที
HTML Emailทำได้ แต่ยุ่งยาก✅ ง่ายมาก รองรับ Attachment ด้วย
Error Handlingคืนแค่ true/false✅ มี Exception พร้อม Error Message
เหมาะกับShared Hosting ทั่วไปทุก Environment โดยเฉพาะ Production

ต่อยอดเข้า CodeIgniter 4 และ Laravel

ถ้าใช้ Framework อยู่แล้ว ไม่จำเป็นต้องเขียน send.php แยกออกมา ทั้ง CI4 และ Laravel มีโครงสร้างที่รองรับการทำ Contact Form ได้สวยงามกว่า:

CodeIgniter 4

app/Controllers/ContactController.php
<?php
namespace App\Controllers;

use CodeIgniter\Controller;

class ContactController extends Controller
{
    public function send()
    {
        $rules = [
            'name'    => 'required|min_length[2]',
            'email'   => 'required|valid_email',
            'message' => 'required|min_length[10]',
        ];

        if (!$this->validate($rules)) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $email = service('email');
        $email->setTo('your@email.com');
        $email->setSubject('Contact: ' . $this->request->getPost('name'));
        $email->setMessage($this->request->getPost('message'));
        $email->send();

        return redirect()->to('/contact')->with('success', 'ส่งข้อความสำเร็จ!');
    }
}

Laravel (Mailable)

app/Http/Controllers/ContactController.php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use App\Mail\ContactMail;

public function send(Request $request)
{
    $validated = $request->validate([
        'name'    => 'required|string|max:100',
        'email'   => 'required|email',
        'message' => 'required|min:10',
    ]);

    Mail::to('your@email.com')->send(new ContactMail($validated));

    return back()->with('success', 'ขอบคุณ! เราจะติดต่อกลับเร็วๆ นี้');
}
💡 เคล็ดลับ: ใน Laravel ใช้คำสั่ง php artisan make:mail ContactMail เพื่อสร้าง Mailable Class พร้อม Blade Template อัตโนมัติ สะอาดกว่าการ Hard-code HTML ใน Controller มาก

สรุป

Contact Form แบบ Lightweight ที่ไม่ใช้ฐานข้อมูลเป็นจุดเริ่มต้นที่ดีมากสำหรับมือใหม่ แค่ 2 ไฟล์ (contact.php + send.php) ก็พร้อมใช้งานจริงบน Server แล้ว

ขั้นตอนสำคัญที่ต้องจำ:

1รับค่าจาก $_POST แล้ว Sanitize ทันที
2Validate ทุก Field ก่อนดำเนินการต่อ
3ใช้ mail() สำหรับ Hosting ทั่วไป หรือ PHPMailer สำหรับ SMTP
4แสดง Success/Error Message ให้ผู้ใช้รู้ผลทันที
5ต่อยอดเข้า CI4 หรือ Laravel ด้วย Built-in Email Service

อ่านบทความ PHP เพิ่มเติมที่ PHP Code Mania

ผมสร้างระบบ Login และ CRUD แบบง่าย ๆ ด้วย PHP + Bootstrap 5 (โปรเจกต์เริ่มต้นสำหรับมือใหม่)

โดย CyberMAN



PHP PROJECT WALKTHROUGH

ผมสร้างระบบ Login และ CRUD แบบง่าย ๆ ด้วย PHP + Bootstrap 5
(โปรเจกต์เริ่มต้นสำหรับมือใหม่)

ก่อนจะกระโดดไปเรียน CodeIgniter 4 หรือ Laravel แบบเต็มตัว บทความนี้พาทำโปรเจกต์ Login + CRUD ด้วย PHP ล้วน ๆ ให้เห็นภาพว่า Framework ช่วยอะไรเราบ้าง พร้อมแก้จุดอ่อนด้านความปลอดภัยที่บทเรียนทั่วไปมักมองข้าม

อ่านประมาณ 9 นาที🧩ระดับ: เริ่มต้น🛠PHP + MySQLi + Bootstrap 5

หลายคนเริ่มเรียน PHP จากการทำระบบ Login และ CRUD (Create, Read, Update, Delete) เป็นโปรเจกต์แรก เพราะมันคือแกนกลางของเว็บแอปแทบทุกประเภท ตั้งแต่ระบบจัดการนักเรียน ระบบสมาชิกร้านค้า ไปจนถึง Backoffice ขององค์กร ในบทความนี้เราจะสร้างระบบนี้ด้วย PHP ดิบ ๆ (Vanilla PHP) ร่วมกับ Bootstrap 5 ตั้งแต่ศูนย์ และที่สำคัญคือเราจะเขียนให้ ปลอดภัยกว่าตัวอย่างทั่วไปที่หาอ่านได้ตามเว็บ เพราะ Tutorial ส่วนใหญ่มักสอนแบบ query ตรง ๆ ที่เปิดช่องให้เกิด SQL Injection และเก็บรหัสผ่านแบบไม่เข้ารหัส ซึ่งเป็นนิสัยที่ไม่ควรติดไปใช้ในงานจริง

เป้าหมายของบทความนี้ไม่ใช่แค่ "ทำให้รัน" แต่อยากให้มือใหม่เห็นว่าการเขียน PHP แบบดิบต้องดูแลเรื่องอะไรเองบ้าง เมื่อเทียบกับตอนที่เราย้ายไปใช้ CodeIgniter 4 หรือ Laravel ที่มีโครงสร้างพวกนี้เตรียมไว้ให้แล้ว

01สิ่งที่ต้องเตรียมก่อนเริ่ม

  • Local server: XAMPP หรือ Laragon (มี Apache + MySQL + PHP ในตัว)
  • พื้นฐาน PHP: ตัวแปร, ฟังก์ชัน, if/else, array เบื้องต้น
  • พื้นฐาน SQL: CREATE TABLE, SELECT, INSERT, UPDATE, DELETE
  • Bootstrap 5: ใช้ผ่าน CDN ไม่ต้องติดตั้งอะไรเพิ่ม

02สร้างฐานข้อมูลและไฟล์เชื่อมต่อ

เริ่มจากสร้างฐานข้อมูลชื่อ pcm_demo และตาราง users สำหรับ Login กับตาราง students สำหรับฝึก CRUD

schema.sql
CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(50) NOT NULL UNIQUE,
  password VARCHAR(255) NOT NULL
);

CREATE TABLE students (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  class VARCHAR(20) NOT NULL,
  marks INT NOT NULL
);

จากนั้นสร้างไฟล์เชื่อมต่อฐานข้อมูล โดยใช้ MySQLi แบบ Object-Oriented และเปิด exception mode ไว้ เพื่อให้ error แสดงชัดเจนตอนพัฒนา

config/db.php
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'pcm_demo');

try {
    $conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
    $conn->set_charset('utf8mb4');
} catch (mysqli_sql_exception $e) {
    die('Connection failed: ' . $e->getMessage());
}
💡 ทำไมต้อง mysqli_reportการเปิด MYSQLI_REPORT_STRICT ทำให้ทุก query ที่ผิดพลาดโยน Exception ออกมาทันที แทนที่จะปล่อยให้ query ล้มเหลวเงียบ ๆ ซึ่งช่วยจับบั๊กได้เร็วกว่าการเช็ค if ($result === false) ทุกจุด

03ระบบ Login ที่ปลอดภัยด้วย password_hash()

จุดที่ Tutorial ทั่วไปมักพลาดคือเก็บรหัสผ่านเป็น plain text หรือเทียบรหัสผ่านด้วย == ตรง ๆ ในที่นี้เราจะใช้ password_hash() ตอนสมัครสมาชิก และ password_verify() ตอน Login พร้อม Prepared Statement เพื่อกัน SQL Injection

register.php
<?php
require 'config/db.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username']);
    $hashed   = password_hash($_POST['password'], PASSWORD_DEFAULT);

    $stmt = $conn->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
    $stmt->bind_param("ss", $username, $hashed);
    $stmt->execute();

    header('Location: login.php?registered=1');
    exit;
}
login.php
<?php
session_start();
require 'config/db.php';
$error = null;

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username']);
    $password = $_POST['password'];

    $stmt = $conn->prepare("SELECT id, password FROM users WHERE username = ?");
    $stmt->bind_param("s", $username);
    $stmt->execute();
    $result = $stmt->get_result();
    $user   = $result->fetch_assoc();

    if ($user && password_verify($password, $user['password'])) {
        session_regenerate_id(true);
        $_SESSION['user_id']  = $user['id'];
        $_SESSION['username'] = $username;
        header('Location: students.php');
        exit;
    }
    $error = 'Username หรือ Password ไม่ถูกต้อง';
}
⚠️ จุดที่มือใหม่พลาดบ่อยการเทียบรหัสผ่านด้วย $password == $user['password'] ใช้ไม่ได้กับ hash เพราะ password_hash() สร้าง salt แบบสุ่มทุกครั้ง ต้องใช้ password_verify() เท่านั้น และห้าม query แบบเอา $_POST ไปต่อ string SQL ตรง ๆ เด็ดขาด เพราะนั่นคือช่องโหว่ SQL Injection แบบคลาสสิก

ส่วน Form ฝั่ง HTML ใช้ Bootstrap 5 แบบมาตรฐาน เช่น

login.php (HTML part)
<div class="container mt-5" style="max-width:400px">
  <h3 class="mb-4">เข้าสู่ระบบ</h3>
  <?php if ($error): ?>
    <div class="alert alert-danger"><?= htmlspecialchars($error) ?></div>
  <?php endif; ?>
  <form method="post">
    <input name="username" class="form-control mb-3" placeholder="Username" required>
    <input type="password" name="password" class="form-control mb-3" placeholder="Password" required>
    <button class="btn btn-primary w-100">เข้าสู่ระบบ</button>
  </form>
</div>

04CRUD นักเรียนด้วย Bootstrap 5

หลัง Login สำเร็จ เราจะพาไปหน้า students.php ที่แสดงตารางข้อมูล พร้อมปุ่มเพิ่ม/แก้ไข/ลบ ทุกหน้าต้องเช็ค session ก่อนเสมอ ไม่ใช่แค่ซ่อนปุ่มเฉย ๆ

students.php (Read)
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
    header('Location: login.php');
    exit;
}
require 'config/db.php';
$students = $conn->query("SELECT * FROM students ORDER BY id DESC");
?>
<table class="table table-striped">
  <tr><th>ชื่อ</th><th>ห้อง</th><th>คะแนน</th><th></th></tr>
  <?php while ($row = $students->fetch_assoc()): ?>
  <tr>
    <td><?= htmlspecialchars($row['name']) ?></td>
    <td><?= htmlspecialchars($row['class']) ?></td>
    <td><?= $row['marks'] ?></td>
    <td>
      <a href="edit.php?id=<?= $row['id'] ?>" class="btn btn-sm btn-warning">แก้ไข</a>
      <a href="delete.php?id=<?= $row['id'] ?>" class="btn btn-sm btn-danger"
         onclick="return confirm('ลบรายการนี้?')">ลบ</a>
    </td>
  </tr>
  <?php endwhile; ?>
</table>
delete.php (Delete)
<?php
session_start();
if (!isset($_SESSION['user_id'])) { header('Location: login.php'); exit; }
require 'config/db.php';

$id = (int) ($_GET['id'] ?? 0);
$stmt = $conn->prepare("DELETE FROM students WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();

header('Location: students.php');
exit;

ส่วน add.php และ edit.php ก็ใช้แพทเทิร์นเดียวกัน คือ เช็ค session → bind_param ทุก query → redirect หลังบันทึก ทำซ้ำแบบนี้ใน 4 ไฟล์ (add, edit, delete, login) คือสิ่งที่มือใหม่จะเริ่มรู้สึก "เขียนซ้ำเยอะมาก" และนั่นคือจุดเริ่มต้นที่ทำให้เราอยากมี Framework เข้ามาช่วย

05เขียนเองทั้งหมด vs ใช้ Framework

พอลองสร้างระบบ Login + CRUD ด้วย PHP ดิบจนจบ จะเห็นภาพชัดว่าอะไรที่เราต้อง "คิดเอง ทำเอง" ทั้งหมด ลองเทียบให้เห็นภาพกับ CodeIgniter 4 และ Laravel

หัวข้อPHP ดิบ (Vanilla)CodeIgniter 4Laravel
ป้องกัน SQL Injectionต้องเขียน prepared statement เองQuery Builder ป้องกันให้อัตโนมัติEloquent ป้องกันให้อัตโนมัติ
เก็บรหัสผ่านต้องเรียก password_hash() เองมี password helper ในตัวHash facade + bcrypt อัตโนมัติ
โครงสร้างไฟล์ปนกันหมดในไฟล์เดียวMVC แยกชัดเจนMVC + Service/Repository ได้
Validation ฟอร์มเขียน if/else เองทั้งหมดValidation library ในตัวForm Request Validation
ความเร็วในการเริ่มโปรเจกต์เล็กเร็วที่สุด ไม่ต้องตั้งค่าอะไรต้องตั้งค่าเริ่มต้นเล็กน้อยตั้งค่าเริ่มต้นมากกว่า CI4
เหมาะกับโปรเจกต์ใหญ่ขึ้นดูแลยากเมื่อโค้ดเยอะรองรับงานระดับองค์กรได้ดีรองรับงานใหญ่ ระบบซับซ้อนได้ดี

สรุปง่าย ๆ คือ PHP ดิบเหมาะกับการ เข้าใจกลไกเบื้องหลัง ว่า Login ทำงานยังไง CRUD เชื่อมฐานข้อมูลยังไง แต่พอจะทำงานจริงหรือโปรเจกต์ที่มีหลายหน้า หลายฟีเจอร์ Framework อย่าง CodeIgniter 4 หรือ Laravel จะช่วยลดงานซ้ำซ้อนและลดโอกาสพลาดเรื่องความปลอดภัยได้มาก


06สรุป

โปรเจกต์ Login + CRUD ด้วย PHP + Bootstrap 5 เป็นจุดเริ่มต้นที่ดีมากสำหรับมือใหม่ เพราะครอบคลุมทักษะหลักที่ใช้ซ้ำในแทบทุกเว็บแอป ทั้งการเชื่อมต่อฐานข้อมูล การยืนยันตัวตน และการจัดการข้อมูลพื้นฐาน แต่สิ่งสำคัญที่อยากให้จำไว้คือ อย่าก๊อปโค้ดจาก Tutorial ที่ไม่มี prepared statement หรือไม่ hash รหัสผ่าน เพราะนิสัยตอนเริ่มต้นมักติดตัวไปจนถึงงานจริง



PHP CI MANIA - PHP Code Generator 

โปรแกรมช่วยสร้างโค้ด "ลดเวลาการเขียนโปรแกรม"
ราคาสุดคุ้ม  
http://www.phpcodemania.com