WP Staging – DB & File Duplicator & Migration - Version 2.9.4

Version Description

  • New: Add filter to change the cache folder for creating & restoring backups #1528
  • New: Huge performance improvement for search & replace in cloning / pushing / backup process #1522
  • Fix: Call to undefined function human_readable_duration() on backup creation if WP is older than 5.1 #1527 #1525 #1535
  • Dev: Add unit tests for Times class that is used in backup listing view
  • Dev: Update db_version in SQL dumps to match WordPress 5.9 db version #1539
  • Dev: Add command to get db_version from database
Download this release

Release Info

Developer ReneHermi
Plugin Icon 128x128 WP Staging – DB & File Duplicator & Migration
Version 2.9.4
Comparing to
See all releases

Code changes from version 2.9.3 to 2.9.4

Framework/Adapter/Directory.php CHANGED
@@ -69,7 +69,9 @@ class Directory
69
  return $this->cacheDirectory;
70
  }
71
 
72
- $this->cacheDirectory = trailingslashit(wp_normalize_path($this->getPluginUploadsDirectory() . 'cache'));
 
 
73
 
74
  return $this->cacheDirectory;
75
  }
@@ -113,7 +115,9 @@ class Directory
113
  return $this->pluginUploadsDirectory;
114
  }
115
 
116
- $this->pluginUploadsDirectory = trailingslashit(wp_normalize_path($this->getUploadsDirectory() . WPSTG_PLUGIN_DOMAIN));
 
 
117
 
118
  return $this->pluginUploadsDirectory;
119
  }
69
  return $this->cacheDirectory;
70
  }
71
 
72
+ $cachePath = apply_filters('wpstg.directory.cacheDirectory', wp_normalize_path($this->getPluginUploadsDirectory() . 'cache'));
73
+
74
+ $this->cacheDirectory = trailingslashit($cachePath);
75
 
76
  return $this->cacheDirectory;
77
  }
115
  return $this->pluginUploadsDirectory;
116
  }
117
 
118
+ $pluginUploadsDir = apply_filters('wpstg.directory.pluginUploadsDirectory', wp_normalize_path($this->getUploadsDirectory() . WPSTG_PLUGIN_DOMAIN));
119
+
120
+ $this->pluginUploadsDirectory = trailingslashit($pluginUploadsDir);
121
 
122
  return $this->pluginUploadsDirectory;
123
  }
Framework/Database/SearchReplace.php CHANGED
@@ -4,6 +4,8 @@ namespace WPStaging\Framework\Database;
4
 
5
  use RuntimeException;
6
 
 
 
7
  class SearchReplace
8
  {
9
  /** @var array */
@@ -38,6 +40,9 @@ class SearchReplace
38
  $this->isWpBakeryActive = false;
39
  }
40
 
 
 
 
41
  public function getSmallerSearchLength()
