PhpSpreadsheet: Permissions | ZipArchive::close(): Failure to create temporary file

General rules for a directory in PHP to write to it:

It must exist,

It is writable by PHP process,

It is allowed by open_basedir php.ini directive.

Therefore, set some file path as argument in the $writer->save() method and check that these 3 rules are met.

If you want to use only php://output or php://stdout value in the $writer->save() method, check these rules for:

1) The directory returned by the sys_get_temp_dir() function. In Windows sys_get_temp_dir() by default returns the temporary directory of the current OS user. The value can be changed by sys_temp_dir php.ini directive.

or

2) The directory returned by the upload_tmp_dir php.ini directive. $useUploadTempDirectory has false value by default. To set it value to true add this line in your code before saving the file:

\PhpOffice\PhpSpreadsheet\Shared\File::setUseUploadTempDirectory(true);


Here is the code that is responsible for selecting the save path:

From \PhpOffice\PhpSpreadsheet\Writer\Xlsx::save method (source):

// If $pFilename is php://output or php://stdout, make it a temporary file...
$originalFilename = $pFilename;
if (strtolower($pFilename) == 'php://output' || strtolower($pFilename) == 'php://stdout') {
    $pFilename = @tempnam(File::sysGetTempDir(), 'phpxltmp');
    if ($pFilename == '') {
        $pFilename = $originalFilename;
    }
}

From \PhpOffice\PhpSpreadsheet\SharedFile::sysGetTempDir method (source):

/**
 * Get the systems temporary directory.
 *
 * @return string
 */
public static function sysGetTempDir()
{
    if (self::$useUploadTempDirectory) {
        //  use upload-directory when defined to allow 
        // running on environments having very restricted open_basedir configs
        if (ini_get('upload_tmp_dir') !== false) {
            if ($temp = ini_get('upload_tmp_dir')) {
                if (file_exists($temp)) {
                    return realpath($temp);
                }
            }
        }
    }
    return realpath(sys_get_temp_dir());
}