Skip to content

File Handling

Overview

PHP provides powerful file handling capabilities that allow you to read, write, create, and manipulate files and directories. This chapter covers file I/O operations, directory management, file uploads, permission handling, and important topics related to filesystem security.

Basic File Operations

File Reading

php
<?php
// Method 1: Read entire file content
$content = file_get_contents('data.txt');
if ($content !== false) {
    echo $content;
} else {
    echo "Cannot read file";
}

// Method 2: Using fopen and fread
$file = fopen('data.txt', 'r');
if ($file) {
    $content = fread($file, filesize('data.txt'));
    fclose($file);
    echo $content;
} else {
    echo "Cannot open file";
}

// Method 3: Read line by line
$file = fopen('data.txt', 'r');
if ($file) {
    while (($line = fgets($file)) !== false) {
        echo "Line: " . $line;
    }
    fclose($file);
}

// Method 4: Read as array
$lines = file('data.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $lineNumber => $line) {
    echo "Line " . ($lineNumber + 1) . ": $line\n";
}
?>

File Writing

php
<?php
// Method 1: Write entire content (overwrite)
file_put_contents('output.txt', 'This is new file content');

// Method 2: Append content
file_put_contents('output.txt', '\nAppended content', FILE_APPEND | LOCK_EX);

// Method 3: Using fopen and fwrite
$file = fopen('output.txt', 'w');
if ($file) {
    fwrite($file, 'Content written using fwrite');
    fclose($file);
}

// Method 4: Write CSV data
$data = [
    ['Name', 'Age', 'City'],
    ['Zhang San', 25, 'Beijing'],
    ['Li Si', 30, 'Shanghai'],
    ['Wang Wu', 28, 'Guangzhou']
];

$file = fopen('users.csv', 'w');
foreach ($data as $row) {
    fputcsv($file, $row);
}
fclose($file);

// Method 5: Safe write (atomic operation)
function safeFileWrite($filename, $content) {
    $tempFile = $filename . '.tmp';
    if (file_put_contents($tempFile, $content, LOCK_EX) !== false) {
        return rename($tempFile, $filename);
    }
    return false;
}

safeFileWrite('important.txt', 'Important data');
?>

File Information and Checks

php
<?php
// Check if file exists
if (file_exists('data.txt')) {
    echo "File exists";
}

// Get file information
$filename = 'data.txt';
if (file_exists($filename)) {
    echo "File size: " . filesize($filename) . " bytes\n";
    echo "Last modified: " . date('Y-m-d H:i:s', filemtime($filename)) . "\n";
    echo "Last accessed: " . date('Y-m-d H:i:s', fileatime($filename)) . "\n";
    echo "Is readable: " . (is_readable($filename) ? 'Yes' : 'No') . "\n";
    echo "Is writable: " . (is_writable($filename) ? 'Yes' : 'No') . "\n";
    echo "Is file: " . (is_file($filename) ? 'Yes' : 'No') . "\n";
    echo "Is directory: " . (is_dir($filename) ? 'Yes' : 'No') . "\n";
}

// Get detailed file information
$stat = stat('data.txt');
print_r($stat);

// Get file extension and path information
$path = '/path/to/document.pdf';
echo "Directory name: " . dirname($path) . "\n";
echo "File name: " . basename($path) . "\n";
echo "Extension: " . pathinfo($path, PATHINFO_EXTENSION) . "\n";
echo "Filename without extension: " . pathinfo($path, PATHINFO_FILENAME) . "\n";

// Get file MIME type
if (function_exists('mime_content_type')) {
    echo "MIME type: " . mime_content_type($filename) . "\n";
}

// Using pathinfo to get file path information
$pathInfo = pathinfo('/path/to/file.txt');
echo "Directory: " . $pathInfo['dirname'] . "\n";
echo "Filename: " . $pathInfo['basename'] . "\n";
echo "Extension: " . $pathInfo['extension'] . "\n";
echo "Filename without extension: " . $pathInfo['filename'] . "\n";
?>

Directory Operations

Creating and Deleting Directories

php
<?php
// Create directory
if (!is_dir('uploads')) {
    mkdir('uploads', 0755, true); // Recursive creation, set permissions
    echo "Directory created successfully";
}

// Create multi-level directories
mkdir('project/src/controllers', 0755, true);

