When working with ZIP files in PHP, the general workflow is as follows:
$zip = zip_open("example.zip");
<p>if (is_resource($zip)) {<br>
while ($entry = zip_read($zip)) {<br>
echo "Name: " . zip_entry_name($entry) . "\n";<br>
if (zip_entry_open($zip, $entry, "r")) {<br>
$contents = zip_entry_read($entry, 1024);<br>
echo "Content: " . $contents . "\n";<br>
zip_entry_close($entry);<br>
}<br>
}<br>
zip_close($zip);<br>
}<br>
The code above is a typical example from the PHP documentation. It works fine in many scenarios, but as soon as things get a little more complicated or the content being read exceeds a certain size, issues arise.
Unable to read content: zip_entry_read() returns an empty string.
File read is interrupted halfway: Only part of the content is read.
Errors or freezing: Some ZIP files cause PHP to hang during execution.
Reading order is mixed up: Contents of multiple entries are interwoven.
These issues share a common feature: if another library, such as ZipArchive, is used, the problem usually disappears. This suggests that the issue is likely not with the ZIP file itself but with the usage or implementation of zip_* functions.
zip_read() is essentially an iterator that maintains the read pointer for the ZIP file each time it is called. If you call zip_read() again while reading an entry (before it is fully read), it will disrupt the previous entry's read state.
zip_entry_read() does not guarantee that it will read all the data in one go, especially when you pass a fixed length (e.g., 1024). You need to loop through the function until it returns an empty string; otherwise, content will be lost.
This is the core of the issue: zip_entry_read() relies on the entry state maintained by zip_read(). If you call zip_read() again while using zip_entry_read() (even in another logical branch), the former's behavior will be interrupted.
Do not move on to the next zip_read() before finishing reading the current entry, as doing so will advance the read pointer prematurely, leading to lost or scrambled content.
while ($entry = zip_read($zip)) {
zip_entry_open($zip, $entry, "r");
while ($data = zip_entry_read($entry, 1024)) {
$content .= $data;
}
echo $content;
zip_entry_close($entry);
}
Some ZIP files may be compressed with non-standard tools and may contain irregular structures. It is recommended to use the ZipArchive class along with the isReadable() method to check integrity first.
$zip = new ZipArchive();
if ($zip->open('example.zip') === TRUE) {
// Integrity check logic
$zip->close();
}
While zip_open() and similar functions are simple and convenient, they have been marked as experimental (and even discouraged for use) since the early days. On the other hand, the ZipArchive class is more powerful, stable, and better suited to handle complex ZIP files.
$zip = new ZipArchive();
if ($zip->open('example.zip') === TRUE) {
for ($i = 0; $i < $zip->numFiles; $i++) {
$entryName = $zip->getNameIndex($i);
echo "File: " . $entryName . "\n";
$stream = $zip->getStream($entryName);
if ($stream) {
while (!feof($stream)) {
echo fread($stream, 1024);
}
fclose($stream);
}
}
$zip->close();
}
This approach is not only more efficient but also avoids the interruptions caused by zip_entry_read(), making it more suitable for production environments.
The issues that arise when using zip_read() and zip_entry_read() together stem from their strong dependency on the internal pointer state. Even a small mistake in processing order or reading pace can easily lead to logic errors. For projects that require high stability in ZIP operations, it is recommended to use ZipArchive instead of these low-level functions.
If you must use the zip_* function family, always ensure strict reading order, fully read each entry's content, and avoid moving on to the next read before closing the current entry.
Reference documentation: https://www.php.net/manual/zh/ref.zip.php