Keywords: PHP | Folder Compression | ZipArchive | Recursive Iterator | File Deletion
Abstract: This article provides a comprehensive guide on using PHP's ZipArchive class to recursively compress entire folders and selectively delete all files except specified ones after compression. It includes in-depth analysis of recursive directory iterators, file locking mechanisms, complete code implementations, and best practices covering path handling, exception management, and performance optimization.
Introduction
File compression and archiving are common requirements in modern web development. PHP provides the powerful ZipArchive class for handling ZIP compression operations. This article explores in detail how to use PHP to recursively compress entire folders and selectively delete all contents except specific files after compression.
Core Concepts and Preparation
Before implementing folder compression functionality, it's essential to understand several key concepts. First, ZipArchive is PHP's built-in extension class that provides capabilities for creating, opening, and modifying ZIP archive files. Second, recursive directory traversal is fundamental for handling nested folder structures, and PHP offers efficient solutions through RecursiveIteratorIterator and RecursiveDirectoryIterator.
Before using ZipArchive, ensure that the zip extension is enabled in PHP. You can check extension status via the phpinfo() function or command line. Basic file operation permissions are also necessary - ensure the script has permission to read the source folder and write to the target ZIP file.
Basic Folder Compression Implementation
Let's start with the basic folder compression functionality. The following code demonstrates how to use recursive iterators to traverse all files in a folder and add them to a ZIP archive:
// Remove trailing slashes from path
$rootPath = rtrim($rootPath, '\\/');
// Get absolute path of folder
$rootPath = realpath('folder-to-zip');
// Initialize ZipArchive object
$zip = new ZipArchive();
$zip->open('file.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
// Create recursive directory iterator
/** @var SplFileInfo[] $files */
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($rootPath),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $file)
{
// Skip directories (they are added automatically)
if (!$file->isDir())
{
// Get real and relative path for current file
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen($rootPath) + 1);
// Add current file to archive
$zip->addFile($filePath, $relativePath);
}
}
// ZIP archive is created only after closing object
$zip->close();
The key aspect of this code is using RecursiveIteratorIterator::LEAVES_ONLY mode, which ensures only files are processed while directories are not. Directory structures are automatically created in the ZIP file, which is a convenient feature of ZipArchive.
Complete Solution: Compression with Selective Deletion
In practical applications, it's often necessary to clean up the source folder after compression. The following code extends the basic functionality by adding selective file deletion capability:
// Remove trailing slashes from path
$rootPath = rtrim($rootPath, '\\/');
// Get absolute path of folder
$rootPath = realpath('folder-to-zip');
// Initialize ZipArchive object
$zip = new ZipArchive();
$zip->open('file.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
// Initialize empty delete list
$filesToDelete = array();
// Create recursive directory iterator
/** @var SplFileInfo[] $files */
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($rootPath),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $file)
{
// Skip directories
if (!$file->isDir())
{
// Get real and relative path for current file
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen($rootPath) + 1);
// Add current file to archive
$zip->addFile($filePath, $relativePath);
// Add current file to delete list
// Delete it later because ZipArchive creates archive only after calling close function
// and ZipArchive locks files until archive is created
if ($file->getFilename() != 'important.txt')
{
$filesToDelete[] = $filePath;
}
}
}
// ZIP archive is created only after closing object
$zip->close();
// Delete all files from delete list
foreach ($filesToDelete as $file)
{
unlink($file);
}
Key Technical Details Analysis
File Locking Mechanism: ZipArchive locks files being processed during operation until the close() method is called to complete archive creation. This is why deletion operations must be performed after compression is complete. Attempting to delete files during compression may cause operation failures or data corruption.
Path Handling: The code uses rtrim() function to clean trailing slashes from paths, ensuring consistent path formatting. The realpath() function converts relative paths to absolute paths, avoiding errors caused by path issues.
Relative Path Calculation: Calculating relative paths via substr($filePath, strlen($rootPath) + 1) ensures the directory structure in the ZIP file matches the source folder. The +1 operation removes the path separator.
Error Handling and Best Practices
In production environments, appropriate error handling mechanisms should be added:
// Check if folder exists
if (!file_exists($rootPath)) {
throw new Exception("Source folder does not exist: " . $rootPath);
}
// Check if ZipArchive opened successfully
if ($zip->open('file.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
throw new Exception("Cannot create ZIP file");
}
// Check if ZIP operation succeeded before deleting files
if ($zip->close() !== TRUE) {
throw new Exception("ZIP file creation failed");
}
Performance Considerations: For folders containing large numbers of files, consider batch processing or adding progress indicators. Also, ensure sufficient disk space is available for temporary files and the final ZIP archive.
Extended Functionality and Variants
The reference article demonstrates an alternative implementation using traditional directory traversal methods instead of iterators. This approach may be advantageous in specific scenarios, such as when more granular control over directory creation is needed.
Additional features that can be implemented include: compression level control, password-protected ZIP file support, adding empty directories to archives, and handling symbolic links. The ZipArchive class provides extensive methods to meet various advanced requirements.
Security Considerations
Security is crucial when handling file operations:
- Validate user input to prevent path traversal attacks
- Limit the scope of operable directories
- Check file permissions to avoid privilege escalation
- Exercise particular caution when handling user-uploaded files
Conclusion
This article provides a comprehensive solution for compressing folders and selectively deleting files using PHP. By combining the ZipArchive class with recursive iterators, complex folder structures can be efficiently handled. The key is understanding the file locking mechanism and proper operation sequence, ensuring deletion occurs only after compression is complete. This pattern can be widely applied to scenarios such as file backup, data archiving, and resource cleanup.