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
- Always check return values of file operations
- Use appropriate file locking mechanisms
- Validate file paths and filenames
- Use streaming reads for large files
- Set appropriate file permissions
- 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.