Recently we were working on creating a WordPress plugin boilerplate, an online tool that features to create a WordPress plugin skeleton and download it in zip format. For achieving this we had to write a script that creates a zip file containing different files, folders, and subfolders, and then pushes it to browser for user download. We had a look around and discovered that this can be easily done using PHP ZipArchive class.
In this article, we will walk you through the process of zip file creation in PHP, using PHP ZipArchive class and we will use its native methods to push different files, folders, and sub-folders to the zip file. We will also show you how you can enable the force download of a zip file. Sounds interesting? Open up any code editor of your choice to follow along.
Topics at a glance
Create a ZIP file
When it comes to zip file creation, PHP has a ZipArchive class, which is versatile and full-featured. The following code will open a zip file my_zip.php and add a few files into it.
<?php $my_zip = new ZipArchive; if ($my_zip->open('my_zip.zip', ZipArchive::CREATE) === TRUE) { // Add files to the zip file $my_zip->addFile('text_file.txt'); $my_zip->addFile('pdf_file.pdf'); // Add original_name.txt file to zip and rename it to change_name.txt $my_zip->addFile('original_name.txt', 'change_name.txt'); // All files are added, so close the zip file. $my_zip->close(); }
Code Breakdown
$my_zip = new ZipArchive;
We start here by instantiating the PHP ZipArchive class, in order to use it’s utility methods.
$my_zip->open(‘my_zip.zip’, ZipArchive::CREATE)
Next, we try to create a new zip, called my_zip.zip, using the ZipArchive::open function. We wrapped this inside the “if” condition, as it is possible that the function is not able to create a new zip due to file permissions.
$my_zip->addFile(‘text_file.txt’);
Provided that the ZipArchive::open function returned true, now we have the zip open and we will start adding files using ZipArchive::addFile. The above example adds a text file in the zip, but you can add any file type you want.
$my_zip->addFile(‘original_name.txt’, ‘change_name.txt’);
ZipArchive::open function provides you the functionality to change the filename inside the zip when you want to move the file with different name.
$my_zip->close();
It is important to close the object before you can use it.
Overwrite a ZIP file
There may be a scenario where you need to update an existing archive, in place of creating new. This can be easily achieved using the below code.
$my_zip = new ZipArchive; if ($my_zip->open('my_zip.zip', ZipArchive::OVERWRITE) === TRUE) { // Add file to the zip file $my_zip->addFile('image.jpg'); // All files are added, so close the zip file. $my_zip->close(); }
As you can see, we are using ZipArchive::OVERWRITE flag, in place of ZipArchive::CREATE, to overwrite the existing zip.
Pro-tip
Instead of passing ZipArchive::CREATE or ZipArchive::OVERWRITE flag and making extra logic to check if the file is present, pass ZipArchive::CREATE|ZipArchive::OVERWRITE. Refer sample code below.
$my_zip->open('my_zip.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
Combining these flags will tell the ZipArchive class to the first check if the file exists. If it exists then ZipArchive will open the same file. If it doesn’t exist, then the class will create a new zip.
Create runtime files and add to ZIP file
Assume you are creating an application that collects user reviews. As a part of the application, you need to collect user-provided review, create a text file with a unique identifier and add that file to ZIP. To address this, you can create files in your system and then add in the zip file using ZipArchive::addFile.
But this seems to be a very ugly approach as this creates a lot of files in the system, which is unused once the file is added to the zip. To address this, ZipArchive provides the support to create the files on the fly, using ZipArchive::addFromString function. Refer sample code below.
$my_zip = new ZipArchive; if ($my_zip->open('my_zip.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) { // Create a new file new_file.txt using given text and add to zip // This is specially useful when you want to create a new file // with the user-defined dynamic content, such as form submission $my_zip->addFromString('new_file.txt', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis...'); // All files are added, so close the zip file. $my_zip->close(); }
As you can see, we are passing two arguments to the ZipArchive::addFromString function, i.e. file name and file content. You can add this code to your function handling form-submission to achieve the functionality.
Add files inside a folder in the ZIP
Instead of adding all the files at the root level, you may like to keep it structured by placing files inside the directories in the ZIP. This can be simply achieved using ZipArchive::addFile and ZipArchive::addFromString function.
$my_zip = new ZipArchive; if ($my_zip->open('my_zip.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) { // Add files to the zip file inside folder-1 $my_zip->addFile('text_file.txt', 'folder-1/text_file.txt'); // Add original_name.txt file to zip and rename it to new_name.txt and store in folder-2 $my_zip->addFile('original_name.txt', 'folder-2/new_name.txt'); // Add a file folder-1/user-review.txt file to zip using the text specified $my_zip->addFromString('folder-1/user-review.txt', 'text to be added...'); // All files are added, so close the zip file. $my_zip->close(); }
As you can see, the second argument of ZipArchive::addFile and ZipArchive::addFromString function takes care of creating a new folder inside the zip file before adding the file in ZIP. You can create an infinite level of folder nesting here to structure your ZIP.
Add the whole directory recursively in the ZIP file
Sometimes, you may want to add an entire folder, along with its files and folders to be recursively added in the ZIP, instead of adding single files. This can be easily done using the below code snippet.
This can be easily achieved using PHP ZipArchive & RecursiveIteratorIterator class
// Initialize archive object $my_zip = new ZipArchive(); if ($my_zip->open('my_zip.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) { // Create recursive directory iterator $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator('path-to-directory'), RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($files as $name => $file) { // Skip directories (they would be 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 will be created only after closing object $zip->close(); }
Code Breakdown
$my_zip = new ZipArchive();
First, we start by instantiating the ZipArchive class.
$my_zip->open(‘my_zip.zip’, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE
Then we open the ZIP file
$files = new RecursiveIteratorIterator(‘path-to-directory’)
Next, we instantiate RecursiveIteratorIterator class, with directory’s path.
foreach ($files as $name => $file) {}
Then we loop over the directory, to get the all the files recursively.
if (!$file->isDir())
Don’t do anything if it is a directory.
$filePath = $file->getRealPath();
If not, we will get the real path and add it to the ZIP using ZipArchive’s addFile method.
Password protection
Password protection is a very important feature. You may need to password protect the file due to a number of reasons. Luckily, this can be easily achieved using ZipArchive::setEncryptionName feature. Refer below code.
$my_zip = new ZipArchive(); if ($my_zip->open('my_zip.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) { $my_zip->setPassword('secret'); $my_zip->setEncryptionName('text.txt', ZipArchive::EM_AES_256); $my_zip->close(); }
Download the ZIP file
Now, we have created the ZIP file, we want to allow the users to download it. This can be easily done using the PHP header function. Refer below code.
ob_start(); $strFile = file_get_contents('path-to-zip/my_zip.php'); // Set headers for the zip archive header('Content-type: application/zip'); header('Content-Disposition: attachment; filename="my_zip.php"'); header('Content-Length: '.filesize("my_zip.php") ); echo $strFile; while (ob_get_level()) { ob_end_clean(); } readfile($file_path); // Exit. No wp_die(), it produces HTML exit;
Code Breakdown
Here we start by reading the file contents, using file_get_contents and setting up appropriate headers to download the zip. Then we print the file contents, clean the output buffer and turn off output buffering. You need to read the file contents and clean the output buffer, in addition to the header comments, to download a file in ZIP format. If you don’t follow these steps, the downloaded file will be SFX zip volume and not Zip Archive. You can learn the difference between them from wikipedia.org.
Then we push the file using PHP’s readFile function and add an exit statement to disallow any further content getting rendered on the screen. Adding an exit statement is required. If not added and if your app renders some markup after this function, your code will not work.
That is all. We hope you found this article useful. We will be back with some more interesting articles. Until then, HAPPY CODING 🙂