File Handling

PHP can read and write files directly on the server — text logs, CSV exports, cache files, or small notes like the demo below. This page covers opening files with fopen(), reading and writing content, checking whether a file exists before touching it, and cleaning up with fclose() or unlink().

Careful: Never build a file path directly from user input. Always keep uploaded/generated files inside a dedicated folder (like demo-data/ below) so a visitor can never read or overwrite an arbitrary file on your server.

1) Opening a File with fopen()

fopen() opens a file and returns a file handle (a resource) you use for subsequent reads/writes. The second argument is the mode:

ModeMeaning
rRead only. Pointer starts at the beginning. Fails if file doesn't exist.
wWrite only. Erases existing content (or creates the file).
aWrite only, appends to the end. Creates the file if missing.
r+Read and write. Pointer starts at the beginning; doesn't erase content.
<?php
  $handle = fopen('log.txt', 'a');
  if ($handle === false) {
    die('Could not open file.');
  }
  fwrite($handle, "New line\n");
  fclose($handle); // always close what you open
?>

2) Reading Files

For most small/medium files, the simplest approach is file_get_contents() — it opens, reads the whole file, and closes it in one call.

<?php
  $contents = file_get_contents('log.txt');
  echo $contents;
?>
2.1 Reading line-by-line with fgets()
<?php
  $handle = fopen('log.txt', 'r');
  while ($handle && !feof($handle)) {
    $line = fgets($handle);
    echo $line;
  }
  fclose($handle);
?>
2.2 Reading the whole file into an array
<?php
  // Each element is one line, without the trailing newline
  $lines = file('log.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
  $lastFive = array_slice($lines, -5);
?>

3) Writing & Appending with file_put_contents()

file_put_contents() is a shortcut that opens, writes, and closes the file for you in one call — no need to manage a handle manually.

<?php
  // Overwrite the file completely
  file_put_contents('log.txt', "Fresh start\n");

  // Append instead of overwrite, and lock while writing
  file_put_contents('log.txt', "Another line\n", FILE_APPEND | LOCK_EX);
?>
Tip: FILE_APPEND keeps old content and adds to the end (same idea as opening with mode a). Without it, file_put_contents() behaves like mode w and erases what was there.

4) Checking Before You Touch a File

Always check a file's state before reading, writing, or deleting it — this avoids warnings and lets you handle "file not found" gracefully.

<?php
  $path = __DIR__ . '/demo-data/notes.txt';

  if (!file_exists($path)) {
    echo 'No notes saved yet.';
  } elseif (!is_readable($path)) {
    echo 'Notes file exists but cannot be read.';
  } else {
    echo file_get_contents($path);
  }
?>
FunctionReturns
file_exists($path)true if a file or directory exists at that path
is_readable($path)true if PHP has permission to read it
is_writable($path)true if PHP has permission to write to it

5) Deleting a File with unlink()

<?php
  $path = __DIR__ . '/demo-data/notes.txt';
  if (file_exists($path)) {
    unlink($path); // permanently removes the file
  }
?>

6) Working Demo: Save & Read Notes (Real File I/O)

Type a short note below and click Save Note. The server appends it (with a timestamp) to a demo file at php/demo-data/notes.txt using file_put_contents(), then the page reads that file back and shows the last 5 notes — a real, working, server-side file operation, safely contained in its own folder.

Max 200 characters. Stored only in the demo file for this tutorial.

Last 5 Notes

No notes saved yet — be the first!

7) Best Practices & Common Mistakes

IssueProblemFix
Using raw user input in a pathPath traversal (../../etc/passwd)Restrict to a fixed folder; never trust $_GET/$_POST for file paths
Forgetting FILE_APPENDMode w-style writes silently erase old dataUse FILE_APPEND or open with mode a
Not checking file_exists()Warnings / crashes on first runCheck existence before fopen()/file_get_contents()
Concurrent writes without a lockCorrupted/interleaved linesAdd LOCK_EX to file_put_contents()
Leaving handles openResource leaks on busy serversAlways fclose(), or prefer file_get_contents/file_put_contents

8) Practice Tasks

  1. Write a script that appends a visitor's IP address and timestamp to a log file on every load.
  2. Read a log file back and count how many lines it has using count(file($path)).
  3. Write a function safe_read($path) that returns an empty string if the file doesn't exist or isn't readable.
  4. Add a "download notes" link that streams the demo file's contents as a plain-text response.
Next: Continue with Sessions — store data per-visitor on the server between page loads.