PHP File Handling

Su Subhalakshmi Chandran Updated 05 Nov 2026
7 min read ·Lesson 243 of 248

What Is File Handling?

In simple terms: File handling means reading from and writing to files on the server's disk — for example, saving a log of errors, reading a CSV of products, storing uploaded images, or caching generated pages.

Technically: PHP's filesystem functions let you create, read, write, append to, copy, rename, and delete files and directories, and query information such as size and modification time. There are two styles: simple whole-file functions (file_get_contents(), file_put_contents()) and stream functions that work piece by piece through a file handle (fopen(), fgets(), fwrite(), fclose()).

Typical Uses

UseExample
LogsAppending error or audit entries
Data import/exportReading and writing CSV or JSON files
UploadsStoring user files (see $_FILES)
CachingSaving generated HTML or API responses
ConfigurationReading settings files
ReportsGenerating text or CSV reports

For data that many users read and change at the same time (orders, accounts), a database is usually the better choice; files are best for logs, imports/exports, media, and caches.


The Two Approaches

Whole-File Functions

<?php
$file = __DIR__ . '/notes.txt';

$bytes = file_put_contents($file, "First line\n");
echo "Wrote $bytes bytes\n";

$bytes = file_put_contents($file, "Second line\n", FILE_APPEND);
echo "Appended $bytes bytes\n";

echo file_get_contents($file);

print_r(file($file, FILE_IGNORE_NEW_LINES));

echo "Size: " . filesize($file) . " bytes\n";

unlink($file);
var_dump(file_exists($file));

Output

Wrote 11 bytes
Appended 12 bytes
First line
Second line
Array
(
    [0] => First line
    [1] => Second line
)
Size: 23 bytes
bool(false)

How It Works

  1. file_put_contents() creates the file (or replaces its contents) and returns the number of bytes written.
  2. With the FILE_APPEND flag, new content is added to the end instead of replacing the file.
  3. file_get_contents() reads the entire file into one string.
  4. file() reads the file into an array of lines; FILE_IGNORE_NEW_LINES removes the line endings.
  5. filesize() returns the size in bytes, and unlink() deletes the file.

These functions open, process, and close the file in one call — ideal for small files.

Stream Functions

<?php
$file = __DIR__ . '/notes.txt';

$handle = fopen($file, 'w');
fwrite($handle, "Line A\n");
fwrite($handle, "Line B\n");
fclose($handle);

$handle = fopen($file, 'r');
while (($line = fgets($handle)) !== false) {
    echo 'Read: ' . rtrim($line) . "\n";
}
fclose($handle);

unlink($file);

Output

Read: Line A
Read: Line B

Stream functions keep a file handle open, so you can read or write in small pieces. This is essential for large files (which should never be loaded into memory at once) and for writing data incrementally. They are covered in detail in PHP File Open/Read and PHP File Create/Write.

Which to Use

SituationUse
Small files (configuration, JSON, templates)file_get_contents() / file_put_contents()
Large files (logs, big CSV exports)fopen() + fgets() / fwrite() in a loop
Appending a line to a logfile_put_contents($file, $line, FILE_APPEND | LOCK_EX)
CSV datafgetcsv() / fputcsv() (see PHP CSV Files)
Object-oriented styleSplFileObject

Checking Files and Getting Information

<?php
$file = __DIR__ . '/report.txt';
file_put_contents($file, str_repeat('x', 2048));

var_dump(file_exists($file));
var_dump(is_file($file));
var_dump(is_dir(__DIR__));
var_dump(is_readable($file), is_writable($file));

echo "Size: " . filesize($file) . " bytes\n";
echo "Modified: " . date('Y-m-d', filemtime($file)) . "\n";
echo "Extension: " . pathinfo($file, PATHINFO_EXTENSION) . "\n";

unlink($file);

Output (the date is the day you run it)

bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
Size: 2048 bytes
Modified: 2026-10-15
Extension: txt
FunctionReturns
file_exists($path)true if a file or directory exists
is_file($path) / is_dir($path)true if it is a regular file / a directory
is_readable() / is_writable()Permission checks for the PHP process
filesize($path)Size in bytes
filemtime($path)Last modification time (Unix timestamp)
pathinfo($path)Directory, file name, base name, and extension
realpath($path)Absolute, normalized path (or false if it does not exist)

Note: PHP caches the results of filesize(), filemtime(), and similar functions during a request. If a file changes during the script and you need fresh information, call clearstatcache() first.


Error Handling

Filesystem functions return false on failure and emit a warning — they do not throw exceptions:

<?php
$content = file_get_contents(__DIR__ . '/does-not-exist.txt');

var_dump($content);

Output

PHP Warning:  file_get_contents(/path/does-not-exist.txt): Failed to open stream: No such file or directory in /path/script.php on line 2
bool(false)

