diff --git a/phpunit.xml b/phpunit.xml index 25c557a..c69aee3 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,10 +1,23 @@ - + + + + + - ./tests + tests + + + src + + \ No newline at end of file diff --git a/src/PhpZip/ZipFile.php b/src/PhpZip/ZipFile.php index fc6b9d9..403bcd3 100644 --- a/src/PhpZip/ZipFile.php +++ b/src/PhpZip/ZipFile.php @@ -1226,61 +1226,64 @@ class ZipFile implements ZipFileInterface * Output .ZIP archive as attachment. * Die after output. * - * @param string $outputFilename - * @param string|null $mimeType + * @param string $outputFilename Output filename + * @param string|null $mimeType Mime-Type + * @param bool $attachment Http Header 'Content-Disposition' if true then attachment otherwise inline * @throws InvalidArgumentException */ - public function outputAsAttachment($outputFilename, $mimeType = null) + public function outputAsAttachment($outputFilename, $mimeType = null, $attachment = true) { $outputFilename = (string)$outputFilename; - if (strlen($outputFilename) === 0) { - throw new InvalidArgumentException("Output filename is empty."); - } - if (empty($mimeType) || !is_string($mimeType)) { + + if (empty($mimeType) || !is_string($mimeType) && !empty($outputFilename)) { $ext = strtolower(pathinfo($outputFilename, PATHINFO_EXTENSION)); if (!empty($ext) && isset(self::$defaultMimeTypes[$ext])) { $mimeType = self::$defaultMimeTypes[$ext]; - } else { - $mimeType = self::$defaultMimeTypes['zip']; } } - $outputFilename = basename($outputFilename); + if (empty($mimeType)) { + $mimeType = self::$defaultMimeTypes['zip']; + } $content = $this->outputAsString(); $this->close(); + $headerContentDisposition = 'Content-Disposition: ' . ($attachment ? 'attachment' : 'inline'); + if (!empty($outputFilename)) { + $headerContentDisposition .= '; filename="' . basename($outputFilename) . '"'; + } + + header($headerContentDisposition); header("Content-Type: " . $mimeType); - header('Content-Disposition: attachment; filename="' . $outputFilename . '"'); header("Content-Length: " . strlen($content)); exit($content); } /** - * Output .ZIP archive as PSR-Message Response. + * Output .ZIP archive as PSR-7 Response. * - * @param ResponseInterface $response - * @param string $outputFilename - * @param string|null $mimeType + * @param ResponseInterface $response Instance PSR-7 Response + * @param string $outputFilename Output filename + * @param string|null $mimeType Mime-Type + * @param bool $attachment Http Header 'Content-Disposition' if true then attachment otherwise inline * @return ResponseInterface * @throws InvalidArgumentException */ - public function outputAsResponse(ResponseInterface $response, $outputFilename, $mimeType = null) + public function outputAsResponse(ResponseInterface $response, $outputFilename, $mimeType = null, $attachment = true) { $outputFilename = (string)$outputFilename; - if (strlen($outputFilename) === 0) { - throw new InvalidArgumentException("Output filename is empty."); - } - if (empty($mimeType) || !is_string($mimeType)) { + + if (empty($mimeType) || !is_string($mimeType) && !empty($outputFilename)) { $ext = strtolower(pathinfo($outputFilename, PATHINFO_EXTENSION)); if (!empty($ext) && isset(self::$defaultMimeTypes[$ext])) { $mimeType = self::$defaultMimeTypes[$ext]; - } else { - $mimeType = self::$defaultMimeTypes['zip']; } } - $outputFilename = basename($outputFilename); + if (empty($mimeType)) { + $mimeType = self::$defaultMimeTypes['zip']; + } if (!($handle = fopen('php://memory', 'w+b'))) { throw new InvalidArgumentException("Memory can not open from write."); @@ -1288,9 +1291,14 @@ class ZipFile implements ZipFileInterface $this->writeZipToStream($handle); rewind($handle); + $contentDispositionValue = ($attachment ? 'attachment' : 'inline'); + if (!empty($outputFilename)) { + $contentDispositionValue .= '; filename="' . basename($outputFilename) . '"'; + } + $stream = new ResponseStream($handle); $response->withHeader('Content-Type', $mimeType); - $response->withHeader('Content-Disposition', 'attachment; filename="' . $outputFilename . '"'); + $response->withHeader('Content-Disposition', $contentDispositionValue); $response->withHeader('Content-Length', $stream->getSize()); $response->withBody($stream); return $response; @@ -1348,11 +1356,15 @@ class ZipFile implements ZipFileInterface $content = $this->outputAsString(); $this->close(); if ('plainfile' === $meta['wrapper_type']) { - if (file_put_contents($meta['uri'], $content) === false) { - throw new ZipException("Can not overwrite the zip file in the {$meta['uri']} file."); + /** + * @var resource $uri + */ + $uri = $meta['uri']; + if (file_put_contents($uri, $content) === false) { + throw new ZipException("Can not overwrite the zip file in the {$uri} file."); } - if (!($handle = @fopen($meta['uri'], 'rb'))) { - throw new ZipException("File {$meta['uri']} can't open."); + if (!($handle = @fopen($uri, 'rb'))) { + throw new ZipException("File {$uri} can't open."); } return $this->openFromStream($handle); } diff --git a/src/PhpZip/ZipFileInterface.php b/src/PhpZip/ZipFileInterface.php index 09ddd23..05d31e3 100644 --- a/src/PhpZip/ZipFileInterface.php +++ b/src/PhpZip/ZipFileInterface.php @@ -592,22 +592,23 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator * Output .ZIP archive as attachment. * Die after output. * - * @param string $outputFilename - * @param string|null $mimeType - * @throws InvalidArgumentException + * @param string $outputFilename Output filename + * @param string|null $mimeType Mime-Type + * @param bool $attachment Http Header 'Content-Disposition' if true then attachment otherwise inline */ - public function outputAsAttachment($outputFilename, $mimeType = null); + public function outputAsAttachment($outputFilename, $mimeType = null, $attachment = true); /** - * Output .ZIP archive as PSR-Message Response. + * Output .ZIP archive as PSR-7 Response. * - * @param ResponseInterface $response - * @param string $outputFilename - * @param string|null $mimeType + * @param ResponseInterface $response Instance PSR-7 Response + * @param string $outputFilename Output filename + * @param string|null $mimeType Mime-Type + * @param bool $attachment Http Header 'Content-Disposition' if true then attachment otherwise inline * @return ResponseInterface * @throws InvalidArgumentException */ - public function outputAsResponse(ResponseInterface $response, $outputFilename, $mimeType = null); + public function outputAsResponse(ResponseInterface $response, $outputFilename, $mimeType = null, $attachment = true); /** * Returns the zip archive as a string. diff --git a/tests/PhpZip/ZipFileTest.php b/tests/PhpZip/ZipFileTest.php index 3685db0..e23d82e 100644 --- a/tests/PhpZip/ZipFileTest.php +++ b/tests/PhpZip/ZipFileTest.php @@ -30,7 +30,7 @@ class ZipFileTest extends ZipTestCase */ public function testOpenFileCantOpen() { - if (0 === posix_getuid()){ + if (0 === posix_getuid()) { $this->markTestSkipped('Skip the test for a user with root privileges'); } @@ -1007,7 +1007,7 @@ class ZipFileTest extends ZipTestCase */ public function testExtractFail3() { - if (0 === posix_getuid()){ + if (0 === posix_getuid()) { $this->markTestSkipped('Skip the test for a user with root privileges'); } @@ -1213,7 +1213,7 @@ class ZipFileTest extends ZipTestCase */ public function testAddFileCantOpen() { - if (0 === posix_getuid()){ + if (0 === posix_getuid()) { $this->markTestSkipped('Skip the test for a user with root privileges'); } @@ -1501,7 +1501,7 @@ class ZipFileTest extends ZipTestCase */ public function testSaveAsFileNotWritable() { - if (0 === posix_getuid()){ + if (0 === posix_getuid()) { $this->markTestSkipped('Skip the test for a user with root privileges'); } @@ -1662,26 +1662,6 @@ class ZipFileTest extends ZipTestCase $zipFile->addEmptyDir(""); } - /** - * @expectedException \PhpZip\Exception\InvalidArgumentException - * @expectedExceptionMessage Output filename is empty. - */ - public function testOutputAsAttachmentNullName() - { - $zipFile = new ZipFile(); - $zipFile->outputAsAttachment(null); - } - - /** - * @expectedException \PhpZip\Exception\InvalidArgumentException - * @expectedExceptionMessage Output filename is empty. - */ - public function testOutputAsAttachmentEmptyName() - { - $zipFile = new ZipFile(); - $zipFile->outputAsAttachment(''); - } - /** * @expectedException \PhpZip\Exception\ZipNotFoundEntry * @expectedExceptionMessage Zip entry bad entry name not found @@ -1916,18 +1896,6 @@ class ZipFileTest extends ZipTestCase $this->assertInstanceOf(ResponseInterface::class, $response); } - /** - * @expectedException \PhpZip\Exception\InvalidArgumentException - * @expectedExceptionMessage Output filename is empty. - */ - public function testInvalidPsrResponse() - { - $zipFile = new ZipFile(); - $zipFile['file'] = 'content'; - $response = $this->getMock(ResponseInterface::class); - $zipFile->outputAsResponse($response, ''); - } - public function testCompressionLevel() { $zipFile = new ZipFile();