// Delete directory (must be empty)
if (is_dir('temp') && rmdir('temp')) {
    echo "Directory deleted successfully";
}

// Recursively delete directory and all contents
function deleteDirectory($dir) {
    if (!is_dir($dir)) {
        return false;
    }
    
    $files = array_diff(scandir($dir), ['.', '..']);
    foreach ($files as $file) {
        $path = $dir . DIRECTORY_SEPARATOR . $file;
        is_dir($path) ? deleteDirectory($path) : unlink($path);
    }
    
    return rmdir($dir);
}

deleteDirectory('old_project');
?>

Directory Traversal

php
<?php
// Method 1: Using scandir
$files = scandir('.');
foreach ($files as $file) {
    if ($file != '.' && $file != '..') {
        echo $file . "\n";
    }
}

// Method 2: Using glob
$phpFiles = glob('*.php');
foreach ($phpFiles as $file) {
    echo "PHP file: $file\n";
}

// Find all PHP files in subdirectories
$allPhpFiles = glob('**/*.php', GLOB_BRACE);

// Method 3: Using DirectoryIterator
$iterator = new DirectoryIterator('.');
foreach ($iterator as $fileInfo) {
    if (!$fileInfo->isDot()) {
        echo $fileInfo->getFilename() . " (" . 
             ($fileInfo->isDir() ? 'Directory' : 'File') . ")\n";
    }
}

// Method 4: Recursive traversal
$recursiveIterator = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator('.'),
    RecursiveIteratorIterator::SELF_FIRST
);

foreach ($recursiveIterator as $file) {
    echo str_repeat('  ', $recursiveIterator->getDepth()) . 
         $file->getFilename() . "\n";
}
?>

File Upload Handling

Basic File Upload

php
<?php
// HTML form example
/*
<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="uploadFile" required>
    <input type="submit" value="Upload File">
</form>
*/

// Handle file upload
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['uploadFile'])) {
    $uploadDir = 'uploads/';
    $uploadFile = $uploadDir . basename($_FILES['uploadFile']['name']);
    
    // Basic validation
    if ($_FILES['uploadFile']['error'] === UPLOAD_ERR_OK) {
        if (move_uploaded_file($_FILES['uploadFile']['tmp_name'], $uploadFile)) {
            echo "File uploaded successfully: " . $uploadFile;
        } else {
            echo "File upload failed";
        }
    } else {
        echo "Upload error code: " . $_FILES['uploadFile']['error'];
    }
}
?>

Advanced File Upload Handling

php
<?php
class FileUploader {
    private $uploadDir;
    private $maxFileSize;
    private $allowedTypes;
    private $allowedExtensions;
    
    public function __construct($uploadDir = 'uploads/', $maxFileSize = 2097152) { // 2MB
        $this->uploadDir = rtrim($uploadDir, '/') . '/';
        $this->maxFileSize = $maxFileSize;
        $this->allowedTypes = [
            'image/jpeg',
            'image/png',
            'image/gif',
            'application/pdf',
            'text/plain'
        ];
        $this->allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'txt'];
        
