Skip to content

Commit 090712d

Browse files
committed
Fixes #7 - add $continueOnError parameter
1 parent 657cda9 commit 090712d

File tree

1 file changed

+63
-39
lines changed

1 file changed

+63
-39
lines changed

src/VIPSoft/Unzip/Unzip.php

Lines changed: 63 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class Unzip
1616
/**
1717
* @var array
1818
*/
19-
static $statusStrings = array(
19+
private static $statusStrings = array(
2020
\ZipArchive::ER_OK => 'No error',
2121
\ZipArchive::ER_MULTIDISK => 'Multi-disk zip archives not supported',
2222
\ZipArchive::ER_RENAME => 'Renaming temporary file failed',
@@ -43,6 +43,39 @@ class Unzip
4343
\ZipArchive::ER_DELETED => 'Entry has been deleted',
4444
);
4545

46+
/**
47+
* @var boolean
48+
*/
49+
private $continueOnError;
50+
51+
/**
52+
* Extract zip file to target path
53+
*
54+
* @param string $zipFile Path of .zip file
55+
* @param string $targetPath Extract to this target (destination) path
56+
* @param boolean $continueOnError Continue extracting files on error
57+
*
58+
* @return mixed Array of filenames corresponding to the extracted files
59+
*
60+
* @throw \Exception
61+
*/
62+
public function extract($zipFile, $targetPath, $continueOnError = false)
63+
{
64+
$this->continueOnError = $continueOnError;
65+
66+
$zipArchive = $this->openZipFile($zipFile);
67+
$targetPath = $this->fixPath($targetPath);
68+
$filenames = $this->extractFilenames($zipArchive);
69+
70+
if ($zipArchive->extractTo($targetPath, $filenames) === false) {
71+
throw new \Exception($this->getStatusAsText($zipArchive->status));
72+
}
73+
74+
$zipArchive->close();
75+
76+
return $filenames;
77+
}
78+
4679
/**
4780
* Make sure target path ends in '/'
4881
*
@@ -65,13 +98,16 @@ private function fixPath($path)
6598
* @param string $zipFile
6699
*
67100
* @return \ZipArchive
101+
*
102+
* @throw \Exception
68103
*/
69104
private function openZipFile($zipFile)
70105
{
71106
$zipArchive = new \ZipArchive;
107+
$status = $zipArchive->open($zipFile);
72108

73-
if ($zipArchive->open($zipFile) !== true) {
74-
throw new \Exception('Error opening '.$zipFile);
109+
if ($status !== true) {
110+
throw new \Exception($this->getStatusAsText($status) . ": $zipFile");
75111
}
76112

77113
return $zipArchive;
@@ -90,7 +126,9 @@ private function extractFilenames(\ZipArchive $zipArchive)
90126
$fileCount = $zipArchive->numFiles;
91127

92128
for ($i = 0; $i < $fileCount; $i++) {
93-
if (($filename = $this->extractFilename($zipArchive, $i)) !== false) {
129+
$filename = $this->extractFilename($zipArchive, $i);
130+
131+
if ($filename !== false) {
94132
$filenames[] = $filename;
95133
}
96134
}
@@ -101,21 +139,22 @@ private function extractFilenames(\ZipArchive $zipArchive)
101139
/**
102140
* Test for valid filename path
103141
*
104-
* The .zip file is untrusted input. We check for absolute path (i.e., leading slash),
142+
* The .zip file is untrusted input. We check for absolute path (i.e., leading slash),
105143
* possible directory traversal attack (i.e., '..'), and use of PHP wrappers (i.e., ':').
144+
* Subclass and override this method at your own risk!
106145
*
107146
* @param string $path
108147
*
109148
* @return boolean
110149
*/
111-
private function isValidPath($path)
150+
protected function isValidPath($path)
112151
{
113152
$pathParts = explode('/', $path);
114153

115-
if (!strncmp($path, '/', 1) ||
154+
if (strncmp($path, '/', 1) === 0 ||
116155
array_search('..', $pathParts) !== false ||
117-
strpos($path, ':') !== false)
118-
{
156+
strpos($path, ':') !== false
157+
) {
119158
return false;
120159
}
121160

@@ -129,59 +168,44 @@ private function isValidPath($path)
129168
* @param integer $fileIndex File index
130169
*
131170
* @return string
171+
*
172+
* @throw \Exception
132173
*/
133174
private function extractFilename(\ZipArchive $zipArchive, $fileIndex)
134175
{
135176
$entry = $zipArchive->statIndex($fileIndex);
136177

137178
// convert Windows directory separator to Unix style
138-
$filename = str_replace('\\', '/', $entry['name']);
179+
$filename = str_replace('\\', '/', $entry['name']);
139180

140181
if ($this->isValidPath($filename)) {
141182
return $filename;
142183
}
143184

144-
throw new \Exception('Invalid filename path in zip archive');
185+
$statusText = "Invalid filename path in zip archive: $filename";
186+
187+
if ($this->continueOnError) {
188+
trigger_error($statusText);
189+
190+
return false;
191+
}
192+
193+
throw new \Exception($statusText);
145194
}
146195

147196
/**
148-
* Get error
197+
* Get status as text string
149198
*
150199
* @param integer $status ZipArchive status
151200
*
152201
* @return string
153202
*/
154-
private function getError($status)
203+
private function getStatusAsText($status)
155204
{
156205
$statusString = isset($this->statusStrings[$status])
157206
? $this->statusStrings[$status]
158-
:'Unknown status';
207+
: 'Unknown status';
159208

160209
return $statusString . '(' . $status . ')';
161210
}
162-
163-
/**
164-
* Extract zip file to target path
165-
*
166-
* @param string $zipFile Path of .zip file
167-
* @param string $targetPath Extract to this target (destination) path
168-
*
169-
* @return mixed Array of filenames corresponding to the extracted files
170-
*
171-
* @throw \Exception
172-
*/
173-
public function extract($zipFile, $targetPath)
174-
{
175-
$zipArchive = $this->openZipFile($zipFile);
176-
$targetPath = $this->fixPath($targetPath);
177-
$filenames = $this->extractFilenames($zipArchive);
178-
179-
if ($zipArchive->extractTo($targetPath, $filenames) === false) {
180-
throw new \Exception($this->getError($zipArchive->status));
181-
}
182-
183-
$zipArchive->close();
184-
185-
return $filenames;
186-
}
187211
}

0 commit comments

Comments
 (0)