PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.8.13
UpdraftPlus: WP Backup & Migration Plugin v1.8.13
1.26.7 1.26.6 1.26.5 1.26.4 1.26.3 1.9.19 1.9.25 1.9.26 1.9.30 1.9.31 1.9.32 1.9.4 1.9.40 1.9.41 1.9.42 1.9.43 1.9.44 1.9.45 1.9.46 1.9.5 1.9.50 1.9.51 1.9.60 1.9.62 1.9.63 All 371 releases
updraftplus / class-zip.php

class-zip.php in UpdraftPlus: WP Backup & Migration Plugin 1.8.13, at class-zip.php

307 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined ('ABSPATH')) die('No direct access allowed');
4
5 if (class_exists('ZipArchive')):
6 # We just add a last_error variable for comaptibility with our UpdraftPlus_PclZip object
7 class UpdraftPlus_ZipArchive extends ZipArchive {
8 public $last_error = 'Unknown: ZipArchive does not return error messages';
9 }
10 endif;
11
12 class UpdraftPlus_BinZip extends UpdraftPlus_PclZip {
13
14 private $binzip;
15
16 function __construct() {
17 global $updraftplus_backup;
18 $this->binzip = $updraftplus_backup->binzip;
19 if (!is_string($this->binzip)) {
20 $this->last_error = "No binary zip was found";
21 return false;
22 }
23 return parent::__construct();
24 }
25
26 public function addFile($file, $add_as) {
27
28 global $updraftplus;
29 # Get the directory that $add_as is relative to
30 $base = $updraftplus->str_lreplace($add_as, '', $file);
31
32 if ($file == $base) {
33 // Shouldn't happen
34 } else {
35 $rdirname = untrailingslashit($base);
36 # Note: $file equals $rdirname/$add_as
37 $this->addfiles[$rdirname][] = $add_as;
38 }
39
40 }
41
42 # The standard zip binary cannot list; so we use PclZip for that
43 # Do the actual write-out - it is assumed that close() is where this is done. Needs to return true/false
44 public function close() {
45
46 if (empty($this->pclzip)) {
47 $this->last_error = 'Zip file was not opened';
48 return false;
49 }
50
51 global $updraftplus, $updraftplus_backup;
52 $updraft_dir = $updraftplus->backups_dir_location();
53
54 $activity = false;
55
56 # BinZip does not like zero-sized zip files
57 if (file_exists($this->path) && 0 == filesize($this->path)) @unlink($this->path);
58
59 $descriptorspec = array(
60 0 => array('pipe', 'r'),
61 1 => array('pipe', 'w'),
62 2 => array('pipe', 'w')
63 );
64 $exec = $this->binzip." -v -@ ".escapeshellarg($this->path);
65 $last_recorded_alive = time();
66 $something_useful_happened = $updraftplus->something_useful_happened;
67 $orig_size = file_exists($this->path) ? filesize($this->path) : 0;
68 $last_size = $orig_size;
69 clearstatcache();
70
71 $added_dirs_yet = false;
72
73 # If there are no files to add, but there are empty directories, then we need to make sure the directories actually get added
74 if (0 == count($this->addfiles) && 0 < count($this->adddirs)) {
75 $dir = realpath($updraftplus_backup->make_zipfile_source);
76 $this->addfiles[$dir] = '././.';
77 }
78
79 // Loop over each destination directory name
80 foreach ($this->addfiles as $rdirname => $files) {
81
82 $process = proc_open($exec, $descriptorspec, $pipes, $rdirname);
83
84 if (!is_resource($process)) {
85 $updraftplus->log('BinZip error: proc_open failed');
86 $this->last_error = 'BinZip error: proc_open failed';
87 return false;
88 }
89
90 if (!$added_dirs_yet) {
91 # Add the directories - (in fact, with binzip, non-empty directories automatically have their entries added; but it doesn't hurt to add them explicitly)
92 foreach ($this->adddirs as $dir) {
93 fwrite($pipes[0], $dir."/\n");
94 }
95 $added_dirs_yet=true;
96 }
97
98 $read = array($pipes[1], $pipes[2]);
99 $except = null;
100
101 if (!is_array($files) || 0 == count($files)) {
102 fclose($pipes[0]);
103 $write = array();
104 } else {
105 $write = array($pipes[0]);
106 }
107
108 while ((!feof($pipes[1]) || !feof($pipes[2]) || (is_array($files) && count($files)>0)) && false !== ($changes = @stream_select($read, $write, $except, 0, 200000))) {
109
110 if (is_array($write) && in_array($pipes[0], $write) && is_array($files) && count($files)>0) {
111 $file = array_pop($files);
112 // Send the list of files on stdin
113 fwrite($pipes[0], $file."\n");
114 if (0 == count($files)) fclose($pipes[0]);
115 }
116
117 if (is_array($read) && in_array($pipes[1], $read)) {
118 $w = fgets($pipes[1]);
119 // Logging all this really slows things down; use debug to mitigate
120 if ($w && $updraftplus_backup->debug) $updraftplus->log("Output from zip: ".trim($w), 'debug');
121 if (time() > $last_recorded_alive + 5) {
122 $updraftplus->record_still_alive();
123 $last_recorded_alive = time();
124 }
125 if (file_exists($this->path)) {
126 $new_size = @filesize($this->path);
127 if (!$something_useful_happened && $new_size > $orig_size + 20) {
128 $updraftplus->something_useful_happened();
129 $something_useful_happened = true;
130 }
131 clearstatcache();
132 # Log when 20% bigger or at least every 50Mb
133 if ($new_size > $last_size*1.2 || $new_size > $last_size + 52428800) {
134 $updraftplus->log(basename($this->path).sprintf(": size is now: %.2f Mb", round($new_size/1048576,1)));
135 $last_size = $new_size;
136 }
137 }
138 }
139
140 if (is_array($read) && in_array($pipes[2], $read)) {
141 $last_error = fgets($pipes[2]);
142 if (!empty($last_error)) $this->last_error = rtrim($last_error);
143 }
144
145 // Re-set
146 $read = array($pipes[1], $pipes[2]);
147 $write = (is_array($files) && count($files) >0) ? array($pipes[0]) : array();
148 $except = null;
149
150 }
151
152 fclose($pipes[1]);
153 fclose($pipes[2]);
154
155 $ret = proc_close($process);
156
157 if ($ret != 0 && $ret != 12) {
158 $updraftplus->log("Binary zip: error (code: $ret - look it up in the Diagnostics section at http://www.info-zip.org/mans/zip.html for interpretation... and also check that your hosting account quota is not full)");
159 if (!empty($w) && !$updraftplus_backup->debug) $updraftplus->log("Last output from zip: ".trim($w), 'debug');
160 return false;
161 }
162
163 unset($this->addfiles[$rdirname]);
164 }
165
166 return true;
167 }
168
169 }
170
171 # A ZipArchive compatibility layer, with behaviour sufficient for our usage of ZipArchive
172 class UpdraftPlus_PclZip {
173
174 protected $pclzip;
175 protected $path;
176 protected $addfiles;
177 protected $adddirs;
178 private $statindex;
179 public $last_error;
180
181 function __construct() {
182 $this->addfiles = array();
183 $this->adddirs = array();
184 }
185
186 public function __get($name) {
187 if ($name != 'numFiles') return null;
188
189 if (empty($this->pclzip)) return false;
190
191 $statindex = $this->pclzip->listContent();
192
193 if (empty($statindex)) {
194 $this->statindex=array();
195 return 0;
196 }
197
198 $result = array();
199 foreach ($statindex as $i => $file) {
200 if (!isset($statindex[$i]['folder']) || 0 == $statindex[$i]['folder']) {
201 $result[] = $file;
202 }
203 unset($statindex[$i]);
204 }
205
206 $this->statindex=$result;
207
208 return count($this->statindex);
209
210 }
211
212 public function statIndex($i) {
213 if (empty($this->statindex[$i])) return array('name' => null, 'size' => 0);
214 return array('name' => $this->statindex[$i]['filename'], 'size' => $this->statindex[$i]['size']);
215 }
216
217 public function open($path, $flags = 0) {
218 if(!class_exists('PclZip')) include_once(ABSPATH.'/wp-admin/includes/class-pclzip.php');
219 if(!class_exists('PclZip')) {
220 $this->last_error = "No PclZip class was found";
221 return false;
222 }
223
224 $ziparchive_create_match = (defined('ZIPARCHIVE::CREATE')) ? ZIPARCHIVE::CREATE : 1;
225
226 if ($flags == $ziparchive_create_match && file_exists($path)) @unlink($path);
227
228 $this->pclzip = new PclZip($path);
229 if (empty($this->pclzip)) {
230 $this->last_error = 'Could not get a PclZip object';
231 return false;
232 }
233
234 # Make the empty directory we need to implement addEmptyDir()
235 global $updraftplus;
236 $updraft_dir = $updraftplus->backups_dir_location();
237 if (!is_dir($updraft_dir.'/emptydir') && !mkdir($updraft_dir.'/emptydir')) {
238 $this->last_error = "Could not create empty directory ($updraft_dir/emptydir)";
239 return false;
240 }
241
242 $this->path = $path;
243
244 return true;
245
246 }
247
248 # Do the actual write-out - it is assumed that close() is where this is done. Needs to return true/false
249 public function close() {
250 if (empty($this->pclzip)) {
251 $this->last_error = 'Zip file was not opened';
252 return false;
253 }
254
255 global $updraftplus;
256 $updraft_dir = $updraftplus->backups_dir_location();
257
258 $activity = false;
259
260 # Add the empty directories
261 foreach ($this->adddirs as $dir) {
262 if (false == $this->pclzip->add($updraft_dir.'/emptydir', PCLZIP_OPT_REMOVE_PATH, $updraft_dir.'/emptydir', PCLZIP_OPT_ADD_PATH, $dir)) {
263 $this->last_error = $this->pclzip->errorInfo(true);
264 return false;
265 }
266 $activity = true;
267 }
268
269 foreach ($this->addfiles as $rdirname => $adirnames) {
270 foreach ($adirnames as $adirname => $files) {
271 if (false == $this->pclzip->add($files, PCLZIP_OPT_REMOVE_PATH, $rdirname, PCLZIP_OPT_ADD_PATH, $adirname)) {
272 $this->last_error = $this->pclzip->errorInfo(true);
273 return false;
274 }
275 $activity = true;
276 }
277 unset($this->addfiles[$rdirname]);
278 }
279
280 $this->pclzip = false;
281 $this->addfiles = array();
282 $this->adddirs = array();
283
284 clearstatcache();
285 if ($activity && filesize($this->path) < 50) {
286 $this->last_error = "Write failed - unknown cause (check your file permissions)";
287 return false;
288 }
289
290 return true;
291 }
292
293 # Note: basename($add_as) is irrelevant; that is, it is actually basename($file) that will be used. But these are always identical in our usage.
294 public function addFile($file, $add_as) {
295 # Add the files. PclZip appears to do the whole (copy zip to temporary file, add file, move file) cycle for each file - so batch them as much as possible. We have to batch by dirname(). On a test with 1000 files of 25Kb each in the same directory, this reduced the time needed on that directory from 120s to 15s (or 5s with primed caches).
296 $rdirname = dirname($file);
297 $adirname = dirname($add_as);
298 $this->addfiles[$rdirname][$adirname][] = $file;
299 }
300
301 # PclZip doesn't have a direct way to do this
302 public function addEmptyDir($dir) {
303 $this->adddirs[] = $dir;
304 }
305
306 }
307