Sau khi nắm vững xử lý chuỗi trong PHP, bước tiếp theo là học cách thu thập dữ liệu từ người dùng với Form HTML và GET/POST. Đây là kiến thức nền tảng quan trọng nhất để xây dựng các tính năng như đăng ký, đăng nhập, tìm kiếm, bình luận, liên hệ, và mọi tương tác yêu cầu người dùng nhập thông tin. Bạn sẽ học cách tạo form, gửi dữ liệu, validate, và bảo mật ứng dụng.
1. Form HTML là gì?
Form (biểu mẫu) là thành phần HTML cho phép người dùng nhập dữ liệu và gửi đến server. PHP nhận dữ liệu này thông qua 2 phương thức chính: GET và POST.
1.1. Cấu trúc form cơ bản
<!-- Form đơn giản -->
<form action="xu-ly.php" method="POST">
<label>Họ tên:</label>
<input type="text" name="ho_ten">
<label>Email:</label>
<input type="email" name="email">
<button type="submit">Gửi</button>
</form>Giải thích:
- ✅
action: File PHP xử lý dữ liệu (để trống nếu xử lý cùng file) - ✅
method: GET hoặc POST (mặc định GET) - ✅
name: Tên biến để PHP nhận dữ liệu - ✅
type="submit": Nút gửi form
2. GET vs POST – Sự khác biệt quan trọng
2.1. Phương thức GET
Gửi dữ liệu qua URL (query string):
<!-- Form tìm kiếm -->
<form action="tim-kiem.php" method="GET">
<input type="text" name="tu_khoa" placeholder="Nhập từ khóa...">
<button type="submit">Tìm kiếm</button>
</form>Khi submit, URL sẽ là:
tim-kiem.php?tu_khoa=laptopXử lý trong PHP:
<?php
// tim-kiem.php
// Nhận dữ liệu từ GET
if (isset($_GET['tu_khoa'])) {
$tu_khoa = $_GET['tu_khoa'];
echo "Bạn đang tìm: " . htmlspecialchars($tu_khoa);
} else {
echo "Chưa có từ khóa tìm kiếm";
}
?>Đặc điểm GET:
- ✅ Dữ liệu hiển thị trên URL
- ✅ Có thể bookmark (lưu link)
- ✅ Giới hạn độ dài (~2000 ký tự)
- ✅ Dùng cho: Tìm kiếm, filter, phân trang
- ❌ KHÔNG dùng cho: Mật khẩu, dữ liệu nhạy cảm
2.2. Phương thức POST
Gửi dữ liệu ẩn trong HTTP request body:
<!-- Form đăng nhập -->
<form action="dang-nhap.php" method="POST">
<input type="text" name="username" placeholder="Tên đăng nhập">
<input type="password" name="password" placeholder="Mật khẩu">
<button type="submit">Đăng nhập</button>
</form>Xử lý trong PHP:
<?php
// dang-nhap.php
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$username = $_POST['username'];
$password = $_POST['password'];
// Validate
if (empty($username) || empty($password)) {
echo "Vui lòng điền đầy đủ thông tin";
} else {
// Xử lý đăng nhập (kiểm tra database...)
echo "Đang đăng nhập với tài khoản: " . htmlspecialchars($username);
}
}
?>Đặc điểm POST:
- ✅ Dữ liệu KHÔNG hiển thị trên URL
- ✅ Không giới hạn độ dài
- ✅ Upload file được
- ✅ Dùng cho: Đăng ký, đăng nhập, upload, insert/update data
- ❌ Không bookmark được
2.3. Bảng so sánh GET vs POST
| Tiêu chí | GET | POST |
|---|---|---|
| Dữ liệu | Trên URL | Trong request body |
| Bảo mật | Thấp (nhìn thấy được) | Cao hơn (ẩn) |
| Bookmark | ✅ Được | ❌ Không |
| Cache | ✅ Có | ❌ Không |
| Giới hạn | ~2000 ký tự | Không giới hạn |
| Upload file | ❌ Không | ✅ Được |
| Dùng cho | Tìm kiếm, filter | Đăng ký, đăng nhập |
3. Các loại input trong form
<form action="xu-ly.php" method="POST">
<!-- Text input -->
<input type="text" name="ho_ten" placeholder="Họ tên">
<!-- Email -->
<input type="email" name="email" placeholder="Email">
<!-- Password -->
<input type="password" name="mat_khau" placeholder="Mật khẩu">
<!-- Number -->
<input type="number" name="tuoi" min="1" max="120">
<!-- Date -->
<input type="date" name="ngay_sinh">
<!-- Radio buttons -->
<input type="radio" name="gioi_tinh" value="nam"> Nam
<input type="radio" name="gioi_tinh" value="nu"> Nữ
<!-- Checkbox -->
<input type="checkbox" name="so_thich[]" value="php"> PHP
<input type="checkbox" name="so_thich[]" value="js"> JavaScript
<!-- Select dropdown -->
<select name="thanh_pho">
<option value="">Chọn thành phố</option>
<option value="hn">Hà Nội</option>
<option value="hcm">TP.HCM</option>
</select>
<!-- Textarea -->
<textarea name="ghi_chu" rows="4"></textarea>
<!-- File upload -->
<input type="file" name="anh_dai_dien">
<!-- Hidden field -->
<input type="hidden" name="user_id" value="123">
<button type="submit">Gửi</button>
</form>Xử lý trong PHP:
<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$ho_ten = $_POST['ho_ten'];
$email = $_POST['email'];
$tuoi = $_POST['tuoi'];
$gioi_tinh = $_POST['gioi_tinh'];
$thanh_pho = $_POST['thanh_pho'];
$ghi_chu = $_POST['ghi_chu'];
// Checkbox - mảng
$so_thich = isset($_POST['so_thich']) ? $_POST['so_thich'] : [];
echo "Họ tên: $ho_ten<br>";
echo "Sở thích: " . implode(", ", $so_thich);
}
?>4. Validate dữ liệu form
Validate là bước QUAN TRỌNG NHẤT – không bao giờ tin tưởng dữ liệu từ người dùng!
4.1. Validate cơ bản
<?php
$errors = [];
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// 1. Kiểm tra rỗng
if (empty($_POST['ho_ten'])) {
$errors[] = "Họ tên không được để trống";
}
if (empty($_POST['email'])) {
$errors[] = "Email không được để trống";
}
// 2. Validate email
if (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
$errors[] = "Email không hợp lệ";
}
// 3. Kiểm tra độ dài
if (strlen($_POST['mat_khau']) < 6) {
$errors[] = "Mật khẩu phải ít nhất 6 ký tự";
}
// 4. Kiểm tra số
if (!is_numeric($_POST['tuoi']) || $_POST['tuoi'] < 1 || $_POST['tuoi'] > 120) {
$errors[] = "Tuổi không hợp lệ";
}
// 5. Hiển thị lỗi hoặc xử lý
if (count($errors) > 0) {
foreach ($errors as $error) {
echo "<p style='color:red'>$error</p>";
}
} else {
echo "Dữ liệu hợp lệ! Đang xử lý...";
// Lưu vào database...
}
}
?>4.2. Validate nâng cao
<?php
function validateForm($data) {
$errors = [];
// Trim khoảng trắng
$data = array_map('trim', $data);
// Họ tên
if (empty($data['ho_ten'])) {
$errors['ho_ten'] = "Họ tên không được để trống";
} elseif (strlen($data['ho_ten']) < 3) {
$errors['ho_ten'] = "Họ tên phải ít nhất 3 ký tự";
}
// Email
if (empty($data['email'])) {
$errors['email'] = "Email không được để trống";
} elseif (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
$errors['email'] = "Email không đúng định dạng";
}
// Mật khẩu
if (empty($data['mat_khau'])) {
$errors['mat_khau'] = "Mật khẩu không được để trống";
} elseif (strlen($data['mat_khau']) < 8) {
$errors['mat_khau'] = "Mật khẩu phải ít nhất 8 ký tự";
} elseif (!preg_match('/[A-Z]/', $data['mat_khau'])) {
$errors['mat_khau'] = "Mật khẩu phải có ít nhất 1 chữ hoa";
} elseif (!preg_match('/[0-9]/', $data['mat_khau'])) {
$errors['mat_khau'] = "Mật khẩu phải có ít nhất 1 chữ số";
}
// Số điện thoại
if (!empty($data['dien_thoai'])) {
if (!preg_match('/^0[0-9]{9}$/', $data['dien_thoai'])) {
$errors['dien_thoai'] = "Số điện thoại không hợp lệ (VD: 0912345678)";
}
}
return $errors;
}
// Sử dụng
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$errors = validateForm($_POST);
if (empty($errors)) {
echo "Đăng ký thành công!";
} else {
foreach ($errors as $field => $error) {
echo "<p style='color:red'>$error</p>";
}
}
}
?>5. Bảo mật form
5.1. Ngăn chặn XSS (Cross-Site Scripting)
<?php
// ❌ NGUY HIỂM - Không bao giờ làm thế này
echo $_POST['ho_ten'];
// ✅ AN TOÀN - Luôn dùng htmlspecialchars()
echo htmlspecialchars($_POST['ho_ten'], ENT_QUOTES, 'UTF-8');
// Hàm tiện ích
function escape($string) {
return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}
echo escape($_POST['ho_ten']);
?>5.2. Ngăn chặn CSRF (Cross-Site Request Forgery)
<?php
// Tạo CSRF token
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// Trong form
?>
<form method="POST">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<!-- Các field khác... -->
<button type="submit">Gửi</button>
</form>
<?php
// Xử lý form
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Kiểm tra CSRF token
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die("CSRF token không hợp lệ!");
}
// Xử lý tiếp...
}
?>5.3. Làm sạch dữ liệu (Sanitize)
<?php
// Làm sạch string
$ho_ten = filter_var($_POST['ho_ten'], FILTER_SANITIZE_STRING);
// Làm sạch email
$email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
// Làm sạch URL
$website = filter_var($_POST['website'], FILTER_SANITIZE_URL);
// Làm sạch số
$tuoi = filter_var($_POST['tuoi'], FILTER_SANITIZE_NUMBER_INT);
// Loại bỏ tags HTML
$noi_dung = strip_tags($_POST['noi_dung']);
// Cho phép một số tags
$noi_dung = strip_tags($_POST['noi_dung'], '<p><br><strong>');
?>6. Upload file
6.1. Form upload
<!-- QUAN TRỌNG: Phải có enctype="multipart/form-data" -->
<form action="upload.php" method="POST" enctype="multipart/form-data">
<input type="file" name="avatar">
<button type="submit">Upload</button>
</form>6.2. Xử lý upload
<?php
// upload.php
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Kiểm tra có file upload không
if (isset($_FILES['avatar']) && $_FILES['avatar']['error'] == 0) {
$file = $_FILES['avatar'];
// Thông tin file
$file_name = $file['name']; // avatar.jpg
$file_tmp = $file['tmp_name']; // /tmp/phpXXXXXX
$file_size = $file['size']; // bytes
$file_error = $file['error']; // 0 = OK
// Lấy extension
$file_ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
// 1. Kiểm tra extension
$allowed = ['jpg', 'jpeg', 'png', 'gif'];
if (!in_array($file_ext, $allowed)) {
die("Chỉ cho phép upload: " . implode(", ", $allowed));
}
// 2. Kiểm tra kích thước (2MB)
$max_size = 2 * 1024 * 1024; // 2MB
if ($file_size > $max_size) {
die("File quá lớn! Tối đa 2MB");
}
// 3. Tạo tên file mới (tránh trùng)
$new_name = uniqid() . '.' . $file_ext;
// 4. Đường dẫn lưu
$upload_dir = 'uploads/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0777, true);
}
$upload_path = $upload_dir . $new_name;
// 5. Di chuyển file
if (move_uploaded_file($file_tmp, $upload_path)) {
echo "Upload thành công: $new_name";
} else {
echo "Lỗi khi upload file";
}
} else {
echo "Chưa chọn file hoặc có lỗi";
}
}
?>6.3. Upload nhiều file
<form action="upload-multi.php" method="POST" enctype="multipart/form-data">
<input type="file" name="images[]" multiple>
<button type="submit">Upload</button>
</form><?php
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (isset($_FILES['images'])) {
$files = $_FILES['images'];
$file_count = count($files['name']);
for ($i = 0; $i < $file_count; $i++) {
if ($files['error'][$i] == 0) {
$file_name = $files['name'][$i];
$file_tmp = $files['tmp_name'][$i];
$file_ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
$new_name = uniqid() . '.' . $file_ext;
$upload_path = 'uploads/' . $new_name;
if (move_uploaded_file($file_tmp, $upload_path)) {
echo "Upload thành công: $new_name<br>";
}
}
}
}
}
?>7. Ví dụ hoàn chỉnh: Form đăng ký
<?php
session_start();
// Tạo CSRF token
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$errors = [];
$success = "";
// Xử lý form
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Kiểm tra CSRF
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die("CSRF token không hợp lệ!");
}
// Validate
if (empty($_POST['ho_ten'])) {
$errors['ho_ten'] = "Họ tên không được để trống";
}
if (empty($_POST['email'])) {
$errors['email'] = "Email không được để trống";
} elseif (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
$errors['email'] = "Email không hợp lệ";
}
if (empty($_POST['mat_khau'])) {
$errors['mat_khau'] = "Mật khẩu không được để trống";
} elseif (strlen($_POST['mat_khau']) < 8) {
$errors['mat_khau'] = "Mật khẩu phải ít nhất 8 ký tự";
}
if ($_POST['mat_khau'] !== $_POST['xac_nhan_mat_khau']) {
$errors['xac_nhan_mat_khau'] = "Mật khẩu xác nhận không khớp";
}
// Nếu không có lỗi
if (empty($errors)) {
// Làm sạch dữ liệu
$ho_ten = htmlspecialchars(trim($_POST['ho_ten']));
$email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
$mat_khau = password_hash($_POST['mat_khau'], PASSWORD_DEFAULT);
// Lưu vào database (giả lập)
$success = "Đăng ký thành công với email: $email";
// Tạo token mới
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Đăng ký tài khoản</title>
<style>
.error { color: red; font-size: 14px; }
.success { color: green; padding: 10px; background: #d4edda; }
input { display: block; margin: 10px 0; padding: 8px; width: 300px; }
</style>
</head>
<body>
<h2>Đăng ký tài khoản</h2>
<?php if ($success): ?>
<div class="success"><?= $success ?></div>
<?php endif; ?>
<form method="POST">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<label>Họ tên:</label>
<input type="text" name="ho_ten" value="<?= $_POST['ho_ten'] ?? '' ?>">
<?php if (isset($errors['ho_ten'])): ?>
<span class="error"><?= $errors['ho_ten'] ?></span>
<?php endif; ?>
<label>Email:</label>
<input type="email" name="email" value="<?= $_POST['email'] ?? '' ?>">
<?php if (isset($errors['email'])): ?>
<span class="error"><?= $errors['email'] ?></span>
<?php endif; ?>
<label>Mật khẩu:</label>
<input type="password" name="mat_khau">
<?php if (isset($errors['mat_khau'])): ?>
<span class="error"><?= $errors['mat_khau'] ?></span>
<?php endif; ?>
<label>Xác nhận mật khẩu:</label>
<input type="password" name="xac_nhan_mat_khau">
<?php if (isset($errors['xac_nhan_mat_khau'])): ?>
<span class="error"><?= $errors['xac_nhan_mat_khau'] ?></span>
<?php endif; ?>
<button type="submit">Đăng ký</button>
</form>
</body>
</html>8. Superglobals quan trọng
PHP cung cấp các biến toàn cục để truy cập dữ liệu:
<?php
// $_GET - Dữ liệu từ URL
$id = $_GET['id'] ?? null;
// $_POST - Dữ liệu từ form POST
$email = $_POST['email'] ?? '';
// $_REQUEST - Kết hợp $_GET, $_POST, $_COOKIE
$data = $_REQUEST['data'] ?? null;
// $_FILES - File upload
$avatar = $_FILES['avatar'] ?? null;
// $_SERVER - Thông tin server
$method = $_SERVER['REQUEST_METHOD']; // GET, POST
$url = $_SERVER['REQUEST_URI']; // /tim-kiem.php?q=abc
$ip = $_SERVER['REMOTE_ADDR']; // IP người dùng
$user_agent = $_SERVER['HTTP_USER_AGENT']; // Trình duyệt
// $_SESSION - Lưu dữ liệu giữa các request
session_start();
$_SESSION['user_id'] = 123;
$user_id = $_SESSION['user_id'];
// $_COOKIE - Cookie trình duyệt
setcookie('theme', 'dark', time() + 86400);
$theme = $_COOKIE['theme'] ?? 'light';
?>Tóm tắt
Qua bài này bạn đã nắm được:
- ✅ Form HTML cho phép thu thập dữ liệu từ người dùng với
<form> - ✅ GET: Gửi dữ liệu qua URL, dùng cho tìm kiếm/filter, bookmark được
- ✅ POST: Gửi dữ liệu ẩn, dùng cho đăng ký/đăng nhập, upload file
- ✅ $_GET: Nhận dữ liệu từ URL query string
- ✅ $_POST: Nhận dữ liệu từ form POST
- ✅ $_FILES: Nhận file upload
- ✅ Validate: Bắt buộc để kiểm tra dữ liệu hợp lệ (rỗng, email, độ dài...)
- ✅ Sanitize: Làm sạch dữ liệu với
filter_var(),strip_tags() - ✅ Bảo mật:
htmlspecialchars()chống XSS, CSRF token chống CSRF - ✅ Upload: Kiểm tra extension, size, dùng
move_uploaded_file()
Bài tiếp theo, bạn sẽ học về Làm việc với File.
🎯 Bài tập thực hành
- Bài 1: Tạo form tìm kiếm sản phẩm (GET). Hiển thị "Kết quả tìm kiếm cho: [từ khóa]" và giữ giá trị trong ô input sau khi submit.
- Bài 2: Tạo form đăng ký có validate đầy đủ: họ tên (>3 ký tự), email (đúng format), mật khẩu (>8 ký tự, có chữ hoa, chữ số), xác nhận mật khẩu (khớp).
- Bài 3: Tạo form upload ảnh đại diện. Chỉ cho phép jpg/png, tối đa 1MB, hiển thị ảnh sau khi upload thành công.
Gợi ý Bài 1:
<?php
$tu_khoa = $_GET['q'] ?? '';
?>
<form method="GET">
<input type="text" name="q" value="<?= htmlspecialchars($tu_khoa) ?>" placeholder="Tìm kiếm...">
<button type="submit">Tìm</button>
</form>
<?php if ($tu_khoa): ?>
<h3>Kết quả tìm kiếm cho: <?= htmlspecialchars($tu_khoa) ?></h3>
<!-- Hiển thị kết quả... -->
<?php endif; ?>Gợi ý Bài 2:
<?php
$errors = [];
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Validate họ tên
if (empty($_POST['ho_ten']) || strlen($_POST['ho_ten']) < 3) {
$errors['ho_ten'] = "Họ tên phải ít nhất 3 ký tự";
}
// Validate email
if (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
$errors['email'] = "Email không hợp lệ";
}
// Validate mật khẩu
$pass = $_POST['mat_khau'];
if (strlen($pass) < 8 || !preg_match('/[A-Z]/', $pass) || !preg_match('/[0-9]/', $pass)) {
$errors['mat_khau'] = "Mật khẩu phải >8 ký tự, có chữ hoa và số";
}
// Xác nhận mật khẩu
if ($_POST['mat_khau'] !== $_POST['xac_nhan']) {
$errors['xac_nhan'] = "Mật khẩu xác nhận không khớp";
}
if (empty($errors)) {
echo "Đăng ký thành công!";
}
}
?>Gợi ý Bài 3:
<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['avatar'])) {
$file = $_FILES['avatar'];
if ($file['error'] == 0) {
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
// Kiểm tra extension
if (!in_array($ext, ['jpg', 'png'])) {
echo "Chỉ cho phép JPG/PNG";
exit;
}
// Kiểm tra size (1MB)
if ($file['size'] > 1024 * 1024) {
echo "File quá lớn! Tối đa 1MB";
exit;
}
// Upload
$new_name = uniqid() . '.' . $ext;
move_uploaded_file($file['tmp_name'], 'uploads/' . $new_name);
echo "<img src='uploads/$new_name' width='200'>";
}
}
?>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="avatar">
<button type="submit">Upload</button>
</form>