Always check results. In application code, it is common to convert failures into exceptions:

<?php
function readFileOrFail(string $path): string
{
    if (!is_file($path) || !is_readable($path)) {
        throw new RuntimeException("Cannot read file: $path");
    }

    $content = file_get_contents($path);

    if ($content === false) {
        throw new RuntimeException("Failed to read file: $path");
    }

    return $content;
}

Note the strict check === false: an empty file returns '', which is falsy but not an error.


Paths, Permissions, and Security

Build Paths From __DIR__

Relative paths depend on the current working directory, which differs between web requests, cron jobs, and the command line. Build paths from the script's location:

$logFile = __DIR__ . '/../storage/logs/app.log';

Use / as the separator; PHP accepts it on Windows too.

Permissions

The PHP process (for example, the www-data user under Nginx or Apache) needs permission to read or write the files and folders involved. A typical setup gives the web server write access only to specific folders such as storage/ and uploads/, never to the application code.

Never Trust File Names From Users

<?php
function safeStoragePath(string $baseDir, string $fileName): ?string
{
    $name = basename($fileName);

    if (!preg_match('/^[a-zA-Z0-9_-]+\.(txt|csv|json)$/', $name)) {
        return null;
    }

    return rtrim($baseDir, '/') . '/' . $name;
}

var_dump(safeStoragePath('/var/app/storage', 'report-2026.csv'));
var_dump(safeStoragePath('/var/app/storage', '../../config/secrets.php'));

Output

string(32) "/var/app/storage/report-2026.csv"
NULL
  • basename() removes any directory part, so ../../ tricks cannot escape the storage folder (path traversal).
  • An allow-list pattern restricts names to safe characters and expected extensions.
  • Files that users can read or download should be stored outside the public web root and served through PHP after permission checks.

Practical Example: A Simple Application Log

<?php
function writeLog(string $file, string $level, string $message, DateTimeInterface $at): void
{
    $line = sprintf("[%s] %s: %s\n", $at->format('Y-m-d H:i:s'), strtoupper($level), $message);

    if (file_put_contents($file, $line, FILE_APPEND | LOCK_EX) === false) {
        throw new RuntimeException("Cannot write to log file: $file");
    }
}

function lastLogLines(string $file, int $count): array
{
    $lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

    return $lines === false ? [] : array_slice($lines, -$count);
}

$logFile = __DIR__ . '/app.log';
$time = new DateTimeImmutable('2026-10-15 10:00:00');

writeLog($logFile, 'info', 'User asha logged in', $time);
writeLog($logFile, 'error', 'Payment PAY-889 failed', $time->modify('+2 minutes'));
writeLog($logFile, 'info', 'Order 501 created', $time->modify('+5 minutes'));

echo implode("\n", lastLogLines($logFile, 2)) . "\n";

unlink($logFile);

Output

[2026-10-15 10:02:00] ERROR: Payment PAY-889 failed
[2026-10-15 10:05:00] INFO: Order 501 created

Step-by-Step Explanation

  1. Each entry is formatted as one line with a timestamp, a level, and a message.
  2. FILE_APPEND adds to the end of the file; LOCK_EX locks the file during the write, so entries from simultaneous requests are not mixed together.
  3. lastLogLines() reads all lines and returns the last few with array_slice($lines, -$count). (For very large log files, reading from the end with a stream would be more efficient.)
  4. The time is passed in as a parameter, which keeps the function testable.

In production, use an established logging library such as Monolog, which supports log levels, rotation, and sending logs to external services.


Common Mistakes

Not Checking Return Values

Effect: false is treated as file content or a byte count, causing confusing errors later.

Fix: Check with === false.

Loading Huge Files Into Memory

Effect: file_get_contents() or file() on a multi-gigabyte file exceeds the memory limit.

Fix: Read line by line with fgets() or SplFileObject.

Concurrent Writes Without Locking

Effect: Corrupted or interleaved file content.

Fix: Use LOCK_EX or flock().

Using User Input in Paths

Effect: Path traversal and exposure of sensitive files.

Fix: Use basename(), allow-lists, and fixed base directories.

Storing Private Files in the Web Root

Effect: Anyone can download them by URL.

Fix: Store them outside the public folder.


Best Practices

  • Use whole-file functions for small files and streams for large ones.
  • Build paths from __DIR__ and keep writable folders separate from code.
  • Check every return value and convert failures into exceptions in application code.
  • Lock files during concurrent writes.
  • Never build file paths directly from user input.

Official reference: https://www.php.net/manual/en/book.filesystem.php

0 Comments

Reviewed before they appear

No comments yet.

