PHP: File Handling

Databases store structured data; files store unstructured data—logs, config files, CSV reports, user-uploaded images. This lesson gives you full control over files and directories in PHP.

1. Reading Files

PHP
<?php
// Method 1: file_get_contents() — read an entire file in one line
$content = file_get_contents('hello.txt');
echo $content;

// Method 2: fopen + fread — streamed reading (for large files)
$handle = fopen('hello.txt', 'r');
$content = fread($handle, filesize('hello.txt'));
fclose($handle);

// Method 3: Read line by line (most memory-efficient)
$handle = fopen('data.txt', 'r');
while (($line = fgets($handle)) !== false) {
    echo trim($line) . "<br>";
}
fclose($handle);

// Method 4: file() — read all lines into an array at once
$lines = file('data.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
print_r($lines);
?>
Method Best For Memory
file_get_contents() Small files (config, JSON) All at once
fopen + fread Medium files Controlled
fopen + fgets Large files (logs, CSV) Low
file() Small files processed by line All at once

2. Writing Files

PHP
<?php
// file_put_contents() — write a file in one line
file_put_contents('notes.txt', "Today I learned PHP file handling\n");

// Append mode (third parameter: FILE_APPEND)
file_put_contents('notes.txt', "Now I can append content\n", FILE_APPEND);

// Exclusive lock for writing (prevents concurrent conflicts)
file_put_contents('counter.txt', $count, LOCK_EX);

// fwrite — fine-grained control
$handle = fopen('log.txt', 'a');  // 'a' = append mode
fwrite($handle, date("Y-m-d H:i:s") . " — User logged in\n");
fclose($handle);
?>

▶ Example: Simple Logging System

Output:

TEXT 📖 Display only
<h3>Recent Logs</h3><pre>" . implode("
", value->getRecent(10)) . "</pre>
PHP
<?php
class Logger {
    public function __construct(
        private string $logFile
    ) {}
    
    public function info(string $message): void {
        $this->write('INFO', $message);
    }
    
    public function error(string $message): void {
        $this->write('ERROR', $message);
    }
    
    public function warning(string $message): void {
        $this->write('WARNING', $message);
    }
    
    private function write(string $level, string $message): void {
        $time = date("Y-m-d H:i:s");
        $line = "[{$time}] [{$level}] {$message}\n";
        file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
    }
    
    public function getRecent(int $lines = 20): array {
        if (!file_exists($this->logFile)) {
            return [];
        }
        
        $all = file($this->logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        return array_slice($all, -$lines);
    }
}

$log = new Logger('app.log');
$log->info('App started');
$log->warning('Config file not found, using defaults');
$log->error('Database connection timed out');
$log->info('App finished');

echo "<h3>Recent Logs</h3><pre>" . implode("\n", $log->getRecent(10)) . "</pre>";
?>

Output:

TEXT 📖 Display only
[2025-01-15 10:00:00] [INFO] App started
[2025-01-15 10:00:00] [WARNING] Config file not found, using defaults
[2025-01-15 10:00:00] [ERROR] Database connection timed out
[2025-01-15 10:00:00] [INFO] App finished

3. File Detection

PHP
<?php
$file = 'test.txt';

// Existence checks
var_dump(file_exists($file));       // bool
var_dump(is_file($file));           // Also check it's a file (not a directory)
var_dump(is_dir($file));            // Check if it's a directory

// Read/Write checks
var_dump(is_readable($file));      // Can it be read?
var_dump(is_writable($file));      // Can it be written to?

// File information
echo filesize($file) . " bytes<br>";  // Size
echo filemtime($file);                 // Last modified time (Unix timestamp)
echo date("Y-m-d H:i:s", filemtime($file)) . "<br>";  // Formatted date
echo pathinfo($file, PATHINFO_EXTENSION);  // txt
?>

▶ Example: File Cache

Output:

TEXT 📖 Display only
Array
(
    [0] => Alice
    [1] => Bob
    [2] => Charlie
)
PHP
<?php
class FileCache {
    private string $cacheDir;
    
    public function __construct(string $dir = 'cache') {
        $this->cacheDir = $dir;
        if (!is_dir($this->cacheDir)) {
            mkdir($this->cacheDir, 0755, true);
        }
    }
    
    public function get(string $key, int $ttl = 3600): mixed {
        $file = "{$this->cacheDir}/{$key}.cache";
        
        if (!file_exists($file)) return null;
        if (time() - filemtime($file) > $ttl) {
            unlink($file);
            return null;
        }
        
        return unserialize(file_get_contents($file));
    }
    
    public function set(string $key, mixed $value): void {
        $file = "{$this->cacheDir}/{$key}.cache";
        file_put_contents($file, serialize($value), LOCK_EX);
    }
}

$cache = new FileCache();
$cache->set('config', ['app_name' => 'MyBlog', 'version' => '1.0']);
print_r($cache->get('config'));
?>

Output:

TEXT 📖 Display only
Array
(
    [app_name] => MyBlog
    [version] => 1.0
)

4. File Operation Quick Reference

PHP
<?php
$src = 'source.txt';
$dst = 'backup.txt';

// Copy
copy($src, $dst);

// Rename / Move
rename($src, 'new-name.txt');
rename('/tmp/upload.txt', '/var/www/files/upload.txt');  // Move

// Delete
unlink('old-file.txt');

// Directory operations
mkdir('new-folder', 0755, true);  // true = recursive creation
rmdir('empty-folder');             // Only deletes empty directories

// Traverse a directory
foreach (scandir('uploads') as $item) {
    if ($item !== '.' && $item !== '..') {
        echo "{$item} — " . (is_dir("uploads/{$item}") ? 'Directory' : 'File') . "<br>";
    }
}

// Use glob for pattern-based file search
$images = glob('uploads/*.{jpg,png,gif}', GLOB_BRACE);
echo "Found " . count($images) . " images";
?>

5. CSV Reading and Writing

CSV is the universal format for data exchange—Excel can open it, and any language can read and write it:

PHP
<?php
// === Writing CSV ===
$data = [
    ['Name', 'Age', 'City'],
    ['John', 25, 'Beijing'],
    ['Jane', 22, 'Shanghai'],
    ['Bob', 28, 'Guangzhou'],
];

$handle = fopen('users.csv', 'w');
// Write UTF-8 BOM (helps Excel recognize the encoding correctly)
fwrite($handle, "\xEF\xBB\xBF");

foreach ($data as $row) {
    fputcsv($handle, $row);
}
fclose($handle);
echo "CSV file generated ✅<br>";

// === Reading CSV ===
$handle = fopen('users.csv', 'r');
// Skip BOM
fseek($handle, 3);

// Read headers
$headers = fgetcsv($handle);

while (($row = fgetcsv($handle)) !== false) {
    // Use headers as keys
    $item = array_combine($headers, $row);
    echo "{$item['Name']}, {$item['Age']} years old, lives in {$item['City']}<br>";
}
fclose($handle);
?>

▶ Example: CSV Data Export

Output:

TEXT 📖 Display only
(no visible output - function defined)
PHP
<?php
function exportToCSV(array $data, string $filename): void {
    header('Content-Type: text/csv; charset=utf-8');
    header("Content-Disposition: attachment; filename={$filename}");
    
    $output = fopen('php://output', 'w');
    fwrite($output, "\xEF\xBB\xBF");  // BOM
    
    // Headers
    if (!empty($data)) {
        fputcsv($output, array_keys($data[0]));
    }
    
    // Data
    foreach ($data as $row) {
        fputcsv($output, $row);
    }
    fclose($output);
}
// exportToCSV($usersData, 'users_export.csv');
?>

Output:

TEXT 📖 Display only
(no visible output - class defined)

6. File Locking (Preventing Concurrent Writes)

PHP
<?php
class Counter {
    private string $file;
    
    public function __construct(string $file = 'counter.dat') {
        $this->file = $file;
        if (!file_exists($this->file)) {
            file_put_contents($this->file, 0);
        }
    }
    
    public function increment(): int {
        $handle = fopen($this->file, 'c+');
        
        // Acquire an exclusive lock (other processes wait)
        if (flock($handle, LOCK_EX)) {
            $count = (int)fread($handle, 100);
            $count++;
            
            // Go back to the beginning of the file
            fseek($handle, 0);
            ftruncate($handle, 0);  // Clear it
            fwrite($handle, (string)$count);
            
            flock($handle, LOCK_UN);  // Unlock
            fclose($handle);
            return $count;
        }
        fclose($handle);
        return -1;
    }
}
?>

❓ FAQ

Q How do I choose between file_get_contents() and fopen + fgets?
A Use file_get_contents() for small files (<1MB)—one line and you're done. Use fgets() to read line by line for large files (tens of thousands of log lines)—it's memory-friendly. If you don't know the size, start with fopen.
Q Chinese characters appear garbled in Excel?
A Add a UTF-8 BOM ("\xEF\xBB\xBF") at the beginning of the CSV file. Excel reads files using the local encoding by default—the BOM tells it the file is UTF-8.
Q Is file locking with flock actually necessary?
A Absolutely, when multiple processes read and write the same file simultaneously. Counters, caches, and logs can all suffer from race conditions where values get read after another process has already modified them—without locking.

📖 Summary

📝 Exercises

  1. Write a visitor counter: each page refresh increments the count by 1, storing the count in a file.
  2. Write a simple guestbook: a form submits a message → append to a file → read and display all messages from the file (with timestamps).
  3. Export your users table to a CSV file (read data from MySQL → generate a CSV file → provide a download link).
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