        // Ensure upload directory exists
        if (!is_dir($this->uploadDir)) {
            mkdir($this->uploadDir, 0755, true);
        }
    }
    
    public function upload($fileInput) {
        if (!isset($_FILES[$fileInput])) {
            return ['success' => false, 'message' => 'No file selected'];
        }
        
        $file = $_FILES[$fileInput];
        
        // Check upload error
        if ($file['error'] !== UPLOAD_ERR_OK) {
            return ['success' => false, 'message' => $this->getUploadErrorMessage($file['error'])];
        }
        
        // Validate file size
        if ($file['size'] > $this->maxFileSize) {
            return ['success' => false, 'message' => 'File size exceeds limit'];
        }
        
        // Validate file type
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mimeType = finfo_file($finfo, $file['tmp_name']);
        finfo_close($finfo);
        
        if (!in_array($mimeType, $this->allowedTypes)) {
            return ['success' => false, 'message' => 'Unsupported file type'];
        }
        
        // Validate file extension
        $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        if (!in_array($extension, $this->allowedExtensions)) {
            return ['success' => false, 'message' => 'Unsupported file extension'];
        }
        
        // Generate safe filename
        $filename = $this->generateSafeFilename($file['name']);
        $filepath = $this->uploadDir . $filename;
        
        // Move file
        if (move_uploaded_file($file['tmp_name'], $filepath)) {
            return [
                'success' => true,
                'message' => 'File uploaded successfully',
                'filename' => $filename,
                'filepath' => $filepath,
                'size' => $file['size'],
                'type' => $mimeType
            ];
        } else {
            return ['success' => false, 'message' => 'File move failed'];
        }
    }
    
    private function generateSafeFilename($originalName) {
        $extension = pathinfo($originalName, PATHINFO_EXTENSION);
        $basename = pathinfo($originalName, PATHINFO_FILENAME);
        
        // Clean filename
        $basename = preg_replace('/[^a-zA-Z0-9_-]/', '_', $basename);
        $basename = substr($basename, 0, 50); // Limit length
        
        // Add timestamp to avoid duplicates
        $timestamp = time();
        
        return $basename . '_' . $timestamp . '.' . $extension;
    }
    
    private function getUploadErrorMessage($errorCode) {
        switch ($errorCode) {
            case UPLOAD_ERR_INI_SIZE:
                return 'File size exceeds upload_max_filesize setting in php.ini';
            case UPLOAD_ERR_FORM_SIZE:
                return 'File size exceeds MAX_FILE_SIZE setting in form';
            case UPLOAD_ERR_PARTIAL:
                return 'File was only partially uploaded';
            case UPLOAD_ERR_NO_FILE:
                return 'No file was uploaded';
            case UPLOAD_ERR_NO_TMP_DIR:
                return 'Missing temporary directory';
            case UPLOAD_ERR_CANT_WRITE:
                return 'Failed to write file';
            case UPLOAD_ERR_EXTENSION:
                return 'Upload blocked by PHP extension';
            default:
                return 'Unknown upload error';
        }
    }
}

// Usage example
$uploader = new FileUploader('uploads/', 5242880); // 5MB
$result = $uploader->upload('uploadFile');

if ($result['success']) {
    echo "Upload successful: " . $result['filename'];
} else {
    echo "Upload failed: " . $result['message'];
}
?>

File Permissions and Security

File Permission Management

php
<?php
// Check and set file permissions
$file = 'config.txt';

// Get current permissions
$perms = fileperms($file);
echo "Current permissions: " . decoct($perms & 0777) . "\n";

// Set permissions
chmod($file, 0644); // Owner read/write, group and others read-only
chmod('script.sh', 0755); // Owner read/write/execute, group and others read/execute

// Change file owner (requires appropriate permissions)
// chown($file, 'www-data');
// chgrp($file, 'www-data');

// Permission check function
function checkFilePermissions($file) {
    if (!file_exists($file)) {
        return "File does not exist";
    }
    
    $perms = fileperms($file);
    $info = '';
    
    // File type
    if (($perms & 0xC000) == 0xC000) {
        $info = 's'; // Socket
    } elseif (($perms & 0xA000) == 0xA000) {
        $info = 'l'; // Symbolic link
    } elseif (($perms & 0x8000) == 0x8000) {
        $info = '-'; // Regular file
    } elseif (($perms & 0x6000) == 0x6000) {
        $info = 'b'; // Block device
    } elseif (($perms & 0x4000) == 0x4000) {
        $info = 'd'; // Directory
    } elseif (($perms & 0x2000) == 0x2000) {
        $info = 'c'; // Character device
    } elseif (($perms & 0x1000) == 0x1000) {
        $info = 'p'; // FIFO pipe
    } else {
        $info = 'u'; // Unknown
    }
    
    // Permissions
    $info .= (($perms & 0x0100) ? 'r' : '-');
    $info .= (($perms & 0x0080) ? 'w' : '-');
    $info .= (($perms & 0x0040) ?
        (($perms & 0x0800) ? 's' : 'x') :
        (($perms & 0x0800) ? 'S' : '-'));
    
    $info .= (($perms & 0x0020) ? 'r' : '-');
    $info .= (($perms & 0x0010) ? 'w' : '-');
    $info .= (($perms & 0x0008) ?
        (($perms & 0x0400) ? 's' : 'x') :
        (($perms & 0x0400) ? 'S' : '-'));
    
    $info .= (($perms & 0x0004) ? 'r' : '-');
    $info .= (($perms & 0x0002) ? 'w' : '-');
    $info .= (($perms & 0x0001) ?
        (($perms & 0x0200) ? 't' : 'x') :
        (($perms & 0x0200) ? 'T' : '-'));
    
    return $info;
}

