1. 12:00 06/08/2026
Tiếp Phần 5 (đã có giỏ hàng). Hôm nay làm hệ thống thành viên: đăng ký, đăng nhập, đăng xuất. Bảng users đã tạo sẵn từ Phần 2 rồi nên phần này chủ yếu là xử lý form + mật khẩu.
==============================
1. FILE register.php (đăng ký)
==============================
<?php
require_once __DIR__ . '/includes/functions.php';
if (is_logged_in()) {
redirect('account.php');
}
$page_title = 'Dang ky tai khoan';
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
csrf_verify();
$full_name = trim($_POST['full_name'] ?? '');
$email = trim($_POST['email'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$password = $_POST['password'] ?? '';
$password_confirm = $_POST['password_confirm'] ?? '';
if ($full_name === '' || mb_strlen($full_name) < 2) {
$errors[] = 'Vui long nhap ho ten hop le.';
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Email khong hop le.';
}
if (mb_strlen($password) < 6) {
$errors[] = 'Mat khau phai co it nhat 6 ky tu.';
}
if ($password !== $password_confirm) {
$errors[] = 'Xac nhan mat khau khong khop.';
}
if (empty($errors)) {
$stmt = $pdo->prepare('SELECT id FROM users WHERE email = ?');
$stmt->execute([$email]);
if ($stmt->fetch()) {
$errors[] = 'Email nay da duoc dang ky.';
}
}
if (empty($errors)) {
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare(
'INSERT INTO users (full_name, email, phone, password_hash, role) VALUES (?, ?, ?, ?, "customer")'
);
$stmt->execute([$full_name, $email, $phone, $hash]);
$_SESSION['user_id'] = (int) $pdo->lastInsertId();
$_SESSION['user_name'] = $full_name;
$_SESSION['user_role'] = 'customer';
session_regenerate_id(true);
redirect('account.php');
}
}
require __DIR__ . '/includes/header.php';
?>
<div class="form-box">
<h1 class="page-title">Dang ky tai khoan</h1>
<?php foreach ($errors as $err): ?>
<div class="alert alert-error"><?= e($err) ?></div>
<?php endforeach; ?>
<form action="register.php" method="post">
<?= csrf_field() ?>
<div class="form-group">
<label>Ho ten</label>
<input type="text" name="full_name" value="<?= e($_POST['full_name'] ?? '') ?>" required>
</div>
<div class="form-group">
<label>Email</label>
<input type="email" name="email" value="<?= e($_POST['email'] ?? '') ?>" required>
</div>
<div class="form-group">
<label>So dien thoai</label>
<input type="text" name="phone" value="<?= e($_POST['phone'] ?? '') ?>">
</div>
<div class="form-group">
<label>Mat khau</label>
<input type="password" name="password" required>
</div>
<div class="form-group">
<label>Xac nhan mat khau</label>
<input type="password" name="password_confirm" required>
</div>
<button type="submit" class="btn" style="width:100%;">Dang ky</button>
</form>
<p style="margin-top:14px; text-align:center;">Da co tai khoan? <a href="login.php">Dang nhap</a></p>
</div>
<?php require __DIR__ . '/includes/footer.php'; ?>
==============================
2. FILE login.php (đăng nhập)
==============================
<?php
require_once __DIR__ . '/includes/functions.php';
if (is_logged_in()) {
redirect('account.php');
}
$page_title = 'Dang nhap';
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
csrf_verify();
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? LIMIT 1');
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password_hash'])) {
$_SESSION['user_id'] = (int) $user['id'];
$_SESSION['user_name'] = $user['full_name'];
$_SESSION['user_role'] = $user['role'];
session_regenerate_id(true);
redirect($user['role'] === 'admin' ? 'admin/index.php' : 'account.php');
}
$error = 'Email hoac mat khau khong dung.';
}
require __DIR__ . '/includes/header.php';
?>
<div class="form-box">
<h1 class="page-title">Dang nhap</h1>
<?php if ($error): ?>
<div class="alert alert-error"><?= e($error) ?></div>
<?php endif; ?>
<form action="login.php" method="post">
<?= csrf_field() ?>
<div class="form-group">
<label>Email</label>
<input type="email" name="email" value="<?= e($_POST['email'] ?? '') ?>" required>
</div>
<div class="form-group">
<label>Mat khau</label>
<input type="password" name="password" required>
</div>
<button type="submit" class="btn" style="width:100%;">Dang nhap</button>
</form>
<p style="margin-top:14px; text-align:center;">Chua co tai khoan? <a href="register.php">Dang ky ngay</a></p>
</div>
<?php require __DIR__ . '/includes/footer.php'; ?>
==============================
3. FILE logout.php (đăng xuất)
==============================
<?php
require_once __DIR__ . '/includes/functions.php';
$_SESSION = [];
session_destroy();
redirect('index.php');
==============================
4. FILE account.php (trang tài khoản - khung sườn, Phần 7 sẽ bổ sung lịch sử đơn hàng)
==============================
<?php
require_once __DIR__ . '/includes/functions.php';
require_login();
$page_title = 'Tai khoan cua toi';
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch();
require __DIR__ . '/includes/header.php';
?>
<h1 class="page-title">Xin chao, <?= e($user['full_name']) ?></h1>
<p>Email: <?= e($user['email']) ?></p>
<p>So dien thoai: <?= e($user['phone'] ?: 'Chua cap nhat') ?></p>
<h2 style="margin-top:24px; font-size:18px;">Lich su don hang</h2>
<p><em>(Se hoan thien o Phan 7 sau khi lam xong chuc nang dat hang)</em></p>
<?php require __DIR__ . '/includes/footer.php'; ?>
==============================
GIẢI THÍCH BẢO MẬT QUAN TRỌNG
==============================
- Mật khẩu KHÔNG BAO GIỜ lưu dạng thường (plain text), luôn dùng password_hash() khi lưu và password_verify() khi kiểm tra - đây là chuẩn bắt buộc, tuyệt đối không tự viết hàm mã hoá riêng (md5, sha1 đều KHÔNG an toàn cho mật khẩu).
- session_regenerate_id(true) được gọi ngay sau khi đăng nhập/đăng ký thành công: đổi session ID mới, chống tấn công "session fixation" (kẻ gian gài sẵn 1 session ID rồi dụ nạn nhân đăng nhập vào session đó).
- logout.php xoá sạch $_SESSION rồi mới session_destroy() - xoá cả dữ liệu lẫn session trên server, không chỉ xoá cookie.
- Route phân quyền: sau khi đăng nhập, nếu role là admin thì chuyển thẳng vào admin/index.php, còn customer thì vào account.php - khu vực admin sẽ được chốt chặn kỹ hơn ở Phần 8 bằng require_admin().
Phần 7 tiếp theo là phần quan trọng nhất: xử lý đặt hàng thật sự (tạo đơn hàng, trừ kho, xoá giỏ hàng) và hiển thị lịch sử đơn hàng cho khách. Hẹn gặp lại!
--- Hết Phần 6 ---
----------------------------------------
MỤC LỤC LOẠT BÀI:
1. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 1: Tổng Quan Dự Án, Yêu Cầu Môi Trường & Cấu Trúc Thư Mục - https://diendan.anyvlogs.info/chu-de/457-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-1-tong-quan-du-an-yeu-cau-moi-truong-cau-truc-thu-muc
2. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 2: Thiết Kế Cơ Sở Dữ Liệu (Database) Đầy Đủ Cho Website Bán Hàng - https://diendan.anyvlogs.info/chu-de/458-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-2-thiet-ke-co-so-du-lieu-database-day-du-cho-website-ban-hang
3. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 3: Dựng Khung Ứng Dụng - Kết Nối PDO, Hàm Dùng Chung, Giao Diện Chung - https://diendan.anyvlogs.info/chu-de/459-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-3-dung-khung-ung-dung-ket-noi-pdo-ham-dung-chung-giao-dien-chung
4. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 4: Trang Chủ, Danh Mục Sản Phẩm & Chi Tiết Sản Phẩm - https://diendan.anyvlogs.info/chu-de/460-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-4-trang-chu-danh-muc-san-pham-chi-tiet-san-pham
5. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 5: Xây Dựng Giỏ Hàng (Cart) Bằng Session - https://diendan.anyvlogs.info/chu-de/461-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-5-xay-dung-gio-hang-cart-bang-session
>> 6. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 6: Đăng Ký, Đăng Nhập, Đăng Xuất Thành Viên - https://diendan.anyvlogs.info/chu-de/462-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-6-dang-ky-dang-nhap-dang-xuat-thanh-vien
7. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 7: Đặt Hàng, Thanh Toán COD & Lịch Sử Đơn Hàng - https://diendan.anyvlogs.info/chu-de/463-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-7-dat-hang-thanh-toan-cod-lich-su-don-hang
8. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 8: Trang Quản Trị (Admin) - Quản Lý Danh Mục, Sản Phẩm, Đơn Hàng - https://diendan.anyvlogs.info/chu-de/464-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-8-trang-quan-tri-admin-quan-ly-danh-muc-san-pham-don-hang
9. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 9: Bảo Mật Website (CSRF, XSS, SQL Injection, Upload An Toàn) - https://diendan.anyvlogs.info/chu-de/465-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-9-bao-mat-website-csrf-xss-sql-injection-upload-an-toan
10. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 10 (Kết Thúc): Tối Ưu Hiệu Năng & Triển Khai Lên Hosting Thật - https://diendan.anyvlogs.info/chu-de/466-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-10-ket-thuc-toi-uu-hieu-nang-trien-khai-len-hosting-that
Phần trước: https://diendan.anyvlogs.info/chu-de/461-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-5-xay-dung-gio-hang-cart-bang-session
Phần sau: https://diendan.anyvlogs.info/chu-de/463-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-7-dat-hang-thanh-toan-cod-lich-su-don-hang
0