Sau khi nắm vững xử lý Form và GET/POST, bước tiếp theo là học cách làm việc với file trong PHP. File là phương thức lưu trữ dữ liệu đơn giản nhất – từ lưu log hệ thống, cấu hình website, cache dữ liệu, đến lưu trữ nội dung tạm thời. Bạn sẽ học cách đọc, ghi, sửa, xóa file, kiểm tra tồn tại, và xử lý file một cách an toàn trong PHP.
1. Tại sao cần làm việc với File?
File giúp bạn lưu trữ và truy xuất dữ liệu mà không cần database:
- ✅ Log hệ thống: Ghi lại lỗi, hoạt động người dùng
- ✅ Cấu hình: Lưu settings trong file .ini, .json, .env
- ✅ Cache: Lưu dữ liệu tạm để tăng tốc độ
- ✅ Upload: Lưu ảnh, PDF, document người dùng upload
- ✅ Export: Xuất báo cáo CSV, TXT
- ✅ Template: Đọc file HTML làm email template
2. Kiểm tra file và thư mục
<?php
$file = "data.txt";
$folder = "uploads";
// Kiểm tra file tồn tại
if (file_exists($file)) {
echo "File tồn tại";
} else {
echo "File không tồn tại";
}
// Kiểm tra là file hay thư mục
if (is_file($file)) {
echo "$file là file";
}
if (is_dir($folder)) {
echo "$folder là thư mục";
}
// Kiểm tra file có thể đọc/ghi
if (is_readable($file)) {
echo "File có thể đọc";
}
if (is_writable($file)) {
echo "File có thể ghi";
}
// Lấy thông tin file
$size = filesize($file); // Kích thước (bytes)
$modified = filemtime($file); // Thời gian sửa cuối
$accessed = fileatime($file); // Thời gian truy cập cuối
$created = filectime($file); // Thời gian tạo
echo "Kích thước: " . $size . " bytes<br>";
echo "Sửa lần cuối: " . date("d/m/Y H:i:s", $modified);
?>3. Đọc file
3.1. Đọc toàn bộ file một lần
<?php
// Cách 1: file_get_contents() - Đơn giản nhất
$content = file_get_contents("data.txt");
echo $content;
// Cách 2: file() - Đọc thành mảng (mỗi dòng = 1 phần tử)
$lines = file("data.txt");
foreach ($lines as $line_num => $line) {
echo "Dòng $line_num: $line<br>";
}
// Cách 3: readfile() - Đọc và in ra luôn
readfile("data.txt");
// Xử lý lỗi
if (file_exists("data.txt")) {
$content = file_get_contents("data.txt");
echo $content;
} else {
echo "File không tồn tại";
}
?>3.2. Đọc file từng dòng (file lớn)
<?php
// Mở file
$file = fopen("large.txt", "r") or die("Không thể mở file");
// Đọc từng dòng
while (!feof($file)) {
$line = fgets($file); // Đọc 1 dòng
echo $line . "<br>";
}
// Đóng file
fclose($file);
// Hoặc đọc từng ký tự
$file = fopen("data.txt", "r");
while (!feof($file)) {
$char = fgetc($file); // Đọc 1 ký tự
echo $char;
}
fclose($file);
?>3.3. Đọc file CSV
<?php
// products.csv:
// Ten,Gia,So_luong
// Laptop,15000000,10
// Chuot,200000,50
$file = fopen("products.csv", "r");
// Đọc header
$header = fgetcsv($file);
print_r($header); // ["Ten", "Gia", "So_luong"]
// Đọc từng dòng
while (($row = fgetcsv($file)) !== false) {
echo "Tên: " . $row[0] . " - Giá: " . $row[1] . "<br>";
}
fclose($file);
// Hoặc đọc toàn bộ
$data = [];
$file = fopen("products.csv", "r");
while (($row = fgetcsv($file)) !== false) {
$data[] = $row;
}
fclose($file);
print_r($data);
?>4. Ghi file
4.1. Ghi đè file (ghi mới)
<?php
// Cách 1: file_put_contents() - Đơn giản nhất
$content = "Đây là nội dung mới\nDòng 2\nDòng 3";
file_put_contents("output.txt", $content);
// Cách 2: fopen() + fwrite()
$file = fopen("output.txt", "w") or die("Không thể tạo file");
fwrite($file, "Dòng 1\n");
fwrite($file, "Dòng 2\n");
fclose($file);
echo "Ghi file thành công";
?>4.2. Ghi thêm vào cuối file (append)
<?php
// FILE_APPEND - Ghi thêm, không xóa nội dung cũ
$log = date("Y-m-d H:i:s") . " - Người dùng đăng nhập\n";
file_put_contents("log.txt", $log, FILE_APPEND);
// Hoặc dùng fopen với mode "a"
$file = fopen("log.txt", "a");
fwrite($file, $log);
fclose($file);
?>4.3. Ghi file CSV
<?php
$products = [
["Laptop", 15000000, 10],
["Chuột", 200000, 50],
["Bàn phím", 500000, 30]
];
$file = fopen("export.csv", "w");
// Ghi header
fputcsv($file, ["Tên sản phẩm", "Giá", "Số lượng"]);
// Ghi dữ liệu
foreach ($products as $product) {
fputcsv($file, $product);
}
fclose($file);
echo "Xuất CSV thành công";
?>4.4. Ghi file JSON
<?php
$data = [
"name" => "Nguyễn Văn A",
"age" => 20,
"email" => "test@email.com",
"skills" => ["PHP", "JavaScript", "MySQL"]
];
// Chuyển thành JSON và ghi file
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
file_put_contents("user.json", $json);
// Đọc lại
$json_content = file_get_contents("user.json");
$user = json_decode($json_content, true);
echo "Tên: " . $user['name'];
echo "Email: " . $user['email'];
?>5. Các chế độ mở file (File Modes)
| Mode | Mô tả | Con trỏ | Tạo file mới? |
|---|---|---|---|
r | Chỉ đọc | Đầu file | ❌ |
r+ | Đọc và ghi | Đầu file | ❌ |
w | Chỉ ghi (xóa cũ) | Đầu file | ✅ |
w+ | Đọc và ghi (xóa cũ) | Đầu file | ✅ |
a | Chỉ ghi (thêm cuối) | Cuối file | ✅ |
a+ | Đọc và ghi (thêm cuối) | Cuối file | ✅ |
x | Tạo file mới (lỗi nếu tồn tại) | Đầu file | ✅ |
x+ | Tạo mới + đọc ghi | Đầu file | ✅ |
<?php
// r - Chỉ đọc
$file = fopen("data.txt", "r");
// w - Ghi mới (xóa cũ)
$file = fopen("output.txt", "w");
// a - Ghi thêm cuối file
$file = fopen("log.txt", "a");
// x - Tạo file mới (lỗi nếu đã tồn tại)
$file = fopen("new.txt", "x");
if ($file === false) {
echo "File đã tồn tại!";
}
?>6. Sửa file
<?php
// Đọc file
$content = file_get_contents("data.txt");
// Sửa nội dung
$content = str_replace("cũ", "mới", $content);
// Ghi lại
file_put_contents("data.txt", $content);
// Ví dụ: Tìm và thay thế trong file cấu hình
$config = file_get_contents("config.ini");
$config = str_replace("debug=false", "debug=true", $config);
file_put_contents("config.ini", $config);
?>7. Xóa và đổi tên file
<?php
// Xóa file
if (file_exists("temp.txt")) {
unlink("temp.txt");
echo "Đã xóa file";
} else {
echo "File không tồn tại";
}
// Đổi tên file
rename("old_name.txt", "new_name.txt");
// Di chuyển file
rename("uploads/file.txt", "archive/file.txt");
// Sao chép file
copy("source.txt", "destination.txt");
?>8. Làm việc với thư mục
<?php
// Tạo thư mục
if (!is_dir("uploads")) {
mkdir("uploads", 0777, true); // 0777 = full permission, true = tạo đệ quy
echo "Đã tạo thư mục";
}
// Xóa thư mục (phải rỗng)
if (is_dir("temp") && count(scandir("temp")) == 2) {
rmdir("temp");
echo "Đã xóa thư mục";
}
// Liệt kê file trong thư mục
$files = scandir("uploads");
foreach ($files as $file) {
if ($file != "." && $file != "..") {
echo $file . "<br>";
}
}
// Hoặc dùng glob()
$files = glob("uploads/*.{jpg,png,gif}", GLOB_BRACE);
foreach ($files as $file) {
echo basename($file) . "<br>";
}
// Xóa thư mục có nội dung
function deleteDirectory($dir) {
if (!is_dir($dir)) return false;
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = $dir . '/' . $file;
is_dir($path) ? deleteDirectory($path) : unlink($path);
}
return rmdir($dir);
}
deleteDirectory("old_folder");
?>9. Phân quyền file (Permissions)
<?php
// Đọc quyền hiện tại
$perms = fileperms("data.txt");
echo substr(sprintf('%o', $perms), -4); // 0644
// Đổi quyền (chmod)
chmod("data.txt", 0644); // rw-r--r--
chmod("script.sh", 0755); // rwxr-xr-x
// Quyền thường dùng:
// 0644 - File thông thường (rw-r--r--)
// 0755 - File thực thi (rwxr-xr-x)
// 0777 - Full quyền (rwxrwxrwx) - NGUY HIỂM!
// Kiểm tra quyền
if (is_readable("data.txt")) {
echo "File có thể đọc";
}
if (is_writable("data.txt")) {
echo "File có thể ghi";
}
if (is_executable("script.sh")) {
echo "File có thể thực thi";
}
?>Giải thích quyền:
r (4)– Read (đọc)w (2)– Write (ghi)x (1)– Execute (thực thi)0644 = rw-r--r--– Owner: đọc+ghi, Group: chỉ đọc, Others: chỉ đọc0755 = rwxr-xr-x– Owner: full, Group+Others: đọc+thực thi
⚠️ Lưu ý: Không bao giờ chmod 0777 trên production! Đây là lỗ hổng bảo mật nghiêm trọng.
10. Ví dụ thực tế
10.1. Hệ thống log đơn giản
<?php
function writeLog($message, $level = "INFO") {
$log_file = "logs/app.log";
// Tạo thư mục nếu chưa có
if (!is_dir("logs")) {
mkdir("logs", 0755, true);
}
// Format log
$timestamp = date("Y-m-d H:i:s");
$log_entry = "[$timestamp] [$level] $message\n";
// Ghi log
file_put_contents($log_file, $log_entry, FILE_APPEND);
}
// Sử dụng
writeLog("Người dùng đăng nhập", "INFO");
writeLog("Không thể kết nối database", "ERROR");
writeLog("Xử lý thanh toán", "DEBUG");
// Đọc log
$logs = file("logs/app.log");
foreach (array_reverse($logs) as $log) {
echo htmlspecialchars($log) . "<br>";
}
?>10.2. Counter đếm lượt truy cập
<?php
$counter_file = "counter.txt";
// Tạo file nếu chưa có
if (!file_exists($counter_file)) {
file_put_contents($counter_file, "0");
}
// Đọc số lượt hiện tại
$count = (int)file_get_contents($counter_file);
// Tăng lên 1
$count++;
// Ghi lại
file_put_contents($counter_file, $count);
echo "Lượt truy cập: " . number_format($count);
?>10.3. Cache đơn giản
<?php
function getCache($key) {
$cache_file = "cache/$key.cache";
if (file_exists($cache_file)) {
$cache_time = filemtime($cache_file);
$expire = 3600; // 1 giờ
if (time() - $cache_time < $expire) {
return unserialize(file_get_contents($cache_file));
}
}
return null;
}
function setCache($key, $data) {
if (!is_dir("cache")) {
mkdir("cache", 0755, true);
}
$cache_file = "cache/$key.cache";
file_put_contents($cache_file, serialize($data));
}
// Sử dụng
$products = getCache("products");
if ($products === null) {
// Lấy từ database
$products = [
["id" => 1, "name" => "Laptop"],
["id" => 2, "name" => "Chuột"]
];
// Lưu cache
setCache("products", $products);
}
print_r($products);
?>10.4. Export danh sách ra CSV
<?php
$users = [
["ID" => 1, "Tên" => "Nguyễn Văn A", "Email" => "a@email.com"],
["ID" => 2, "Tên" => "Trần Thị B", "Email" => "b@email.com"],
["ID" => 3, "Tên" => "Lê Văn C", "Email" => "c@email.com"]
];
// Tạo file CSV
$filename = "users_" . date("Y-m-d") . ".csv";
$file = fopen($filename, "w");
// BOM UTF-8 (để Excel hiển thị đúng tiếng Việt)
fprintf($file, chr(0xEF).chr(0xBB).chr(0xBF));
// Ghi header
fputcsv($file, array_keys($users[0]));
// Ghi dữ liệu
foreach ($users as $user) {
fputcsv($file, $user);
}
fclose($file);
// Download file
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $filename . '"');
readfile($filename);
// Xóa file tạm
unlink($filename);
?>10.5. Đọc file cấu hình .env
<?php
// .env file:
// DB_HOST=localhost
// DB_USER=root
// DB_PASS=secret
// DB_NAME=mydb
function loadEnv($file) {
if (!file_exists($file)) {
return [];
}
$env = [];
$lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
// Bỏ qua comment
if (strpos(trim($line), '#') === 0) {
continue;
}
// Tách key = value
list($key, $value) = explode('=', $line, 2);
$env[trim($key)] = trim($value);
}
return $env;
}
// Sử dụng
$config = loadEnv(".env");
echo "Database: " . $config['DB_HOST'];
echo "Username: " . $config['DB_USER'];
?>11. Bảo mật khi làm việc với file
11.1. Validate đường dẫn file
<?php
// ❌ NGUY HIỂM - Path Traversal Attack
$file = $_GET['file']; // ../../etc/passwd
$content = file_get_contents($file);
// ✅ AN TOÀN - Validate đường dẫn
$file = $_GET['file'];
$allowed_dir = realpath("uploads");
$file_path = realpath("uploads/" . $file);
// Kiểm tra file có nằm trong thư mục cho phép không
if (strpos($file_path, $allowed_dir) !== 0) {
die("Truy cập bị từ chối!");
}
// Kiểm tra extension
$allowed_ext = ['txt', 'pdf', 'jpg', 'png'];
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
if (!in_array($ext, $allowed_ext)) {
die("File không được phép!");
}
$content = file_get_contents($file_path);
?>11.2. Giới hạn kích thước file
<?php
$max_size = 5 * 1024 * 1024; // 5MB
if (filesize($file) > $max_size) {
die("File quá lớn!");
}
?>11.3. Không lưu file với tên gốc
<?php
// ❌ NGUY HIỂM
$filename = $_FILES['file']['name']; // malicious.php
move_uploaded_file($_FILES['file']['tmp_name'], "uploads/$filename");
// ✅ AN TOÀN - Đổi tên file
$ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
$new_name = uniqid() . '.' . $ext;
move_uploaded_file($_FILES['file']['tmp_name'], "uploads/$new_name");
?>Tóm tắt
Qua bài này bạn đã nắm được:
- ✅ Kiểm tra file:
file_exists(),is_file(),is_dir(),filesize() - ✅ Đọc file:
file_get_contents(),file(),fopen()+fgets() - ✅ Ghi file:
file_put_contents(),fopen()+fwrite() - ✅ File modes:
r(đọc),w(ghi mới),a(ghi thêm) - ✅ CSV:
fgetcsv()đọc,fputcsv()ghi - ✅ JSON:
json_encode()+file_put_contents() - ✅ Xóa/Đổi tên:
unlink(),rename(),copy() - ✅ Thư mục:
mkdir(),rmdir(),scandir() - ✅ Phân quyền:
chmod()– 0644 (file), 0755 (thư mục/script) - ✅ Bảo mật: Validate đường dẫn, kiểm tra extension, đổi tên file upload
Bài tiếp theo, bạn sẽ học về Kết nối MySQL trong PHP để làm việc với database.
🎯 Bài tập thực hành
- Bài 1: Tạo form ghi guestbook. Mỗi lần submit, lưu thông tin (tên, nội dung, thời gian) vào file
guestbook.txt. Hiển thị danh sách guestbook từ file. - Bài 2: Viết hàm
countWords($file)đếm số từ trong file text. Hiển thị top 10 từ xuất hiện nhiều nhất. - Bài 3: Tạo hệ thống tải file đơn giản: upload file (chỉ cho phép PDF, max 2MB), lưu vào thư mục
uploads/, hiển thị danh sách file đã upload với link download.
Gợi ý Bài 1:
<?php
$file = "guestbook.txt";
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$name = htmlspecialchars($_POST['name']);
$message = htmlspecialchars($_POST['message']);
$time = date("Y-m-d H:i:s");
$entry = "$time | $name: $message\n";
file_put_contents($file, $entry, FILE_APPEND);
}
// Hiển thị
if (file_exists($file)) {
$entries = file($file);
foreach (array_reverse($entries) as $entry) {
echo htmlspecialchars($entry) . "<br>";
}
}
?>
<form method="POST">
<input type="text" name="name" placeholder="Tên" required>
<textarea name="message" placeholder="Nội dung" required></textarea>
<button type="submit">Gửi</button>
</form>Gợi ý Bài 2:
<?php
function countWords($file) {
$content = file_get_contents($file);
$content = strtolower($content);
$words = str_word_count($content, 1);
$word_count = array_count_values($words);
arsort($word_count);
return array_slice($word_count, 0, 10);
}
$top_words = countWords("article.txt");
foreach ($top_words as $word => $count) {
echo "$word: $count lần<br>";
}
?>Gợi ý Bài 3:
<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['file'])) {
$file = $_FILES['file'];
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
// Validate
if ($ext != 'pdf') {
echo "Chỉ cho phép PDF";
exit;
}
if ($file['size'] > 2 * 1024 * 1024) {
echo "File quá lớn! Tối đa 2MB";
exit;
}
// Upload
if (!is_dir("uploads")) {
mkdir("uploads", 0755);
}
$new_name = uniqid() . '.pdf';
move_uploaded_file($file['tmp_name'], "uploads/$new_name");
echo "Upload thành công!";
}
// Hiển thị danh sách
$files = glob("uploads/*.pdf");
foreach ($files as $file) {
$name = basename($file);
echo "<a href='$file' download>$name</a><br>";
}
?>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="file" accept=".pdf">
<button type="submit">Upload</button>
</form>