#2588·PHPWord

Race condition in ZipArchive::$tempDir

Author: francmaCreated Mar 20, 2024Updated Jul 6, 2026
LabelsBug Report

Describe the Bug

We have noticed an issue if we ran our project tests in parallel then the PhpOffice implementation of ZipArchive will randomly fail. After some digging, I have probably found the issue.

php
    public function pclzipAddFile($filename, $localname = null)
    {
        /** @var \PclZip $zip Type hint */
        $zip = $this->zip;

        // Bugfix GH-261 https://github.com/PHPOffice/PHPWord/pull/261
        $realpathFilename = realpath($filename);
        if ($realpathFilename !== false) {
            $filename = $realpathFilename;
        }

        $filenameParts = pathinfo($filename);
        $localnameParts = pathinfo($localname);

        // To Rename the file while adding it to the zip we
        //   need to create a temp file with the correct name
        $tempFile = false;
        if ($filenameParts['basename'] != $localnameParts['basename']) {
            $tempFile = true; // temp file created
            $temppath = $this->tempDir . DIRECTORY_SEPARATOR . $localnameParts['basename'];
            copy($filename, $temppath);
            $filename = $temppath;
            $filenameParts = pathinfo($temppath);
        }

        $pathRemoved = $filenameParts['dirname'];
        $pathAdded = $localnameParts['dirname'];

        if (!$this->usePclzip) {
            $pathAdded = $pathAdded . '/' . ltrim(str_replace('\\', '/', substr($filename, strlen($pathRemoved))), '/');
            //$res = $zip->addFile($filename, $pathAdded);
            $res = $zip->addFromString($pathAdded, file_get_contents($filename));       // addFile can't use subfolders in some cases
        } else {
            $res = $zip->add($filename, PCLZIP_OPT_REMOVE_PATH, $pathRemoved, PCLZIP_OPT_ADD_PATH, $pathAdded);
        }

        if ($tempFile) {
            // Remove temp file, if created
            unlink($this->tempDir . DIRECTORY_SEPARATOR . $localnameParts['basename']);
        }

        return $res != 0;
    }

The problem is that if this piece of code is called from multiple locations, at the same time, with the same name, then the $temppath variable will contain the same name, resulting in race condition.

Steps to Reproduce

Create multiple zip files in parallel and wait for the race condition to appear.

Context

Please fill in your environment information:

  • PHP Version: 8.3
  • PHPWord Version: 0.18.3-p3