Làm việc với File

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
CÁC THAO TÁC VỚI FILE TRONG PHP ĐỌC FILE file_get_contents() file() fopen() + fgets() fgetcsv() GHI FILE file_put_contents() fopen() + fwrite() fputcsv() FILE_APPEND KIỂM TRA file_exists() is_file() / is_dir() filesize() is_readable() XÓA / ĐỔI TÊN unlink() rename() copy() rmdir() THƯ MỤC mkdir() scandir() glob() realpath() PHÂN QUYỀN chmod() fileperms() is_writable() is_executable() 💡 Luôn kiểm tra file_exists() trước khi thao tác với file
Hình 1: Các thao tác với file trong PHP

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

CÁC CHẾ ĐỘ MỞ FILE (FILE MODES) Mode Mô tả Con trỏ Tạo file mới? r Chỉ đọc (read-only) Đầu file ❌ Không r+ Đọc và ghi Đầu file ❌ Không w Chỉ ghi (xóa nội dung cũ) Đầu file ✅ Có w+ Đọc và ghi (xóa nội dung cũ) Đầu file ✅ Có a Chỉ ghi (thêm vào cuối) Cuối file ✅ Có a+ Đọc và ghi (thêm vào cuối) Cuối file ✅ Có x Tạo mới (lỗi nếu tồn tại) Đầu file ✅ Có 💡 r = read, w = write (xóa cũ), a = append (thêm cuối) ⚠️ Mode "w" xóa toàn bộ nội dung cũ - Cẩn thận khi dùng! 📝 Dùng "a" cho log file, "w" cho tạo file mới
Hình 2: Các chế độ mở file (File Modes)

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)

ModeMô tảCon trỏTạo file mới?
rChỉ đọcĐầu file
r+Đọc và ghiĐầu file
wChỉ ghi (xóa cũ)Đầu file
w+Đọc và ghi (xóa cũ)Đầu file
aChỉ ghi (thêm cuối)Cuối file
a+Đọc và ghi (thêm cuối)Cuối file
xTạ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)

PHÂN QUYỀN FILE TRÊN LINUX CẤU TRÚC PHÂN QUYỀN: rwxrwxrwx OWNER (Chủ) rwx = 7 GROUP (Nhóm) r-x = 5 OTHERS (Khác) r-- = 4 GIÁ TRỊ QUYỀN r (read) = 4 - Đọc file w (write) = 2 - Ghi/Sửa file x (execute) = 1 - Thực thi file VÍ DỤ PHÂN QUYỀN: 0644 = rw-r--r-- File thường (Owner: đọc+ghi, Khác: chỉ đọc) ⚠️ KHÔNG BAO GIỜ chmod 0777 trên production - Lỗ hổng bảo mật!
Hình 3: Phân quyền file trên Linux
<?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ỉ đọc
  • 0755 = 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

  1. 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.
  2. 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.
  3. 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>

Để lại bình luận

Email của bạn sẽ không được hiển thị.