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);
?>

▶ サンプル: Simple Logging System

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>";
?>
▶ 試してみよう

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
?>

▶ サンプル: File Cache

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'));
?>
▶ 試してみよう

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);
?>

▶ サンプル: CSV Data Export

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');
?>
▶ 試してみよう

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;
    }
}
?>

❓ よくある質問

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.

📖 まとめ

📝 練習問題

  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 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%