42
  {
43
  if ($this->smallerReplacement < PHP_INT_MAX) {
@@ -59,6 +64,10 @@ class SearchReplace
59
  */
60
  public function replace($data)
61
  {
 
 
 
 
62
  if (!$this->search || !$this->replace) {
63
  return $data;
64
  }
@@ -177,7 +186,12 @@ class SearchReplace
177
  }
178
 
179
  // Some unserialized data cannot be re-serialized eg. SimpleXMLElements
180
- $unserialized = @unserialize($data);
 
 
 
 
 
181
  if ($unserialized !== false) {
182
  return serialize($this->walker($unserialized));
183
  }
@@ -213,7 +227,7 @@ class SearchReplace
213
  return $data;
214
  }
215
 
216
- private function strReplace($data)
217
  {
218
  $regexExclude = '';
219
  foreach ($this->exclude as $excludeString) {
4
 
5
  use RuntimeException;
6
 
7
+ use function WPStaging\functions\debug_log;
8
+
9
  class SearchReplace
10
  {
11
  /** @var array */
40
  $this->isWpBakeryActive = false;
41
  }
42
 
43
+ /**
44
+ * @return int
45
+ */
46
  public function getSmallerSearchLength()
47
  {
48
  if ($this->smallerReplacement < PHP_INT_MAX) {
64
  */
65
  public function replace($data)
66
  {
67
+ if (defined('DISABLE_WPSTG_SEARCH_REPLACE') && DISABLE_WPSTG_SEARCH_REPLACE) {
68
+ return $data;
69
+ }
70
+
71
  if (!$this->search || !$this->replace) {
72
  return $data;
73
  }
186
  }
187
 
188
  // Some unserialized data cannot be re-serialized eg. SimpleXMLElements
189
+ try {
190
+ $unserialized = @unserialize($data);
191
+ } catch (\Exception $e) {
192
+ debug_log('replaceString. Can not unserialize data. Error: ' . $e->getMessage() . ' Data: ' . $data);
193
+ }
194
+
195
  if ($unserialized !== false) {
196
  return serialize($this->walker($unserialized));
197
  }
227
  return $data;
228
  }
229
 
230
+ private function strReplace($data = '')
231
  {
232
  $regexExclude = '';
233
  foreach ($this->exclude as $excludeString) {
Framework/Filesystem/PathIdentifier.php CHANGED
@@ -4,6 +4,16 @@ namespace WPStaging\Framework\Filesystem;
4
 
5
  use WPStaging\Framework\Adapter\Directory;
6
 
 
 
 
 
 
 
 
 
 
 
7
  class PathIdentifier
8
  {
9
  const IDENTIFIER_WP_CONTENT = 'wpstg_c_';
4
 
5
  use WPStaging\Framework\Adapter\Directory;
6
 
7
+ /**
8
+ * This class is used to shorten the full file path
9
+ * to reduce the overall file size of the backup file.
10
+ *
11
+ * A file like wp-content/uploads/wp-staging-pro/wp-staging-pro.zip turn into
12
+ * wpstg_p_/wp-staging-pro/wp-staging.zip
13
+ *
14
+ * @todo rename this class to PathShortener
15
+ */
16
+
17
  class PathIdentifier
18
  {
19
  const IDENTIFIER_WP_CONTENT = 'wpstg_c_';
Framework/Traits/DbRowsGeneratorTrait.php CHANGED
@@ -20,6 +20,58 @@ trait DbRowsGeneratorTrait
20
  {
21
  use ResourceTrait;
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  /**
24
  * Returns a generator of rows.
25
  *
@@ -28,9 +80,9 @@ trait DbRowsGeneratorTrait
28
  * If the current thread is over 80% memory or execution time, then the Generator will yield `null` to stop
29
  * the processing.
30
  *
31
- * @param string $table The prefixed name of the table to pull rows from.
32
- * @param int $offset The number of row to start the work from.
33
- * @param int $limit The maximum number of rows to try and process; the actual number of
34
  * processed will depend on the server available memory and max request execution time.
35
  * @param \wpdb|null A reference to the database instance to fetch rows from.
36
  *
@@ -38,26 +90,31 @@ trait DbRowsGeneratorTrait
38
  */
39
  protected function rowsGenerator($table, $offset, $limit, \wpdb $db = null)
40
  {
41
- /* if (defined('WPSTG_DEBUG') && WPSTG_DEBUG) {
42
- \WPStaging\functions\debug_log(
43
- sprintf(
44
- 'DbRowsGeneratorTrait: max-memory-limit=%s; script-memory-limit=%s; memory-usage=%s; execution-time-limit=%s; running-time=%s; is-threshold=%s',
45
- $this->getMaxMemoryLimit(),
46
- $this->getScriptMemoryLimit(),
47
- $this->getMemoryUsage(),
48
- $this->findExecutionTimeLimit(),
49
- $this->getRunningTime(),
50
- ($this->isThreshold() ? 'yes' : 'no')
51
- )
52
- );
53
- }*/
54
-
 
55
 
56
  if (null === $db) {
57
  global $wpdb;
58
  $db = $wpdb;
59
  }
60
 
 
 
 
 
61
  $suppressErrorsOriginal = $db->suppress_errors;
62
  $db->suppress_errors(false);
63
 
@@ -69,6 +126,7 @@ trait DbRowsGeneratorTrait
69
  // More rows equals more memory; to process more let's reduce the memory footprint by using smaller fetch sizes.
70
  $batchSize = $limit / 5;
71
  $lastFetch = false;
 
72
 
73
  do {
74
  if (count($rows) === 0) {
@@ -76,21 +134,35 @@ trait DbRowsGeneratorTrait
76
  break;
77
  }
78
 
79
- $batchSize = ceil($batchSize);
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
- $rows = $db->get_results("SELECT * FROM {$table} LIMIT {$offset}, {$batchSize}", ARRAY_A);
 
82
 
83
  if (!empty($db->last_error)) {
84
  \WPStaging\functions\debug_log($db->last_error);
85
  }
86
 
 
87
  if (empty($rows)) {
88
- // We're done here.
89
  break;
90
  }
91
 
92
  if (!is_array($rows)) {
93
- \WPStaging\functions\debug_log(sprintf('$rows is not an array. Actual type: %s', gettype($rows)));
94
  }
95
 
96
  $offset += $batchSize;
@@ -101,8 +173,8 @@ trait DbRowsGeneratorTrait
101
  // Take the next row from the ready set.
102
  $row = array_shift($rows);
103
 
 
104
  if (null === $row) {
105
- // We're done, no more rows to process.
106
  break;
107
  }
108
 
20
  {
21
  use ResourceTrait;
22
 
23
+ protected $tableName = '';
24
+
25
+ /** @var object */
26
+ private $stagingSiteDb;
27
+
28
+ /** @var null */
29
+ public $hasNumericPrimaryKey = true;
30
+
31
+ /**
32
+ * @return string The primary key of the current table, if any.
33
+ */
34
+ protected function getPrimaryKey()
35
+ {
36
+
37
+ if (!$this->hasNumericPrimaryKey) {
38
+ return false;
39
+ }
40
+
41
+ $dbname = $this->stagingSiteDb->dbname;
42
+
43
+ $query = "SELECT COLUMN_NAME
44
+ FROM INFORMATION_SCHEMA.COLUMNS
45
+ WHERE TABLE_NAME = '$this->tableName'
46
+ AND TABLE_SCHEMA = '$dbname'
47
+ AND IS_NULLABLE = 'NO'
48
+ AND DATA_TYPE IN ('int', 'bigint', 'smallint', 'mediumint')
49
+ AND COLUMN_KEY = 'PRI'
50
+ AND EXTRA like '%auto_increment%';";
51
+
52
+ $primaryKey = $this->stagingSiteDb->get_results($query, ARRAY_A);
53
+
54
+ $this->stagingSiteDb->flush();
55
+
56
+ if (!$primaryKey) {
57
+ return false;
58
+ }
59
+
60
+ if (!is_array($primaryKey[0])) {
61
+ return false;
62
+ }
63
+
64
+ if (!array_key_exists('COLUMN_NAME', $primaryKey[0])) {
65
+ return false;
66
+ }
67
+
68
+ if (empty($primaryKey[0]['COLUMN_NAME'])) {
69
+ return false;
70
+ }
71
+
72
+ return $primaryKey[0]['COLUMN_NAME'];
73
+ }
74
+
75
  /**
76
  * Returns a generator of rows.
77
  *
80
  * If the current thread is over 80% memory or execution time, then the Generator will yield `null` to stop
81
  * the processing.
82
  *
83
+ * @param string $table The prefixed name of the table to pull rows from.
84
+ * @param int $offset The number of row to start the work from.
85
+ * @param int $limit The maximum number of rows to try and process; the actual number of
86
  * processed will depend on the server available memory and max request execution time.
87
  * @param \wpdb|null A reference to the database instance to fetch rows from.
88
  *
90
  */
91
  protected function rowsGenerator($table, $offset, $limit, \wpdb $db = null)
92
  {
93
+ /* if (defined('WPSTG_DEBUG') && WPSTG_DEBUG) {
94
+ \WPStaging\functions\debug_log(
95
+ sprintf(
96
+ 'DbRowsGeneratorTrait: max-memory-limit=%s; script-memory-limit=%s; memory-usage=%s; execution-time-limit=%s; running-time=%s; is-threshold=%s',
97
+ $this->getMaxMemoryLimit(),
98
+ $this->getScriptMemoryLimit(),
99
+ $this->getMemoryUsage(),
100
+ $this->findExecutionTimeLimit(),
101
+ $this->getRunningTime(),
102
+ ($this->isThreshold() ? 'yes' : 'no')
103
+ )
104
+ );
105
+ }*/
106
+
107
+ $this->tableName = $table;
108
 
109
  if (null === $db) {
110
  global $wpdb;
111
  $db = $wpdb;
112
  }
113
 
114
+ $this->stagingSiteDb = $db;
115
+
116
+ $numericPrimaryKey = ($key = $this->getPrimaryKey()) ? $key : false;
117
+
118
  $suppressErrorsOriginal = $db->suppress_errors;
119
  $db->suppress_errors(false);
120
 
126
  // More rows equals more memory; to process more let's reduce the memory footprint by using smaller fetch sizes.
127
  $batchSize = $limit / 5;
128
  $lastFetch = false;
129
+ $batchSize = ceil($batchSize);
130
 
131
  do {
132
  if (count($rows) === 0) {
134
  break;
135
  }
136
 
137
+ // Optimal! We have Primary Keys so it doesn't get slower on large offsets.
138
+ if (!empty($numericPrimaryKey)) {
139
+ $query = <<<SQL
140
+ SELECT *
141
+ FROM `{$table}`
142
+ WHERE `{$numericPrimaryKey}` > {$offset}
143
+ ORDER BY `{$numericPrimaryKey}` ASC
144
+ LIMIT 0, {$batchSize}
145
+ SQL;
146
+ } else {
147
+ $query = "SELECT * FROM `{$table}` LIMIT {$offset}, {$batchSize}";
148
+ }
149
+
150
+ $rows = $db->get_results($query, ARRAY_A);
151
 
152
+ // Call to mysql_free_result
153
+ $db->flush();
154
 
155
  if (!empty($db->last_error)) {
156
  \WPStaging\functions\debug_log($db->last_error);
157
  }
158
 
159
+ // We're done here.
160
  if (empty($rows)) {
 
161
  break;
162
  }
163
 
164
  if (!is_array($rows)) {
165
+ \WPStaging\functions\debug_log(sprintf('DbRowsGenerator: $rows is not an array. Actual type: %s', gettype($rows)));
166
  }
167
 
168
  $offset += $batchSize;
173
  // Take the next row from the ready set.
174
  $row = array_shift($rows);
175
 
176
+ // We're done, no more rows to process.
177
  if (null === $row) {
 
178
  break;
179
  }
180
 
Framework/Utils/Times.php CHANGED
@@ -28,9 +28,9 @@ class Times
28
  * Uses the `timezone_string` option to get a proper timezone if available,
29
  * otherwise falls back to an offset.
30
  *
 
31
  * @see wp_timezone_string()
32
  *
33
- * @return mixed|string|void PHP timezone string or a ±HH:MM offset.
34
  */
35
  public function getSiteTimezoneString()
36
  {
@@ -116,4 +116,159 @@ class Times
116
 
117
  return $values;
118
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  }
28
  * Uses the `timezone_string` option to get a proper timezone if available,
29
  * otherwise falls back to an offset.
30
  *
31
+ * @return mixed|string|void PHP timezone string or a ±HH:MM offset.
32
  * @see wp_timezone_string()
33
  *
 
34
  */
35
  public function getSiteTimezoneString()
36
  {
116
 
117
  return $values;
118
  }
119
+
120
+ /**
121
+ * Alternative to human_readable_duration() as it is not available for WP < 5.1
122
+ * @param string $duration Duration will be in string format (HH:ii:ss) OR (ii:ss),
123
+ * with a possible prepended negative sign (-).
124
+ * @return string|false A human readable duration string, false on failure.
125
+ */
126
+ public function getHumanReadableDuration($duration)
127
+ {
128
+ if ((empty($duration) || !is_string($duration))) {
129
+ return false;
130
+ }
131
+
132
+ $duration = trim($duration);
133
+
134
+ // Remove prepended negative sign.
135
+ if ('-' === substr($duration, 0, 1)) {
136
+ $duration = substr($duration, 1);
137
+ }
138
+
139
+ // Extract duration parts.
140
+ $duration_parts = array_reverse(explode(':', $duration));
141
+ $duration_count = count($duration_parts);
142
+
143
+ $hour = null;
144
+ $minute = null;
145
+ $second = null;
146
+
147
+ if (3 === $duration_count) {
148
+ // Validate HH:ii:ss duration format.
149
+ if (!((bool)preg_match('/^([0-9]+):([0-5]?[0-9]):([0-5]?[0-9])$/', $duration))) {
150
+ return false;
151
+ }
152
+ // Three parts: hours, minutes & seconds.
153
+ list($second, $minute, $hour) = $duration_parts;
154
+ } elseif (2 === $duration_count) {
155
+ // Validate ii:ss duration format.
156
+ if (!((bool)preg_match('/^([0-5]?[0-9]):([0-5]?[0-9])$/', $duration))) {
157
+ return false;
158
+ }
159
+ // Two parts: minutes & seconds.
160
+ list($second, $minute) = $duration_parts;
161
+ } else {
162
+ return false;
163
+ }
164
+
165
+ $human_readable_duration = [];
166
+
167
+ // Add the hour part to the string.
168
+ if (is_numeric($hour)) {
169
+ /* translators: %s: Time duration in hour or hours. */
170
+ $human_readable_duration[] = sprintf(_n('%s hour', '%s hours', $hour), (int)$hour);
171
+ }
172
+
173
+ // Add the minute part to the string.
174
+ if (is_numeric($minute)) {
175
+ /* translators: %s: Time duration in minute or minutes. */
176
+ $human_readable_duration[] = sprintf(_n('%s minute', '%s minutes', $minute), (int)$minute);
177
+ }
178
+
179
+ // Add the second part to the string.
180
+ if (is_numeric($second)) {
181
+ /* translators: %s: Time duration in second or seconds. */
182
+ $human_readable_duration[] = sprintf(_n('%s second', '%s seconds', $second), (int)$second);
183
+ }
184
+
185
+ return implode(', ', $human_readable_duration);
186
+ }
187
+
188
+ /**
189
+ *
190
+ * Alternative to human_time_diff() as it has been changed in WP 5.3
191
+ * Determines the difference between two timestamps.
192
+ *
193
+ * The difference is returned in a human readable format such as "1 hour",
194
+ * "5 mins", "2 days".
195
+ *
196
+ * @param int $from Unix timestamp from which the difference begins.
197
+ * @param int $to Optional. Unix timestamp to end the time difference. Default becomes time() if not set.
198
+ * @return string Human readable time difference.
199
+ * @since 5.3.0 Added support for showing a difference in seconds.
200
+ *
201
+ * @since 1.5.0
202
+ */
203
+ public function getHumanTimeDiff($from, $to = 0)
204
+ {
205
+ if (empty($to)) {
206
+ $to = time();
207
+ }
208
+
209
+ $diff = (int)abs($to - $from);
210
+
211
+ if ($diff < MINUTE_IN_SECONDS) {
212
+ $secs = $diff;
213
+ if ($secs <= 1) {
214
+ $secs = 1;
215
+ }
216
+ /* translators: Time difference between two dates, in seconds. %s: Number of seconds. */
217
+ $since = sprintf(_n('%s second', '%s seconds', $secs), $secs);
218
+ } elseif ($diff < HOUR_IN_SECONDS) {
219
+ $mins = round($diff / MINUTE_IN_SECONDS);
220
+ if ($mins <= 1) {
221
+ $mins = 1;
222
+ }
223
+ /* translators: Time difference between two dates, in minutes (min=minute). %s: Number of minutes. */
224
+ $since = sprintf(_n('%s min', '%s mins', $mins), $mins);
225
+ } elseif ($diff < DAY_IN_SECONDS) {
226
+ $hours = round($diff / HOUR_IN_SECONDS);
227
+ if ($hours <= 1) {
228
+ $hours = 1;
229
+ }
230
+ /* translators: Time difference between two dates, in hours. %s: Number of hours. */
231
+ $since = sprintf(_n('%s hour', '%s hours', $hours), $hours);
232
+ } elseif ($diff < WEEK_IN_SECONDS) {
233
+ $days = round($diff / DAY_IN_SECONDS);
234
+ if ($days <= 1) {
235
+ $days = 1;
236
+ }
237
+ /* translators: Time difference between two dates, in days. %s: Number of days. */
238
+ $since = sprintf(_n('%s day', '%s days', $days), $days);
239
+ } elseif ($diff < MONTH_IN_SECONDS) {
240
+ $weeks = round($diff / WEEK_IN_SECONDS);
241
+ if ($weeks <= 1) {
242
+ $weeks = 1;
243
+ }
244
+ /* translators: Time difference between two dates, in weeks. %s: Number of weeks. */
245
+ $since = sprintf(_n('%s week', '%s weeks', $weeks), $weeks);
246
+ } elseif ($diff < YEAR_IN_SECONDS) {
247
+ $months = round($diff / MONTH_IN_SECONDS);
248
+ if ($months <= 1) {
249
+ $months = 1;
250
+ }
251
+ /* translators: Time difference between two dates, in months. %s: Number of months. */
252
+ $since = sprintf(_n('%s month', '%s months', $months), $months);
253
+ } elseif ($diff >= YEAR_IN_SECONDS) {
254
+ $years = round($diff / YEAR_IN_SECONDS);
255
+ if ($years <= 1) {
256
+ $years = 1;
257
+ }
258
+ /* translators: Time difference between two dates, in years. %s: Number of years. */
259
+ $since = sprintf(_n('%s year', '%s years', $years), $years);
260
+ }
261
+
262
+ /**
263
+ * Filters the human readable difference between two timestamps.
264
+ *
265
+ * @param string $since The difference in human readable text.
266
+ * @param int $diff The difference in seconds.
267
+ * @param int $from Unix timestamp from which the difference begins.
268
+ * @param int $to Unix timestamp to end the time difference.
269
+ * @since 4.0.0
270
+ *
271
+ */
272
+ return apply_filters('human_time_diff', $since, $diff, $from, $to);
273
+ }
274
  }
bootstrap.php CHANGED
@@ -52,7 +52,7 @@ if (!defined('WPSTG_FEATURE_ENABLE_BACKUP')) {
52
  define('WPSTG_FEATURE_ENABLE_BACKUP', true);
53
  }
54
 
55
- // Define bytes consts if not already defined for backward compatibility
56
  if (!defined('KB_IN_BYTES')) {
57
  define('KB_IN_BYTES', 1024);
58
  }
@@ -65,6 +65,30 @@ if (!defined('GB_IN_BYTES')) {
65
  define('GB_IN_BYTES', 1024 * MB_IN_BYTES);
66
  }
67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  /**
69
  * Register specific Pro and Free constants. We register them here instead of on the
70
  * entrypoint because we want to make sure we are defining constants for the plugins
52
  define('WPSTG_FEATURE_ENABLE_BACKUP', true);
53
  }
54
 
55
+ // Define WordPress default constants if not already defined in outdated WP version for backward compatibility
56
  if (!defined('KB_IN_BYTES')) {
57
  define('KB_IN_BYTES', 1024);
58
  }
65
  define('GB_IN_BYTES', 1024 * MB_IN_BYTES);
66
  }
67
 
68
+ if (!defined('MINUTE_IN_SECONDS')) {
69
+ define('MINUTE_IN_SECONDS', 60);
70
+ }
71
+
72
+ if (!defined('HOUR_IN_SECONDS')) {
73
+ define('HOUR_IN_SECONDS', 60 * MINUTE_IN_SECONDS);
74
+ }
75
+
76
+ if (!defined('DAY_IN_SECONDS')) {
77
+ define('DAY_IN_SECONDS', 24 * HOUR_IN_SECONDS);
78
+ }
79
+
80
+ if (!defined('WEEK_IN_SECONDS')) {
81
+ define('WEEK_IN_SECONDS', 7 * DAY_IN_SECONDS);
82
+ }
83
+
84
+ if (!defined('MONTH_IN_SECONDS')) {
85
+ define('MONTH_IN_SECONDS', 30 * DAY_IN_SECONDS);
86
+ }
87
+
88
+ if (!defined('YEAR_IN_SECONDS')) {
89
+ define('YEAR_IN_SECONDS', 365 * DAY_IN_SECONDS);
90
+ }
91
+
92
  /**
93
  * Register specific Pro and Free constants. We register them here instead of on the
94
  * entrypoint because we want to make sure we are defining constants for the plugins
constantsFree.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // WP STAGING version number
4
  if (!defined('WPSTG_VERSION')) {
5
- define('WPSTG_VERSION', '2.9.3');
6
  }
7
 
8
  // Compatible up to WordPress Version
2
 
3
  // WP STAGING version number
4
  if (!defined('WPSTG_VERSION')) {
5
+ define('WPSTG_VERSION', '2.9.4');
6
  }
7
 
8
  // Compatible up to WordPress Version
opcacheBootstrap.php CHANGED
@@ -45,7 +45,7 @@ if (!$canInvalidate) {
45
  *
46
  * We use the "Version" from the headers of the main file of the plugin to compare.
47
  */
48
- $runtimeVersionDifferentFromBuildVersion = get_file_data($pluginFilePath, ['Version' => 'Version'])['Version'] !== '2.9.3';
49
  $lastCheckHappenedAfterInterval = current_time('timestamp') > (int)get_site_transient('wpstg.bootstrap.opcache.lastCleared') + 5 * MINUTE_IN_SECONDS;
50
 
51
  $shouldClearOpCache = apply_filters('wpstg.bootstrap.opcache.shouldClear', $runtimeVersionDifferentFromBuildVersion && $lastCheckHappenedAfterInterval);
45
  *
46
  * We use the "Version" from the headers of the main file of the plugin to compare.
47
  */
48
+ $runtimeVersionDifferentFromBuildVersion = get_file_data($pluginFilePath, ['Version' => 'Version'])['Version'] !== '2.9.4';
49
  $lastCheckHappenedAfterInterval = current_time('timestamp') > (int)get_site_transient('wpstg.bootstrap.opcache.lastCleared') + 5 * MINUTE_IN_SECONDS;
50
 
51
  $shouldClearOpCache = apply_filters('wpstg.bootstrap.opcache.shouldClear', $runtimeVersionDifferentFromBuildVersion && $lastCheckHappenedAfterInterval);
readme.txt CHANGED
@@ -9,7 +9,7 @@ License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
  Tags: backup, database backup, staging, duplication, clone
10
  Requires at least: 3.6+
11
  Tested up to: 5.8
12
- Stable tag: 2.9.3
13
  Requires PHP: 5.6
14
 
15
  Backup & Duplicator Plugin - Clone, move, duplicate & migrate websites to staging, backup, and development sites for authorized users only.
@@ -185,6 +185,14 @@ https://wp-staging.com
185
 
186
  == Changelog ==
187
 
 
 
 
 
 
 
 
 
188
  = 2.9.3 =
189
  * New: Add support for WordPress 5.8.3
190
  * New: Add filter for excluding files during cloning / backup #1494
9
  Tags: backup, database backup, staging, duplication, clone
10
  Requires at least: 3.6+
11
  Tested up to: 5.8
12
+ Stable tag: 2.9.4
13
  Requires PHP: 5.6
14
 
15
  Backup & Duplicator Plugin - Clone, move, duplicate & migrate websites to staging, backup, and development sites for authorized users only.
185
 
186
  == Changelog ==
187
 
188
+ = 2.9.4 =
189
+ * New: Add filter to change the cache folder for creating & restoring backups #1528
190
+ * New: Huge performance improvement for search & replace in cloning / pushing / backup process #1522
191
+ * Fix: Call to undefined function human_readable_duration() on backup creation if WP is older than 5.1 #1527 #1525 #1535
192
+ * Dev: Add unit tests for Times class that is used in backup listing view
193
+ * Dev: Update db_version in SQL dumps to match WordPress 5.9 db version #1539
194
+ * Dev: Add command to get db_version from database
195
+
196
  = 2.9.3 =
197
  * New: Add support for WordPress 5.8.3
198
  * New: Add filter for excluding files during cloning / backup #1494
wp-staging.php CHANGED
@@ -7,7 +7,7 @@
7
  * Author: WP-STAGING
8
  * Author URI: https://wp-staging.com
9
  * Contributors: ReneHermi
10
- * Version: 2.9.3
11
  * Text Domain: wp-staging
12
  * Domain Path: /languages/
13
  *
7
  * Author: WP-STAGING
8
  * Author URI: https://wp-staging.com
9
  * Contributors: ReneHermi
10
+ * Version: 2.9.4
11
  * Text Domain: wp-staging
12
  * Domain Path: /languages/
13
  *