Ne-Lexa / php-zip

PhpZip is a php-library for extended work with ZIP-archives.
MIT License
491 stars 60 forks source link

Writing a zip archive to a memory stream without closing it? #57

Closed lukas9393 closed 4 years ago

lukas9393 commented 4 years ago

Hi, I would like to create a zip archive in a memory stream to handle the stream without writing to the local filesystem. Unfortunately my first version does not work as expected:

$zip = new ZipFile();
$zip->addFile("functions/$functionName/main.py", "main.py");
$zip->addFile("functions/$functionName/requirements.txt", "requirements.txt");
if (!($fp = fopen('php://temp', 'w+b'))) {
    throw new InvalidArgumentException('php://temp cannot open for write.');
}
$zip->saveAsStream($fp);
$zip->close();
rewind($fp); <-- error

The code does not run due to the method saveAsStream which terminates the stream L1641.

I have found two solutions so that I can still use the memory stream: First, I create a Response Object, filled with the zip archive and then extract the ResponseStream from this object:

$zip = new ZipFile();
$zip->addFile("functions/$functionName/main.py", "main.py");
$zip->addFile("functions/$functionName/requirements.txt", "requirements.txt");
$fp = $zip->outputAsResponse(new Response(), "")->getBody();
$zip->close();

Secondly, I overwrite the protected of writeZipToStream L1761 and use the function as it is used in saveAsStream, but without calling `fclose':

class ZipFilePlus extends ZipFile {
    public function writeZipToStream($handle) {
        parent::writeZipToStream($handle);
    }
}

$zip = new ZipFilePlus();
$zip->addFile("functions/$functionName/main.py", "main.py");
$zip->addFile("functions/$functionName/requirements.txt", "requirements.txt");
if (!($fp = fopen('php://memory', 'w+b'))) {
    throw new InvalidArgumentException('php://memory cannot open for write.');
}
$zip->writeZipToStream($fp);
$zip->close();
rewind($fp);

Both are solutions that work, but are not quite correct. So I wanted to ask if you know a better solution to store a zip archive in a memory stream without writing to the filesystem? Or if not, if there is a possibility to change the visibility of writeZipToStream to public?

Ne-Lexa commented 4 years ago

Maybe the outputAsString method is right for you.

If stream is needed, then there is nothing wrong with extending a class for a special case.

$zip = new class extends ZipFile{
    /**
     * @return resource
     */
    public function saveAsMemory()
    {
        if (!($fp = fopen('php://temp', 'w+b'))) {
            throw new \InvalidArgumentException('php://temp cannot open for write.');
        }
        $this->writeZipToStream($fp);
        return $fp;
    }
};
$zip->addDir(__DIR__);
$fp = $zip->saveAsMemory();
$zip->close();

rewind($fp);