Version Description
- 02/02/2013 =
- Prefer PHP's native zip functions if available - 25% speed-up on zip creation
- Zip file creation is now resumable; and thus the entire backup operation is; there is now no "too early to resume" point. So even the most enormous site backups should now be able to proceed.
Download this release
Release Info
Developer | DavidAnderson |
Plugin | UpdraftPlus WordPress Backup Plugin |
Version | 1.3.25 |
Comparing to | |
See all releases |
Code changes from version 1.3.24 to 1.3.25
- readme.txt +2 -1
- updraftplus.php +113 -35
readme.txt
CHANGED
@@ -141,8 +141,9 @@ Thanks for asking - yes, I have. Check out my profile page - http://profiles.wor
|
|
141 |
|
142 |
== Changelog ==
|
143 |
|
144 |
-
= 1.3.
|
145 |
* Prefer PHP's native zip functions if available - 25% speed-up on zip creation
|
|
|
146 |
|
147 |
= 1.3.22 - 01/31/2013 =
|
148 |
* More help for really large uploads; dynamically alter the maximum number of resumption attempts if something useful is still happening
|
141 |
|
142 |
== Changelog ==
|
143 |
|
144 |
+
= 1.3.25 - 02/02/2013 =
|
145 |
* Prefer PHP's native zip functions if available - 25% speed-up on zip creation
|
146 |
+
* Zip file creation is now resumable; and thus the entire backup operation is; there is now no "too early to resume" point. So even the most enormous site backups should now be able to proceed.
|
147 |
|
148 |
= 1.3.22 - 01/31/2013 =
|
149 |
* More help for really large uploads; dynamically alter the maximum number of resumption attempts if something useful is still happening
|
updraftplus.php
CHANGED
@@ -4,7 +4,7 @@ Plugin Name: UpdraftPlus - Backup/Restore
|
|
4 |
Plugin URI: http://wordpress.org/extend/plugins/updraftplus
|
5 |
Description: Backup and restore: your content and database can be automatically backed up to Amazon S3, Dropbox, Google Drive, FTP or email, on separate schedules.
|
6 |
Author: David Anderson.
|
7 |
-
Version: 1.3.
|
8 |
Donate link: http://david.dw-perspective.org.uk/donate
|
9 |
License: GPLv3 or later
|
10 |
Author URI: http://wordshell.net
|
@@ -85,7 +85,7 @@ if (!class_exists('UpdraftPlus_Options')) require_once(UPDRAFTPLUS_DIR.'/options
|
|
85 |
|
86 |
class UpdraftPlus {
|
87 |
|
88 |
-
var $version = '1.3.
|
89 |
var $plugin_title = 'UpdraftPlus Backup/Restore';
|
90 |
|
91 |
// Choices will be shown in the admin menu in the order used here
|
@@ -114,6 +114,11 @@ class UpdraftPlus {
|
|
114 |
var $current_resumption;
|
115 |
var $newresumption_scheduled;
|
116 |
|
|
|
|
|
|
|
|
|
|
|
117 |
function __construct() {
|
118 |
// Initialisation actions - takes place on plugin load
|
119 |
# Create admin page
|
@@ -716,9 +721,10 @@ class UpdraftPlus {
|
|
716 |
|
717 |
$microtime_start = microtime(true);
|
718 |
# The paths in the zip should then begin with '$whichone', having removed WP_CONTENT_DIR from the front
|
719 |
-
|
720 |
-
|
721 |
-
$this->
|
|
|
722 |
return false;
|
723 |
} else {
|
724 |
rename($full_path.'.tmp', $full_path);
|
@@ -2110,33 +2116,103 @@ class UpdraftPlus {
|
|
2110 |
// Caution: $source is allowed to be an array, not just a filename
|
2111 |
function make_zipfile($source, $destination) {
|
2112 |
|
2113 |
-
|
2114 |
-
|
2115 |
-
|
2116 |
-
|
2117 |
-
|
2118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
2119 |
|
2120 |
-
|
2121 |
-
if (!$zip->open($destination, ZIPARCHIVE::CREATE)) {
|
2122 |
-
return false;
|
2123 |
-
}
|
2124 |
|
2125 |
-
|
2126 |
-
|
2127 |
-
|
2128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2129 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2130 |
} else {
|
2131 |
-
|
2132 |
}
|
2133 |
|
2134 |
-
|
|
|
|
|
|
|
2135 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2136 |
}
|
2137 |
|
2138 |
// This function recursively packs the zip, dereferencing symlinks but packing into a single-parent tree for universal unpacking
|
2139 |
-
function makezip_recursive_add($
|
2140 |
|
2141 |
// De-reference
|
2142 |
$fullpath = realpath($fullpath);
|
@@ -2150,16 +2226,14 @@ class UpdraftPlus {
|
|
2150 |
|
2151 |
if(is_file($fullpath)) {
|
2152 |
if (is_readable($fullpath)) {
|
2153 |
-
$
|
2154 |
-
$
|
2155 |
-
if ($files_added_so_far % 100 == 0) $this->log("Zip: $files_added_so_far files added");
|
2156 |
-
return true;
|
2157 |
} else {
|
2158 |
$this->log("$fullpath: unreadable file");
|
2159 |
$this->error("$fullpath: unreadable file");
|
2160 |
}
|
2161 |
} elseif (is_dir($fullpath)) {
|
2162 |
-
$
|
2163 |
if (!$dir_handle = @opendir($fullpath)) {
|
2164 |
$this->log("Failed to open directory: $fullpath");
|
2165 |
$this->error("Failed to open directory: $fullpath");
|
@@ -2171,34 +2245,38 @@ class UpdraftPlus {
|
|
2171 |
$deref = realpath($fullpath.'/'.$e);
|
2172 |
if (is_file($deref)) {
|
2173 |
if (is_readable($deref)) {
|
2174 |
-
$
|
2175 |
-
$files_added_so_far++;
|
2176 |
-
if ($files_added_so_far % 100 == 0) $this->log("Zip: $files_added_so_far files added");
|
2177 |
} else {
|
2178 |
$this->log("$deref: unreadable file");
|
2179 |
$this->error("$deref: unreadable file");
|
2180 |
}
|
2181 |
} elseif (is_dir($deref)) {
|
2182 |
-
$this->makezip_recursive_add($
|
2183 |
}
|
2184 |
} elseif (is_file($fullpath.'/'.$e)) {
|
2185 |
if (is_readable($fullpath.'/'.$e)) {
|
2186 |
-
$
|
2187 |
-
$files_added_so_far++;
|
2188 |
-
if ($files_added_so_far % 100 == 0) $this->log("Zip: $files_added_so_far files added");
|
2189 |
} else {
|
2190 |
$this->log("$fullpath/$e: unreadable file");
|
2191 |
$this->error("$fullpath/$e: unreadable file");
|
2192 |
}
|
2193 |
} elseif (is_dir($fullpath.'/'.$e)) {
|
2194 |
// no need to addEmptyDir here, as it gets done when we recurse
|
2195 |
-
$this->makezip_recursive_add($
|
2196 |
}
|
2197 |
}
|
2198 |
}
|
2199 |
closedir($dir_handle);
|
2200 |
}
|
2201 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2202 |
}
|
2203 |
|
2204 |
}
|
4 |
Plugin URI: http://wordpress.org/extend/plugins/updraftplus
|
5 |
Description: Backup and restore: your content and database can be automatically backed up to Amazon S3, Dropbox, Google Drive, FTP or email, on separate schedules.
|
6 |
Author: David Anderson.
|
7 |
+
Version: 1.3.25
|
8 |
Donate link: http://david.dw-perspective.org.uk/donate
|
9 |
License: GPLv3 or later
|
10 |
Author URI: http://wordshell.net
|
85 |
|
86 |
class UpdraftPlus {
|
87 |
|
88 |
+
var $version = '1.3.25';
|
89 |
var $plugin_title = 'UpdraftPlus Backup/Restore';
|
90 |
|
91 |
// Choices will be shown in the admin menu in the order used here
|
114 |
var $current_resumption;
|
115 |
var $newresumption_scheduled;
|
116 |
|
117 |
+
var $zipfiles_added;
|
118 |
+
var $zipfiles_existingfiles;
|
119 |
+
var $zipfiles_dirbatched;
|
120 |
+
var $zipfiles_batched;
|
121 |
+
|
122 |
function __construct() {
|
123 |
// Initialisation actions - takes place on plugin load
|
124 |
# Create admin page
|
721 |
|
722 |
$microtime_start = microtime(true);
|
723 |
# The paths in the zip should then begin with '$whichone', having removed WP_CONTENT_DIR from the front
|
724 |
+
$zipcode = $this->make_zipfile($create_from_dir, $zip_name);
|
725 |
+
if ($zipcode !== true) {
|
726 |
+
$this->log("ERROR: Zip failure: /*Could not create*/ $whichone zip: code=$zipcode");
|
727 |
+
$this->error("Could not create $whichone zip: code $zipcode. Consult the log file for more information.");
|
728 |
return false;
|
729 |
} else {
|
730 |
rename($full_path.'.tmp', $full_path);
|
2116 |
// Caution: $source is allowed to be an array, not just a filename
|
2117 |
function make_zipfile($source, $destination) {
|
2118 |
|
2119 |
+
// Fallback to PclZip - which my tests show is 25% slower
|
2120 |
+
if (!method_exists('ZipArchive', 'addFile')) {
|
2121 |
+
if(!class_exists('PclZip')) require_once(ABSPATH.'/wp-admin/includes/class-pclzip.php');
|
2122 |
+
$zip_object = new PclZip($destination);
|
2123 |
+
$zipcode = $zip_object->create($source, PCLZIP_OPT_REMOVE_PATH, WP_CONTENT_DIR);
|
2124 |
+
if ($zipcode == 0 ) {
|
2125 |
+
$this->log("PclZip Error: ".$zip_object->errorName());
|
2126 |
+
return $zip_object->errorCode();
|
2127 |
+
} else {
|
2128 |
+
return true;
|
2129 |
+
}
|
2130 |
+
}
|
2131 |
|
2132 |
+
$this->existing_files = array();
|
|
|
|
|
|
|
2133 |
|
2134 |
+
// TODO: Resuming! :-)
|
2135 |
+
// If the file exists, then we should grab its index of files inside, and sizes
|
2136 |
+
// Then, when we come to write a file, we should check if it's already there, and only add if it is not
|
2137 |
+
if (file_exists($destination) && is_readable($destination)) {
|
2138 |
+
$zip = new ZipArchive;
|
2139 |
+
$zip->open($destination);
|
2140 |
+
$this->log(basename($destination).": Zip file already exists, with ".$zip->numFiles." files");
|
2141 |
+
for ($i=0; $i<$zip->numFiles; $i++) {
|
2142 |
+
$si = $zip->statIndex($i);
|
2143 |
+
$name = $si['name'];
|
2144 |
+
$this->existing_files[$name] = $si['size'];
|
2145 |
+
}
|
2146 |
+
} elseif (file_exists($destination)) {
|
2147 |
+
$this->log("Zip file already exists, but is not readable; will remove: $destination");
|
2148 |
+
@unlink($destination);
|
2149 |
}
|
2150 |
+
|
2151 |
+
$this->zipfiles_added = 0;
|
2152 |
+
$this->zipfiles_dirbatched = array();
|
2153 |
+
$this->zipfiles_batched = array();
|
2154 |
+
|
2155 |
+
$last_error = -1;
|
2156 |
+
if (is_array($source)) {
|
2157 |
+
foreach ($source as $element) {
|
2158 |
+
$howmany = $this->makezip_recursive_add($destination, $element, basename($element), $element);
|
2159 |
+
if ($howmany < 0) {
|
2160 |
+
$last_error = $howmany;
|
2161 |
+
}
|
2162 |
+
}
|
2163 |
+
} else {
|
2164 |
+
$howmany = $this->makezip_recursive_add($destination, $source, basename($source), $source);
|
2165 |
+
if ($howmany < 0) {
|
2166 |
+
$last_error = $howmany;
|
2167 |
+
}
|
2168 |
+
}
|
2169 |
+
|
2170 |
+
// Any not yet dispatched?
|
2171 |
+
if (count($this->zipfiles_dirbatched)>0 || count($this->zipfiles_batched)>0) {
|
2172 |
+
$howmany = $this->makezip_addfiles($destination);
|
2173 |
+
if ($howmany < 0) {
|
2174 |
+
$last_error = $howmany;
|
2175 |
+
}
|
2176 |
+
}
|
2177 |
+
|
2178 |
+
if ($this->zipfiles_added >= 0) {
|
2179 |
+
return true;
|
2180 |
} else {
|
2181 |
+
return $last_error;
|
2182 |
}
|
2183 |
|
2184 |
+
}
|
2185 |
+
|
2186 |
+
// Q. Why don't we only open and close the zip file just once?
|
2187 |
+
// A. Because apparently PHP doesn't write out until the final close, and it will return an error if anything file has vanished in the meantime. So going directory-by-directory reduces our chances of hitting an error if the filesystem is changing underneath us (which is very possible if dealing with e.g. 1Gb of files)
|
2188 |
|
2189 |
+
// We batch up the files, rather than do them one at a time. So we are more efficient than open,one-write,close.
|
2190 |
+
function makezip_addfiles($zipfile) {
|
2191 |
+
$zip = new ZipArchive();
|
2192 |
+
if (file_exists($zipfile)) {
|
2193 |
+
$opencode = $zip->open($zipfile);
|
2194 |
+
} else {
|
2195 |
+
$opencode = $zip->open($zipfile, ZIPARCHIVE::CREATE);
|
2196 |
+
}
|
2197 |
+
if ($opencode !== true) return array($opencode, 0);
|
2198 |
+
// Make sure all directories are created before we start creating files
|
2199 |
+
while ($dir = array_pop($this->zipfiles_dirbatched)) {
|
2200 |
+
$zip->addEmptyDir($dir);
|
2201 |
+
}
|
2202 |
+
foreach ($this->zipfiles_batched as $file => $add_as) {
|
2203 |
+
if (!isset($this->existing_files[$add_as]) || $this->existing_files[$add_as] != filesize($file)) {
|
2204 |
+
$zip->addFile($file, $add_as);
|
2205 |
+
}
|
2206 |
+
$this->zipfiles_added++;
|
2207 |
+
if ($this->zipfiles_added % 100 == 0) $this->log("Zip: ".basename($zipfile).": ".$this->zipfiles_added." files added");
|
2208 |
+
}
|
2209 |
+
// Reset the array
|
2210 |
+
$this->zipfiles_batched = array();
|
2211 |
+
return $zip->close();
|
2212 |
}
|
2213 |
|
2214 |
// This function recursively packs the zip, dereferencing symlinks but packing into a single-parent tree for universal unpacking
|
2215 |
+
function makezip_recursive_add($zipfile, $fullpath, $use_path_when_storing, $original_fullpath) {
|
2216 |
|
2217 |
// De-reference
|
2218 |
$fullpath = realpath($fullpath);
|
2226 |
|
2227 |
if(is_file($fullpath)) {
|
2228 |
if (is_readable($fullpath)) {
|
2229 |
+
$key = $use_path_when_storing.'/'.basename($fullpath);
|
2230 |
+
$this->zipfiles_batched[$fullpath] = $use_path_when_storing.'/'.basename($fullpath);
|
|
|
|
|
2231 |
} else {
|
2232 |
$this->log("$fullpath: unreadable file");
|
2233 |
$this->error("$fullpath: unreadable file");
|
2234 |
}
|
2235 |
} elseif (is_dir($fullpath)) {
|
2236 |
+
if (!isset($this->existing_files[$use_path_when_storing])) $this->zipfiles_dirbatched[] = $use_path_when_storing;
|
2237 |
if (!$dir_handle = @opendir($fullpath)) {
|
2238 |
$this->log("Failed to open directory: $fullpath");
|
2239 |
$this->error("Failed to open directory: $fullpath");
|
2245 |
$deref = realpath($fullpath.'/'.$e);
|
2246 |
if (is_file($deref)) {
|
2247 |
if (is_readable($deref)) {
|
2248 |
+
$this->zipfiles_batched[$deref] = $use_path_when_storing.'/'.$e;
|
|
|
|
|
2249 |
} else {
|
2250 |
$this->log("$deref: unreadable file");
|
2251 |
$this->error("$deref: unreadable file");
|
2252 |
}
|
2253 |
} elseif (is_dir($deref)) {
|
2254 |
+
$this->makezip_recursive_add($zipfile, $deref, $use_path_when_storing.'/'.$e, $original_fullpath);
|
2255 |
}
|
2256 |
} elseif (is_file($fullpath.'/'.$e)) {
|
2257 |
if (is_readable($fullpath.'/'.$e)) {
|
2258 |
+
$this->zipfiles_batched[$fullpath.'/'.$e] = $use_path_when_storing.'/'.$e;
|
|
|
|
|
2259 |
} else {
|
2260 |
$this->log("$fullpath/$e: unreadable file");
|
2261 |
$this->error("$fullpath/$e: unreadable file");
|
2262 |
}
|
2263 |
} elseif (is_dir($fullpath.'/'.$e)) {
|
2264 |
// no need to addEmptyDir here, as it gets done when we recurse
|
2265 |
+
$this->makezip_recursive_add($zipfile, $fullpath.'/'.$e, $use_path_when_storing.'/'.$e, $original_fullpath);
|
2266 |
}
|
2267 |
}
|
2268 |
}
|
2269 |
closedir($dir_handle);
|
2270 |
}
|
2271 |
|
2272 |
+
if (count($this->zipfiles_batched) > 25) {
|
2273 |
+
$ret = $this->makezip_addfiles($zipfile);
|
2274 |
+
} else {
|
2275 |
+
$ret = true;
|
2276 |
+
}
|
2277 |
+
|
2278 |
+
return $ret;
|
2279 |
+
|
2280 |
}
|
2281 |
|
2282 |
}
|