From 16d3cec298039f322ec13c329f88fa616e9d5818 Mon Sep 17 00:00:00 2001 From: toimtoimtoim Date: Tue, 27 Dec 2016 21:36:21 +0200 Subject: [PATCH] fixed problem with path when extracting files - strpos'sing path to find directory part is not that clever as directory separator is different on windows vs linux extractTo now honors folder set with folder() fixed some of the warnings that Idea/phpstorm inspectors gave --- README.md | 5 +- .../Repositories/RepositoryInterface.php | 9 +++ src/Chumper/Zipper/Zipper.php | 57 +++++++++++++------ tests/ArrayArchive.php | 16 +++++- tests/ZipperTest.php | 10 +++- 5 files changed, 74 insertions(+), 23 deletions(-) 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()