echo checkFilePermissions('data.txt');
?>

Secure File Operations

php
<?php
// Secure path validation
function isValidPath($path, $basePath = '.') {
    $realPath = realpath($path);
    $realBasePath = realpath($basePath);
    
    // Check if path is within base path
    return $realPath !== false && 
           $realBasePath !== false && 
           strpos($realPath, $realBasePath) === 0;
}

// Secure filename validation
function sanitizeFilename($filename) {
    // Remove dangerous characters
    $filename = preg_replace('/[^a-zA-Z0-9._-]/', '', $filename);
    
    // Prevent directory traversal attacks
    $filename = str_replace(['../', '..\\', './'], '', $filename);
    
    return $filename;
}

// Secure file reader class
class SecureFileReader {
    private $basePath;
    private $allowedExtensions;
    
    public function __construct($basePath, $allowedExtensions = ['txt', 'json', 'csv']) {
        $this->basePath = realpath($basePath);
        $this->allowedExtensions = $allowedExtensions;
    }
    
    public function readFile($filename) {
        // Validate filename
        $filename = sanitizeFilename($filename);
        
        // Build full path
        $fullPath = $this->basePath . DIRECTORY_SEPARATOR . $filename;
        
        // Validate path security
        if (!isValidPath($fullPath, $this->basePath)) {
            throw new Exception('Invalid file path');
        }
        
        // Validate file extension
        $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
        if (!in_array($extension, $this->allowedExtensions)) {
            throw new Exception('File type not allowed');
        }
        
        // Check if file exists and is readable
        if (!file_exists($fullPath) || !is_readable($fullPath)) {
            throw new Exception('File does not exist or is not readable');
        }
        
        return file_get_contents($fullPath);
    }
}

// Usage example
try {
    $reader = new SecureFileReader('/safe/data/directory');
    $content = $reader->readFile('user_data.txt');
    echo $content;
} catch (Exception $e) {
    echo "Error: " . $e->getMessage();
}
?>

File Locking and Concurrency Control

File Locking Basics

php
<?php
// Exclusive lock (write lock)
$file = fopen('counter.txt', 'r+');
if (flock($file, LOCK_EX)) { // Exclusive lock
    $count = (int)fread($file, filesize('counter.txt'));
    $count++;
    
    ftruncate($file, 0);
    rewind($file);
    fwrite($file, $count);
    
    flock($file, LOCK_UN); // Release lock
} else {
    echo "Cannot acquire file lock";
}
fclose($file);

// Shared lock (read lock)
$file = fopen('log.txt', 'r');
if (flock($file, LOCK_SH)) { // Shared lock
    $content = fread($file, filesize('log.txt'));
    echo $content;
    flock($file, LOCK_UN);
}
fclose($file);

// Non-blocking lock
$file = fopen('data.txt', 'w');
if (flock($file, LOCK_EX | LOCK_NB)) { // Non-blocking exclusive lock
    fwrite($file, 'Write data');
    flock($file, LOCK_UN);
    echo "Successfully acquired lock and wrote data";
} else {
    echo "File is being used by another process";
}
fclose($file);
?>

Best Practices and Security Recommendations

File Operation Best Practices

  1. Always check return values of file operations
  2. Use appropriate file locking mechanisms
  3. Validate file paths and filenames
  4. Use streaming reads for large files
  5. Set appropriate file permissions
  6. Use exception handling mechanisms

Security Considerations

  • Validate user input for filenames and paths
  • Limit file upload types and sizes
  • Use whitelist instead of blacklist for file type validation
  • Store uploaded files outside web root directory
  • Regularly clean up temporary files

Performance Optimization

  • Use streaming processing for large files
  • Use file caching appropriately
  • Avoid frequent file open/close operations
  • Use appropriate buffer sizes

Summary

This chapter introduced file handling techniques in PHP, including:

  • Basic file read/write operations
  • Directory management and traversal
  • Secure file upload handling
  • File permissions and security control
  • File locking and concurrent processing
  • Streaming processing for large files

Mastering these skills will enable you to build secure and efficient file handling applications. In the next chapter, we will learn about inheritance and Trait features in PHP.

Content is for learning and research only.