Close PHP ZipArchive before Deleting Source Files

ZipArchive::addFile() schedules a file for inclusion, but the archive is finalized when close() runs. Deleting the source immediately after addFile() can leave the extension unable to read it during finalization. Close and verify the archive first.

Last updated: November 11, 2026.

<?php

$source = __DIR__ . '/report.csv';
$archive = __DIR__ . '/reports.zip';

$zip = new ZipArchive();
$result = $zip->open($archive, ZipArchive::CREATE | ZipArchive::OVERWRITE);
if ($result !== true) {
    throw new RuntimeException("Cannot open ZIP archive: {$result}");
}

if (!$zip->addFile($source, 'report.csv')) {
    $zip->close();
    throw new RuntimeException('Cannot add report.csv to the archive');
}

if (!$zip->close()) {
    throw new RuntimeException('Cannot finalize the ZIP archive');
}

if (!unlink($source)) {
    throw new RuntimeException('ZIP succeeded, but source cleanup failed');
}

Treat archive creation and source cleanup as separate operations. A cleanup failure should not be reported as a ZIP failure, and a ZIP failure must never trigger deletion of the only source copy.

Expect work to finish at close

The ZipArchive::close manual states that pending changes are written when the archive closes. Disk-space problems, permissions, invalid paths, or source-file changes can therefore surface late. An implicit close during shutdown makes those failures harder to handle and can interact badly with error handlers.

The ZipArchive::addFile documentation also warns that files remain in use until the archive is closed. Keep every source path available and unchanged until close() returns successfully.

Verify before destructive cleanup

After closing, optionally reopen the archive and check the expected entry count for high-value exports. Write to a temporary archive path first, then rename it atomically to the final name so another process never sees a half-written file. Keep temporary files on the same filesystem when atomic rename behavior matters.

In batch jobs, record whether the archive, verification, publication, and cleanup stages succeeded. Retry only the incomplete stage and use unique temporary names. Avoid deleting a source in a destructor or shutdown function, because application exceptions and process termination can bypass the checks that protect the original data.

Related Web Cheat Sheet guides

Sergey Kornilov

Sergey Kornilov