diff --git a/README.md b/README.md index 51cb08a..24cf6dd 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,9 @@ This will go into the folder `test` in the zip file and extract the content of t This command is really nice to get just a part of the zip file, you can also pass a 2nd & 3rd param to specify a single or an array of files that will be +> NB: Php ZipArchive uses internally '/' as directory separator for files/folders in zip. So Windows users should not set +> whitelist/blacklist patterns with '\' as it will not match anything + white listed >**Zipper::WHITELIST** @@ -170,7 +173,7 @@ Which will extract the `test.zip` into the `public` folder but **only** files/fo test.zip |- test.bat |- test.bat.~ - |- test.bat/ + |- test.bat.dir/ |- fileInSubFolder.log ``` diff --git a/src/Chumper/Zipper/Repositories/RepositoryInterface.php b/src/Chumper/Zipper/Repositories/RepositoryInterface.php index 82e9cef..623b508 100644 --- a/src/Chumper/Zipper/Repositories/RepositoryInterface.php +++ b/src/Chumper/Zipper/Repositories/RepositoryInterface.php @@ -32,6 +32,15 @@ function __construct($filePath, $new = false, $archiveImplementation = null); */ public function addFile($pathToFile, $pathInArchive); + /** + * Add a file to the opened Archive using its contents + * + * @param $name + * @param $content + * @return void + */ + public function addFromString($name, $content); + /** * Add an empty directory * diff --git a/src/Chumper/Zipper/Zipper.php b/src/Chumper/Zipper/Zipper.php index c5acd9f..a503e72 100644 --- a/src/Chumper/Zipper/Zipper.php +++ b/src/Chumper/Zipper/Zipper.php @@ -54,7 +54,7 @@ class Zipper * * @param Filesystem $fs */ - function __construct(Filesystem $fs = null) + public function __construct(Filesystem $fs = null) { $this->file = $fs ? $fs : new Filesystem(); } @@ -67,6 +67,7 @@ function __construct(Filesystem $fs = null) * @param RepositoryInterface|string $type The type of the archive, defaults to zip, possible are zip, phar * * @return $this Zipper instance + * @throws \RuntimeException * @throws \Exception * @throws \InvalidArgumentException */ @@ -84,10 +85,9 @@ public function make($pathToFile, $type = 'zip') throw new \InvalidArgumentException("Class for '{$objectOrName}' must implement RepositoryInterface interface"); } + $this->repository = $type; if (is_string($objectOrName)) { $this->repository = new $objectOrName($pathToFile, $new); - } else { - $this->repository = $type; } return $this; @@ -98,6 +98,7 @@ public function make($pathToFile, $type = 'zip') * * @param $pathToFile * @return $this + * @throws \Exception */ public function zip($pathToFile) { @@ -110,6 +111,7 @@ public function zip($pathToFile) * * @param $pathToFile * @return $this + * @throws \Exception */ public function phar($pathToFile) { @@ -122,6 +124,7 @@ public function phar($pathToFile) * * @param $pathToFile * @return $this + * @throws \Exception */ public function rar($pathToFile) { @@ -150,7 +153,7 @@ public function extractTo($path, array $files = array(), $methodFlags = Zipper:: return in_array($haystack, $files, true); }; } else { - $matchingMethod = function ($haystack) use ($files) { + $matchingMethod = function ($haystack) use ($files) { return starts_with($haystack, $files); }; } @@ -170,8 +173,11 @@ public function extractTo($path, array $files = array(), $methodFlags = Zipper:: * * @param string $extractToPath The path to extract to * @param string $regex regular expression used to match files. See @link http://php.net/manual/en/reference.pcre.pattern.syntax.php + * @throws \InvalidArgumentException + * @throws \RuntimeException */ - public function extractMatchingRegex($extractToPath, $regex) { + public function extractMatchingRegex($extractToPath, $regex) + { if (empty($regex)) { throw new \InvalidArgumentException('Missing pass valid regex parameter'); } @@ -233,7 +239,7 @@ public function add($pathToAdd, $fileName = null) * Add an empty directory * * @param $dirName - * @return void + * @return Zipper */ public function addEmptyDir($dirName) { @@ -313,9 +319,10 @@ public function usePassword($password) */ public function close() { - if (!is_null($this->repository)) + if (null !== $this->repository) { $this->repository->close(); - $this->filePath = ""; + } + $this->filePath = ''; } /** @@ -346,11 +353,12 @@ public function home() */ public function delete() { - if (!is_null($this->repository)) + if (null !== $this->repository) { $this->repository->close(); + } $this->file->delete($this->filePath); - $this->filePath = ""; + $this->filePath = ''; } /** @@ -368,8 +376,9 @@ public function getArchiveType() */ public function __destruct() { - if (!is_null($this->repository)) + if (null !== $this->repository) { $this->repository->close(); + } } /** @@ -382,6 +391,19 @@ public function getCurrentFolderPath() return $this->currentFolder; } + private function getCurrentFolderWithTrailingSlash() + { + if (empty($this->currentFolder)) { + return ''; + } + + $lastChar = mb_substr($this->currentFolder, -1); + if ($lastChar !== '/' || $lastChar !== '\\') { + return $this->currentFolder . '/'; + } + return $this->currentFolder; + } + /** * Checks if a file is present in the archive * @@ -430,9 +452,10 @@ private function createArchiveFile($pathToZip) { if (!$this->file->exists($pathToZip)) { - if (!$this->file->exists(dirname($pathToZip)) && !$this->file->makeDirectory(dirname($pathToZip), 0755, true)) { + $dirname = dirname($pathToZip); + if (!$this->file->exists($dirname) && !$this->file->makeDirectory($dirname, 0755, true)) { throw new \RuntimeException('Failed to create folder'); - } else if (!$this->file->isWritable(dirname($pathToZip))) { + } else if (!$this->file->isWritable($dirname)) { throw new Exception(sprintf('The path "%s" is not writeable', $pathToZip)); } @@ -493,7 +516,7 @@ private function extractFilesInternal($path, callable $matchingMethod) { $self = $this; $this->repository->each(function ($fileName) use ($path, $matchingMethod, $self) { - $currentPath = $self->getCurrentFolderPath(); + $currentPath = $self->getCurrentFolderWithTrailingSlash(); if (!empty($currentPath) && !starts_with($fileName, $currentPath)) { return; } @@ -508,15 +531,15 @@ private function extractFilesInternal($path, callable $matchingMethod) /** * @param $fileName * @param $path + * @throws \RuntimeException */ private function extractOneFileInternal($fileName, $path) { $tmpPath = str_replace($this->getInternalPath(), '', $fileName); // We need to create the directory first in case it doesn't exist - $full_path = $path . DIRECTORY_SEPARATOR . $tmpPath; - $dir = substr($full_path, 0, strrpos($full_path, DIRECTORY_SEPARATOR)); - if (!is_dir($dir) && !$this->getFileHandler()->makeDirectory($dir, 0777, true, true)) { + $dir = pathinfo($path . DIRECTORY_SEPARATOR . $tmpPath, PATHINFO_DIRNAME); + if (!$this->file->exists($dir) && !$this->file->makeDirectory($dir, 0755, true, true)) { throw new \RuntimeException('Failed to create folders'); } diff --git a/tests/ArrayArchive.php b/tests/ArrayArchive.php index 688ac64..517f8d9 100644 --- a/tests/ArrayArchive.php +++ b/tests/ArrayArchive.php @@ -29,6 +29,18 @@ public function addFile($pathToFile, $pathInArchive) $this->entries[$pathInArchive] = $pathInArchive; } + /** + * Add a file to the opened Archive using its contents + * + * @param $name + * @param $content + * @return void + */ + public function addFromString($name, $content) + { + $this->entries[$name] = $name; + } + /** * Remove a file permanently from the Archive * @@ -115,7 +127,7 @@ public function close() * @return void */ public function addEmptyDir($dirName){ - # CODE... + # CODE... } /** @@ -126,6 +138,6 @@ public function addEmptyDir($dirName){ */ public function usePassword($password) { - # CODE... + # CODE... } } diff --git a/tests/ZipperTest.php b/tests/ZipperTest.php index b2b0425..3de2911 100644 --- a/tests/ZipperTest.php +++ b/tests/ZipperTest.php @@ -254,6 +254,7 @@ public function testExtractWhiteListWithExactMatching() public function testExtractWhiteListWithExactMatchingFromSubDirectory() { $this->file->shouldReceive('isFile')->andReturn(true); + $this->file->shouldReceive('exists')->andReturn(false); $this->file->shouldReceive('makeDirectory')->andReturn(true); $this->archive->folder('foo/bar/subDirectory') @@ -264,12 +265,15 @@ public function testExtractWhiteListWithExactMatchingFromSubDirectory() ->add('baz') ->add('baz.log'); - $this->file - ->shouldReceive('put') - ->with(realpath(NULL) . DIRECTORY_SEPARATOR . 'subDirectory/bazInSubDirectory', 'foo/bar/subDirectory/bazInSubDirectory'); + $subDirectoryPath = realpath(NULL) . DIRECTORY_SEPARATOR . 'subDirectory'; + $subDirectoryFilePath = $subDirectoryPath . '/bazInSubDirectory'; + $this->file->shouldReceive('put') + ->with($subDirectoryFilePath, 'foo/bar/subDirectory/bazInSubDirectory'); $this->archive ->extractTo(getcwd(), array('subDirectory/bazInSubDirectory'), Zipper::WHITELIST | Zipper::EXACT_MATCH); + + $this->file->shouldHaveReceived('makeDirectory')->with($subDirectoryPath, 0755, true, true); } public function testExtractToIgnoresBlackListFile()