PHP
PHP Home PHP Switch PHP Match Alternative Syntax PHP Loops While Loop Do While Loop For Loop Foreach Loop Nested Loops Break Statement PHP Intro PHP Install PHP Built-in Web Server PHP Syntax PHP Tags PHP and HTML PHP Comments PHP Multiline Comments PHP Variables Variables Scope PHP Casting Logical Operators Null Coalescing Operator Spaceship Operator Operator Precedence Continue Statement Modify Strings PHP If...Else...Elseif PHP If Modify Strings PHP If Operators PHP Functions Concatenate Strings PHP If...Else User-defined Functions Slicing Strings Function Arguments PHP Shorthand if Escape Characters Default Argument Values PHP Nested if Pass by Reference String Interpolation Named Arguments PHP File Upload Variadic Functions Return Values Search in Strings Multibyte Strings Type Declarations in Functions PHP Numbers PHP Math PHP Floats PHP Random Numbers PHP Constants PHP Properties PHP Methods PHP Constructor Constructor Property Promotion PHP Cookies PHP Destructor PHP Access Modifiers Readonly Properties PHP Sessions PHP Inheritance Method Overriding PHP Filters The final Keyword CSRF Protection PHP Filters Advanced Session Security Secure Cookies PHP Magic Constants File Upload Security PHP Callback Functions PHP Operators Filesystem Security PHP JSON Arithmetic Operators Error Reporting in Production Hiding PHP Assignment Operators PHP Configuration (php.ini) Comparison Operators PHP Command Line (CLI) PHP Exceptions Increment / Decrement Operators Migrating to PHP 8 PHP Form Validation PHP Form Required PHP Form URL/E-mail Error Handling Form Input Sanitizing Escaping Output (htmlspecialchars) Custom Error Handler Checkbox, Radio and Select Inputs Sticky Forms PHP Form Complete PHP Date and Time Date Formatting Error Logging HTTP Headers and Redirects Output Buffering Sending Email PHP cURL Consuming REST APIs PHP Generators AJAX PHP AJAX Database AJAX XML AJAX Live Search AJAX Poll AJAX with JSON Security Introduction Password Hashing SQL Injection Prevention XSS Prevention Garbage Collection Project: Simple Blog PHP What is OOP PHP Classes/Objects The $this Keyword Access Array Items Update Array Items Add Array Items Remove Array Items Sorting Arrays Multidimensional Arrays Array Destructuring Array Spread Operator Search in Arrays Merge Arrays PHP Abstract Classes PHP Interfaces Polymorphism PHP Traits PHP Static Methods PHP Static Properties Late Static Binding Magic Methods Object Cloning MySQL Database MySQL Connect MySQL Create DB MySQL Create Table MySQL Insert Data MySQL Get Last ID MySQL Insert Multiple MySQL Prepared MySQL Select Data MySQL Where Debugging PHP Project: Image Upload Gallery $_SESSION $_ENV PHP RegEx RegEx Patterns Heredoc and Nowdoc RegEx Modifiers preg_match preg_replace preg_split PHP RegEx Functions Nullable Types Mixed and Never Types Nullsafe Operator PHP Attributes PHP Fibers Readonly Classes Property Hooks (PHP 8.4) Asymmetric Visibility (PHP 8.4) Reflection API SPL Data Structures Composer PHP Form Handling PDO Transactions PDO Error Handling PHP XML Parsers PHP SimpleXML Parser PHP SimpleXML - Get PHP XML Expat Parser PHP DOM Parser PHP XMLReader PHP XMLWriter AJAX Intro PHP Class Constants Encryption with Sodium Recursive Functions Variable Functions Anonymous Functions Closures Arrow Functions First-class Callable Syntax PHP Arrays Indexed Arrays Associative Arrays Create Arrays Variables Scope Static Variables Variable Variables PHP Echo / Print PHP printf / sprintf PHP Data Types PHP Booleans PHP NULL PHP Strings String Functions array_map / array_filter / array_reduce Array Functions PHP Superglobals $GLOBALS What Is `$_SERVER` ? $_REQUEST $_POST $_GET $_FILES $_COOKIE PHP Type Juggling MySQL Order By MySQL Delete Data MySQL Update Data MySQL Limit Data MySQL Joins with PHP Pagination with PHP PDO Introduction PDO Connect PDO Prepared Statements PDO Fetch Modes PSR-4 Autoloading Installing Extensions (PECL / PIE) OPcache & Performance PHP-FPM Project: Contact Form with Email Project: Login & Registration System Project: CRUD App with MySQL Project: Shopping Cart with Sessions Project: REST API with PHP DateTime Class Timezones PHP Include Include vs Require PHP File Handling PHP File Open/Read PHP File Create/Write File Delete / Rename / Copy PHP Directories PHP CSV Files
Ask about this post
AI Ask about this post

Ask questions about PHP File Handling and get answers drawn from it.

Signed-in readers only.