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

Version Description

  • New: Add support for WordPress 5.8.3
  • New: Add filter for excluding files during cloning / backup #1494
  • New: Add filter for overwriting max execution for database backup restore #1512
  • New: Add filter to allow overwriting of the maximum allowed request time to make sure backup restore works for huge files. (19.000.000M database rows) #1510
  • Tweak: Show custom uploads folder in tooltip description and explain better that changing a symlink image will also change the image on the production site. #1495
  • Fix: If cloning a multisite subsite into external database, it does not clone / backup wp_users and wp_usermeta #1515
  • Fix: Skip tmp single file plugin during backup PUSH copy process #1491
  • Fix: Preserve table selection during PUSH and UPDATE even when all backup tables unselected #1488
  • Fix: Make sure maximum memory consumption during cloning or backup is never higher than 256MB #1502
  • Fix: Use custom implementation of wp_timezone() for backward compatibility to WordPress older than 5.3 #1505
  • Fix: Override FileObject::fgets to make them behave exactly from SplFileObject of PHP < 8.0.1 #1506
Download this release

Release Info

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

Code changes from version 2.9.2 to 2.9.3

Backend/Administrator.php CHANGED
@@ -18,6 +18,9 @@ use WPStaging\Framework\Filesystem\Filters\ExcludeFilter;
18
  use WPStaging\Framework\TemplateEngine\TemplateEngine;
19
  use WPStaging\Framework\CloningProcess\Database\CompareExternalDatabase;
20
  use WPStaging\Framework\Utils\Math;
 
 
 
21
  use WPStaging\Backend\Modules\Jobs\Cancel;
22
  use WPStaging\Backend\Modules\Jobs\CancelUpdate;
23
  use WPStaging\Backend\Modules\Jobs\Cloning;
@@ -35,8 +38,6 @@ use WPStaging\Backend\Feedback;
35
  use WPStaging\Backend\Pro\Modules\Jobs\Processing;
36
  use WPStaging\Backend\Pro\Modules\Jobs\Backups\BackupUploadsDir;
37
  use WPStaging\Backend\Pluginmeta\Pluginmeta;
38
- use WPStaging\Framework\Notices\DismissNotice;
39
- use WPStaging\Framework\Staging\Sites;
40
 
41
  /**
42
  * Class Administrator
@@ -638,6 +639,7 @@ class Administrator
638
  // Get Options
639
  $options = $scan->getOptions();
640
  $excludeUtils = new ExcludeFilter();
 
641
  require_once "{$this->path}views/clone/ajax/scan.php";
642
 
643
  wp_die();
18
  use WPStaging\Framework\TemplateEngine\TemplateEngine;
19
  use WPStaging\Framework\CloningProcess\Database\CompareExternalDatabase;
20
  use WPStaging\Framework\Utils\Math;
21
+ use WPStaging\Framework\Utils\WpDefaultDirectories;
22
+ use WPStaging\Framework\Notices\DismissNotice;
23
+ use WPStaging\Framework\Staging\Sites;
24
  use WPStaging\Backend\Modules\Jobs\Cancel;
25
  use WPStaging\Backend\Modules\Jobs\CancelUpdate;
26
  use WPStaging\Backend\Modules\Jobs\Cloning;
38
  use WPStaging\Backend\Pro\Modules\Jobs\Processing;
39
  use WPStaging\Backend\Pro\Modules\Jobs\Backups\BackupUploadsDir;
40
  use WPStaging\Backend\Pluginmeta\Pluginmeta;
 
 
41
 
42
  /**
43
  * Class Administrator
639
  // Get Options
640
  $options = $scan->getOptions();
641
  $excludeUtils = new ExcludeFilter();
642
+ $wpDefaultDirectories = new WpDefaultDirectories();
643
  require_once "{$this->path}views/clone/ajax/scan.php";
644
 
645
  wp_die();
Backend/Modules/Jobs/Cloning.php CHANGED
@@ -66,7 +66,7 @@ class Cloning extends Job
66
  $this->options->includedDirectories = [];
67
  $this->options->excludedDirectories = [];
68
  $this->options->extraDirectories = [];
69
- $this->options->excludedFiles = [
70
  '.htaccess',
71
  '.DS_Store',
72
  '*.git',
@@ -78,7 +78,7 @@ class Cloning extends Job
78
  'web.config', // Important: Windows IIS configuration file. Must not be in the staging site!
79
  '.wp-staging', // Determines if a site is a staging site
80
  '.wp-staging-cloneable', // File which make staging site to be cloneable
81
- ];
82
 
83
  $this->options->excludedFilesFullPath = [
84
  $this->dirUtils->getRelativeWpContentPath(SlashMode::TRAILING_SLASH) . 'db.php',
66
  $this->options->includedDirectories = [];
67
  $this->options->excludedDirectories = [];
68
  $this->options->extraDirectories = [];
69
+ $this->options->excludedFiles = apply_filters('wpstg_clone_excluded_files' ,[
70
  '.htaccess',
71
  '.DS_Store',
72
  '*.git',
78
  'web.config', // Important: Windows IIS configuration file. Must not be in the staging site!
79
  '.wp-staging', // Determines if a site is a staging site
80
  '.wp-staging-cloneable', // File which make staging site to be cloneable
81
+ ]);
82
 
83
  $this->options->excludedFilesFullPath = [
84
  $this->dirUtils->getRelativeWpContentPath(SlashMode::TRAILING_SLASH) . 'db.php',
Backend/Modules/Jobs/Database.php CHANGED
@@ -242,18 +242,6 @@ class Database extends CloningProcess
242
  $this->options->job->start = 0;
243
  }
244
 
245
- /**
246
- * Copy data from old table to new table
247
- * @param string $newTableName
248
- * @param string $oldTableName
249
- */
250
- private function copyData($newTableName, $oldTableName)
251
- {
252
- $this->databaseCloningService->copyData($oldTableName, $newTableName, $this->options->job->start, $this->settings->queryLimit);
253
- // Set new offset
254
- $this->options->job->start += $this->settings->queryLimit;
255
- }
256
-
257
  /**
258
  * @param mixed string|object $tableName
259
  * @return bool
@@ -291,6 +279,18 @@ class Database extends CloningProcess
291
  return $this->finishStep();
292
  }
293
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  /**
295
  * Is table excluded from database copying processing?
296
  * @param string $table
@@ -363,6 +363,11 @@ class Database extends CloningProcess
363
  private function addMissingTables()
364
  {
365
  $dbPrefix = WPStaging::getTablePrefix();
 
 
 
 
 
366
  if (!in_array($dbPrefix . 'users', $this->options->tables)) {
367
  $this->options->tables[] = $dbPrefix . 'users';
368
  $this->saveOptions();
242
  $this->options->job->start = 0;
243
  }
244
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  /**
246
  * @param mixed string|object $tableName
247
  * @return bool
279
  return $this->finishStep();
280
  }
281
 
282
+ /**
283
+ * Copy data from old table to new table
284
+ * @param string $destTableName
285
+ * @param string $srcTableName
286
+ */
287
+ private function copyData($destTableName, $srcTableName)
288
+ {
289
+ $this->databaseCloningService->copyData($srcTableName, $destTableName, $this->options->job->start, $this->settings->queryLimit);
290
+ // Set new offset
291
+ $this->options->job->start += $this->settings->queryLimit;
292
+ }
293
+
294
  /**
295
  * Is table excluded from database copying processing?
296
  * @param string $table
363
  private function addMissingTables()
364
  {
365
  $dbPrefix = WPStaging::getTablePrefix();
366
+ // Early bail: if updating
367
+ if (isset($this->options->mainJob) && $this->options->mainJob === 'updating') {
368
+ return;
369
+ }
370
+
371
  if (!in_array($dbPrefix . 'users', $this->options->tables)) {
372
  $this->options->tables[] = $dbPrefix . 'users';
373
  $this->saveOptions();
Backend/Modules/Jobs/Files.php CHANGED
@@ -263,7 +263,7 @@ class Files extends JobExecutable
263
  $this->file->seek($this->options->copiedFiles - 1);
264
  }
265
 
266
- $this->file->setFlags(FileObject::DROP_NEW_LINE);
267
 
268
  for ($i = 0; $i < $this->maxFilesPerRun; $i++) {
269
  // Increment copied files
263
  $this->file->seek($this->options->copiedFiles - 1);
264
  }
265
 
266
+ $this->file->setFlags(FileObject::READ_AHEAD | FileObject::DROP_NEW_LINE);
267
 
268
  for ($i = 0; $i < $this->maxFilesPerRun; $i++) {
269
  // Increment copied files
Backend/Modules/SystemInfo.php CHANGED
@@ -445,12 +445,12 @@ class SystemInfo
445
  public function php()
446
  {
447
  $output = $this->header("PHP Configuration");
448
- $output .= $this->info("Safe Mode:", ($this->isSafeModeEnabled() ? "Enabled" : "Disabled"));
449
  $output .= $this->info("PHP Max Memory Limit:", ini_get("memory_limit"));
450
- $output .= $this->info("Upload Max Size:", ini_get("upload_max_filesize"));
 
 
451
  $output .= $this->info("Post Max Size:", ini_get("post_max_size"));
452
  $output .= $this->info("Upload Max Filesize:", ini_get("upload_max_filesize"));
453
- $output .= $this->info("Time Limit:", ini_get("max_execution_time"));
454
  $output .= $this->info("Max Input Vars:", ini_get("max_input_vars"));
455
  $output .= $this->info("PHP User:", $this->getPHPUser());
456
 
445
  public function php()
446
  {
447
  $output = $this->header("PHP Configuration");
 
448
  $output .= $this->info("PHP Max Memory Limit:", ini_get("memory_limit"));
449
+ $output .= $this->info("Max Execution Time:", ini_get("max_execution_time"));
450
+ $output .= $this->info("Safe Mode:", ($this->isSafeModeEnabled() ? "Enabled" : "Disabled"));
451
+ $output .= $this->info("Upload Max File Size:", ini_get("upload_max_filesize"));
452
  $output .= $this->info("Post Max Size:", ini_get("post_max_size"));
453
  $output .= $this->info("Upload Max Filesize:", ini_get("upload_max_filesize"));
 
454
  $output .= $this->info("Max Input Vars:", ini_get("max_input_vars"));
455
  $output .= $this->info("PHP User:", $this->getPHPUser());
456
 
Backend/views/backup/modal/export.php CHANGED
@@ -95,20 +95,20 @@ $recurrenceTimes = $time->range('midnight', 'tomorrow - 60 minutes', defined('WP
95
  <?php _e(sprintf('Relative to current server time, which you can change in <a href="%s">WordPress Settings</a>.', admin_url('options-general.php#timezone_string'))); ?>
96
  <br>
97
  <br>
98
- <?php _e(sprintf('Current Server Time: %s', (new DateTime('now', wp_timezone()))->format($timeFormatOption)), 'wp-staging'); ?>
99
  <br>
100
- <?php _e(sprintf('Site Timezone: %s', wp_timezone_string()), 'wp-staging'); ?>
101
  </span>
102
  </div>
103
  </label>
104
  <select name="backupScheduleTime" id="backupScheduleTime">
105
- <?php foreach ($recurrenceTimes as $time) : ?>
106
- <option value="<?php echo esc_attr($time->format('H:i')) ?>">
107
- <?php echo esc_html($time->format($timeFormatOption)) ?>
108
  </option>
109
  <?php endforeach; ?>
110
  </select>
111
- <span id="backup-schedule-current-time"><?php _e(sprintf('Current Time: %s', (new DateTime('now', wp_timezone()))->format($timeFormatOption)), 'wp-staging'); ?></span>
112
  <label for="backupScheduleRotation">
113
  <?php esc_html_e('How many backups to keep?', 'wp-staging'); ?>
114
  <div class="wpstg--tooltip" style="position: absolute;">
95
  <?php _e(sprintf('Relative to current server time, which you can change in <a href="%s">WordPress Settings</a>.', admin_url('options-general.php#timezone_string'))); ?>
96
  <br>
97
  <br>
98
+ <?php _e(sprintf('Current Server Time: %s', (new DateTime('now', $time->getSiteTimezoneObject()))->format($timeFormatOption)), 'wp-staging'); ?>
99
  <br>
100
+ <?php _e(sprintf('Site Timezone: %s', $time->getSiteTimezoneString()), 'wp-staging'); ?>
101
  </span>
102
  </div>
103
  </label>
104
  <select name="backupScheduleTime" id="backupScheduleTime">
105
+ <?php foreach ($recurrenceTimes as $recurTime) : ?>
106
+ <option value="<?php echo esc_attr($recurTime->format('H:i')) ?>">
107
+ <?php echo esc_html($recurTime->format($timeFormatOption)) ?>
108
  </option>
109
  <?php endforeach; ?>
110
  </select>
111
+ <span id="backup-schedule-current-time"><?php _e(sprintf('Current Time: %s', (new DateTime('now', $time->getSiteTimezoneObject()))->format($timeFormatOption)), 'wp-staging'); ?></span>
112
  <label for="backupScheduleRotation">
113
  <?php esc_html_e('How many backups to keep?', 'wp-staging'); ?>
114
  <div class="wpstg--tooltip" style="position: absolute;">
Backend/views/clone/ajax/custom-directory.php CHANGED
@@ -6,11 +6,12 @@ use WPStaging\Backend\Modules\Jobs\Scan;
6
 
7
  /**
8
  * This file is currently being called for the both FREE and PRO version:
9
- * src/Backend/views/clone/ajax/scan.php:63
10
  *
11
  * @var Scan $scan
12
  * @var stdClass $options
13
  * @var boolean $isPro
 
14
  *
15
  * @see \WPStaging\Backend\Modules\Jobs\Scan::start For details on $options.
16
  */
@@ -33,7 +34,6 @@ if (is_multisite() && !SUBDOMAIN_INSTALL) {
33
  }
34
  $customHostname = $hostname;
35
 
36
-
37
  // Apply Filters in only PRO version
38
  if ($isPro) {
39
  $hostname = apply_filters('wpstg_cloning_target_hostname', $hostname);
@@ -55,7 +55,8 @@ if ($isPro && !empty($options->current) && $options->current !== null) {
55
  $customDir = $directory;
56
  $uploadsSymlinked = isset($options->existingClones[$options->current]['uploadsSymlinked']) && $options->existingClones[$options->current]['uploadsSymlinked'];
57
  $proSettingsDisabled = true;
58
- } ?>
 
59
 
60
  <p class="wpstg--advance-settings--checkbox">
61
  <label for="wpstg-change-dest"><?php _e('Change Destination'); ?></label>
@@ -113,7 +114,7 @@ if ($isPro && !empty($options->current) && $options->current !== null) {
113
  <span class="wpstg--tooltip">
114
  <img class="wpstg--dashicons" src="<?php echo $scan->getInfoIcon(); ?>" alt="info" />
115
  <span class="wpstg--tooltiptext">
116
- <?php _e('Activate to symlink the folder <code>wp-content/uploads</code> to the production site. All files including images on the production site\'s uploads folder will be linked to the staging site uploads folder. This will speed up the cloning and pushing process tremendously as no files from the uploads folder are copied between both sites. Note: this can lead to mixed and shared content issues if both site loads (custom) stylesheet files from the same wp-content/uploads folder. Use this with care!', 'wp-staging'); ?>
117
  <br/>
118
  <br/>
119
  <?php _e('<strong>This feature only works if the staging site is on the same hosting as the production site.</strong>', 'wp-staging'); ?>
6
 
7
  /**
8
  * This file is currently being called for the both FREE and PRO version:
9
+ * @see src/Backend/views/clone/ajax/scan.php:76
10
  *
11
  * @var Scan $scan
12
  * @var stdClass $options
13
  * @var boolean $isPro
14
+ * @var stdClass $wpDefaultDirectories
15
  *
16
  * @see \WPStaging\Backend\Modules\Jobs\Scan::start For details on $options.
17
  */
34
  }
35
  $customHostname = $hostname;
36
 
 
37
  // Apply Filters in only PRO version
38
  if ($isPro) {
39
  $hostname = apply_filters('wpstg_cloning_target_hostname', $hostname);
55
  $customDir = $directory;
56
  $uploadsSymlinked = isset($options->existingClones[$options->current]['uploadsSymlinked']) && $options->existingClones[$options->current]['uploadsSymlinked'];
57
  $proSettingsDisabled = true;
58
+ }
59
+ ?>
60
 
61
  <p class="wpstg--advance-settings--checkbox">
62
  <label for="wpstg-change-dest"><?php _e('Change Destination'); ?></label>
114
  <span class="wpstg--tooltip">
115
  <img class="wpstg--dashicons" src="<?php echo $scan->getInfoIcon(); ?>" alt="info" />
116
  <span class="wpstg--tooltiptext">
117
+ <?php echo sprintf(__('Activate to symlink the folder %s%s%s to the production site. %s All files including images on the production site\'s uploads folder will be linked to the staging site uploads folder. This will speed up the cloning and pushing process tremendously as no files from the uploads folder are copied between both sites. %s Note: this can lead to mixed and shared content issues if both site loads (custom) stylesheet files from the same uploads folder. %s Using this option means changing images on the staging site will change images on the production site as well. Use this with care! %s', 'wp-staging'), '<code>', $wpDefaultDirectories->getRelativeUploadPath(), '</code>', '<br><br>', '<br><br>', '<br><br><strong>', '</strong>');?>
118
  <br/>
119
  <br/>
120
  <?php _e('<strong>This feature only works if the staging site is on the same hosting as the production site.</strong>', 'wp-staging'); ?>
Framework/Adapter/Directory.php CHANGED
@@ -137,6 +137,9 @@ class Directory
137
  return $this->uploadDir;
138
  }
139
 
 
 
 
140
  public function getDefaultWordPressFolders()
141
  {
142
  if (!isset($this->defaultWordPressFolders)) {
137
  return $this->uploadDir;
138
  }
139
 
140
+ /**
141
+ * @return array
142
+ */
143
  public function getDefaultWordPressFolders()
144
  {
145
  if (!isset($this->defaultWordPressFolders)) {
Framework/CloningProcess/Database/DatabaseCloningService.php CHANGED
@@ -40,7 +40,7 @@ class DatabaseCloningService
40
  */
41
  public function copyData($srcTableName, $destTableName, $offset, $limit)
42
  {
43
- // Don't replace the table name if the table prefix is a custom one and if it is cloned into external database
44
  if (!$this->shouldRenameTable($srcTableName)) {
45
  $destTableName = $srcTableName;
46
  }
@@ -165,17 +165,40 @@ class DatabaseCloningService
165
  }
166
 
167
  /**
 
168
  * @param string
169
  * @return bool
170
  */
171
  private function shouldRenameTable($srcTable)
172
  {
 
 
 
173
  if ($this->dto->isExternal() && !$this->beginsWithWordPressPrefix($srcTable)) {
174
  return false;
175
  }
176
  return true;
177
  }
178
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  /**
180
  * @param string $destTableName
181
  * @param string $srcTableName
@@ -196,7 +219,7 @@ class DatabaseCloningService
196
  // Replace whole table name if it begins with WordPress prefix.
197
  // Don't replace it if it's a custom table beginning with another prefix #1303
198
  // Prevents bug where $old table prefix contains no underscore | Fix missing underscore issue #251.
199
- if ($this->beginsWithWordPressPrefix($srcTableName)) {
200
  $sql = str_replace("CREATE TABLE `$srcTableName`", "CREATE TABLE `$destTableName`", $sql);
201
  }
202
 
@@ -232,7 +255,7 @@ class DatabaseCloningService
232
  * @param $tableName
233
  * @return string
234
  */
235
- public function removeDBBasePrefix($tableName)
236
  {
237
  return (new Strings())->str_replace_first(WPStaging::getTableBasePrefix(), null, $tableName);
238
  }
@@ -294,19 +317,21 @@ class DatabaseCloningService
294
  if ($this->dto->isMultisite()) {
295
  // Convert prefix and entire table name to lowercase to prevent capitalization issues:
296
  // https://dev.mysql.com/doc/refman/5.7/en/identifier-case-sensitivity.html
 
297
  // @todo Testing! Can lead to issues with CONSTRAINTS
 
298
  // Edit: Disabled as we must not change the capitalization of the prefix any longer!
299
  // This prevented sites from proper cloning where prefix contains capitalized letters
300
- // Keep this here for historical purposes and to make sure no one tries to implement this again!
301
- //$row[0] = str_replace($tableName, strtolower($tableName), $row[0]);
302
 
303
  // Build full qualified statement for table [prefix_]users from main site e.g. wp_users
304
- if ($this->removeDBBasePrefix($tableName) === 'users') {
305
- $statement = str_replace($tableName, $productionDb->prefix . 'users', $statement);
306
  }
307
  // Build full qualified statement for table [prefix_]usermeta from main site e.g. wp_usermeta
308
- if ($this->removeDBBasePrefix($tableName) === 'usermeta') {
309
- $statement = str_replace($tableName, $productionDb->prefix . 'usermeta', $statement);
310
  }
311
  }
312
 
40
  */
41
  public function copyData($srcTableName, $destTableName, $offset, $limit)
42
  {
43
+ // Don't replace the table name if the table prefix is a custom prefix and if table is cloned into external database
44
  if (!$this->shouldRenameTable($srcTableName)) {
45
  $destTableName = $srcTableName;
46
  }
165
  }
166
 
167
  /**
168
+ * If table is not multisite user or usermeta table and does not
169
  * @param string
170
  * @return bool
171
  */
172
  private function shouldRenameTable($srcTable)
173
  {
174
+ if ($this->dto->isExternal() && $this->isMultisiteWpCoreTable($srcTable)) {
175
+ return true;
176
+ }
177
  if ($this->dto->isExternal() && !$this->beginsWithWordPressPrefix($srcTable)) {
178
  return false;
179
  }
180
  return true;
181
  }
182
 
183
+ /**
184
+ * @param $tableName
185
+ * @return bool
186
+ */
187
+ private function isMultisiteWpCoreTable($tableName)
188
+ {
189
+ $basePrefix = $this->dto->getProductionDb()->base_prefix;
190
+
191
+ $coreTables = [
192
+ $basePrefix . 'users',
193
+ $basePrefix . 'usermeta'
194
+ ];
195
+
196
+ if (in_array($tableName, $coreTables)) {
197
+ return true;
198
+ }
199
+ return false;
200
+ }
201
+
202
  /**
203
  * @param string $destTableName
204
  * @param string $srcTableName
219
  // Replace whole table name if it begins with WordPress prefix.
220
  // Don't replace it if it's a custom table beginning with another prefix #1303
221
  // Prevents bug where $old table prefix contains no underscore | Fix missing underscore issue #251.
222
+ if ($this->beginsWithWordPressPrefix($srcTableName) || $this->isMultisiteWpCoreTable($srcTableName)) {
223
  $sql = str_replace("CREATE TABLE `$srcTableName`", "CREATE TABLE `$destTableName`", $sql);
224
  }
225
 
255
  * @param $tableName
256
  * @return string
257
  */
258
+ public function removeDbBasePrefix($tableName)
259
  {
260
  return (new Strings())->str_replace_first(WPStaging::getTableBasePrefix(), null, $tableName);
261
  }
317
  if ($this->dto->isMultisite()) {
318
  // Convert prefix and entire table name to lowercase to prevent capitalization issues:
319
  // https://dev.mysql.com/doc/refman/5.7/en/identifier-case-sensitivity.html
320
+
321
  // @todo Testing! Can lead to issues with CONSTRAINTS
322
+
323
  // Edit: Disabled as we must not change the capitalization of the prefix any longer!
324
  // This prevented sites from proper cloning where prefix contains capitalized letters
325
+ // Keep this here for reference purposes and to make sure no one EVER tries to implement this again!
326
+ // $row[0] = str_replace($tableName, strtolower($tableName), $row[0]);
327
 
328
  // Build full qualified statement for table [prefix_]users from main site e.g. wp_users
329
+ if ($this->removeDbBasePrefix($tableName) === 'users') {
330
+ $statement = str_replace($tableName, $productionDb->base_prefix . 'users', $statement);
331
  }
332
  // Build full qualified statement for table [prefix_]usermeta from main site e.g. wp_usermeta
333
+ if ($this->removeDbBasePrefix($tableName) === 'usermeta') {
334
+ $statement = str_replace($tableName, $productionDb->base_prefix . 'usermeta', $statement);
335
  }
336
  }
337
 
Framework/Filesystem/DebugLogReader.php CHANGED
@@ -119,7 +119,7 @@ class DebugLogReader
119
  $debugLines = [];
120
 
121
  do {
122
- $line = trim($debugFile->fgets());
123
  $line = html_entity_decode($line);
124
  $line = sanitize_text_field($line);
125
  $debugLines[] = $line;
119
  $debugLines = [];
120
 
121
  do {
122
+ $line = trim($debugFile->readAndMoveNext());
123
  $line = html_entity_decode($line);
124
  $line = sanitize_text_field($line);
125
  $debugLines[] = $line;
Framework/Filesystem/FileObject.php CHANGED
@@ -165,34 +165,81 @@ class FileObject extends SplFileObject
165
  throw new Exception("Can't seek file: " . $this->getPathname() . " to negative offset: $offset");
166
  }
167
 
168
- if ($offset === 0) {
169
- parent::seek(0);
170
  return;
171
  }
172
 
173
- if ($offset !== PHP_INT_MAX) {
174
- $offset += 1;
175
- }
176
 
177
  if ($this->totalLines !== null && $offset >= $this->totalLines) {
178
- $offset -= 1;
179
  }
180
 
181
  $originalFlags = $this->getFlags();
182
  $newFlags = $originalFlags & ~self::READ_AHEAD;
183
  $this->setFlags($newFlags);
184
 
185
- $this->rewind();
186
- for ($i = 0; $i < $offset; $i++) {
187
- $this->next();
188
- $this->fgets();
189
- if ($this->eof()) {
190
- $this->totalLines = $this->key();
191
- break;
192
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  }
194
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  $this->setFlags($originalFlags);
 
196
  }
197
 
198
  /**
165
  throw new Exception("Can't seek file: " . $this->getPathname() . " to negative offset: $offset");
166
  }
167
 
168
+ if ($offset === 0 || version_compare(PHP_VERSION, '8.0.1', '<')) {
169
+ parent::seek($offset);
170
  return;
171
  }
172
 
173
+ $offset -= 1;
 
 
174
 
175
  if ($this->totalLines !== null && $offset >= $this->totalLines) {
176
+ $offset += 1;
177
  }
178
 
179
  $originalFlags = $this->getFlags();
180
  $newFlags = $originalFlags & ~self::READ_AHEAD;
181
  $this->setFlags($newFlags);
182
 
183
+ parent::seek($offset);
184
+
185
+ if ($this->eof()) {
186
+ $this->current();
187
+ $this->totalLines = $this->key();
188
+ return;
189
+ }
190
+
191
+ $this->current();
192
+ $this->next();
193
+ $this->current();
194
+
195
+ $this->setFlags($originalFlags);
196
+ }
197
+
198
+ /**
199
+ * SplFileObject::fgets() is not consistent after SplFileObject::fseek() between php 5.x/7.x and php 8.0.1.
200
+ * We could either make fgets consistent after SplFileObject::seek() or SplFileObject::fseek()
201
+ * This implementation makes it consistent after SplFileObject::seek across all PHP versions up to 8.0.1.
202
+ * Use readAndMoveNext() instead if you want to achieve consistent behavior of SplFileObject::fgets after SplFileObject::fseek.
203
+ *
204
+ * @return string
205
+ */
206
+ #[\ReturnTypeWillChange]
207
+ public function fgets()
208
+ {
209
+ if ($this->key() === 0 || version_compare(PHP_VERSION, '8.0.1', '<')) {
210
+ return parent::fgets();
211
  }
212
 
213
+ $originalFlags = $this->getFlags();
214
+ $newFlags = $originalFlags & ~self::READ_AHEAD;
215
+ $this->setFlags($newFlags);
216
+
217
+ $this->current();
218
+ $this->next();
219
+ $line = $this->current();
220
+
221
+ $this->setFlags($originalFlags);
222
+ return $line;
223
+ }
224
+
225
+ /**
226
+ * SplFileObject::fgets() is not consistent after SplFileObject::fseek() between php 5.x/7.x and php 8.0.1.
227
+ * Use this method instead if you want to achieve consistent behavior of SplFileObject::fgets after SplFileObject::fseek across all PHP versions up to PHP 8.0.1.
228
+ * READ_AHEAD flag will not have any affect on this method. It's disabled.
229
+ *
230
+ * @return string
231
+ */
232
+ public function readAndMoveNext()
233
+ {
234
+ $originalFlags = $this->getFlags();
235
+ $newFlags = $originalFlags & ~self::READ_AHEAD;
236
+ $this->setFlags($newFlags);
237
+
238
+ $line = $this->current();
239
+ $this->next();
240
+
241
  $this->setFlags($originalFlags);
242
+ return $line;
243
  }
244
 
245
  /**
Framework/Queue/FileSeekableQueue.php CHANGED
@@ -104,7 +104,7 @@ class FileSeekableQueue implements SeekableQueueInterface, \SeekableIterator
104
  {
105
  while ($this->handle->valid()) {
106
  $this->offsetBefore = $this->handle->ftell();
107
- yield $this->handle->fgets();
108
  }
109
  }
110
 
104
  {
105
  while ($this->handle->valid()) {
106
  $this->offsetBefore = $this->handle->ftell();
107
+ yield $this->handle->readAndMoveNext();
108
  }
109
  }
110
 
Framework/SiteInfo.php CHANGED
@@ -153,38 +153,6 @@ class SiteInfo
153
  return (!file_exists($cloneableFile) && $this->cloneOptions->delete(self::IS_CLONEABLE_KEY));
154
  }
155
 
156
- /**
157
- * Ports wp_timezone_string function for compatibility with WordPress < 5.3
158
- *
159
- * @see wp_timezone_string()
160
- *
161
- * @return mixed|string|void
162
- */
163
- public function getSiteTimezone()
164
- {
165
- // Early bail: Let's use WordPress core function, as it is available.
166
- if (function_exists('wp_timezone_string')) {
167
- return wp_timezone_string();
168
- }
169
-
170
- $timezone_string = get_option('timezone_string');
171
-
172
- if ($timezone_string) {
173
- return $timezone_string;
174
- }
175
-
176
- $offset = (float)get_option('gmt_offset');
177
- $hours = (int)$offset;
178
- $minutes = ($offset - $hours);
179
-
180
- $sign = ($offset < 0) ? '-' : '+';
181
- $abs_hour = abs($hours);
182
- $abs_mins = abs($minutes * 60);
183
- $tz_offset = sprintf('%s%02d:%02d', $sign, $abs_hour, $abs_mins);
184
-
185
- return $tz_offset;
186
- }
187
-
188
  /**
189
  * @return bool True if "short_open_tags" is enabled, false if disabled.
190
  */
153
  return (!file_exists($cloneableFile) && $this->cloneOptions->delete(self::IS_CLONEABLE_KEY));
154
  }
155
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  /**
157
  * @return bool True if "short_open_tags" is enabled, false if disabled.
158
  */
Framework/Traits/ResourceTrait.php CHANGED
@@ -13,12 +13,11 @@ trait ResourceTrait
13
  protected $executionTimeLimit;
14
  protected $memoryLimit;
15
  protected $scriptMemoryLimit;
16
- protected $isBackupRestoreJob = false;
17
 
18
  public static $defaultMaxExecutionTimeInSeconds = 30;
19
  public static $executionTimeGapInSeconds = 5;
20
- // Using CPU LOAD LOW to avoid 504 errors in large database
21
- public static $backupRestoreCpuLoadTimeInSeconds = 10;
22
 
23
  /** @var bool Whether this request is taking place in the context of a unit test. */
24
  protected $isUnitTest;
@@ -47,6 +46,17 @@ trait ResourceTrait
47
  return $isMemoryLimit || $isTimeLimit;
48
  }
49
 
 
 
 
 
 
 
 
 
 
 
 
50
  /**
51
  * @see \Codeception\Module\WPLoader::_getConstants
52
  *
@@ -92,7 +102,16 @@ trait ResourceTrait
92
 
93
  return $this->getRunningTime() > $timeLimit;
94
  }
95
- // TODO Recursion for xDebug? Recursion is bad idea will cause more resource usage, need to avoid it.
 
 
 
 
 
 
 
 
 
96
 
97
  /**
98
  * Returns the maximum allowed execution time limit.
@@ -116,14 +135,19 @@ trait ResourceTrait
116
  $cpuBoundMaxExecutionTime = static::$defaultMaxExecutionTimeInSeconds;
117
  }
118
 
 
119
  if ($phpMaxExecutionTime > 0) {
120
- // Never go over PHP own execution time limit, if set.
121
  $cpuBoundMaxExecutionTime = min($phpMaxExecutionTime, $cpuBoundMaxExecutionTime);
122
  }
123
 
124
  // Set a max of 30 seconds to avoid NGINX 504 timeouts that are beyond PHP's control, with a minimum of 10 seconds
125
  $this->executionTimeLimit = max(min($cpuBoundMaxExecutionTime - static::$executionTimeGapInSeconds, 30), 10);
126
 
 
 
 
 
 
127
  if ((bool)apply_filters('wpstg.resources.ignoreTimeLimit', false)) {
128
  $this->executionTimeLimit = PHP_INT_MAX;
129
  }
@@ -180,9 +204,9 @@ trait ResourceTrait
180
  }
181
 
182
  /**
183
- * Returns the current PHP memory limit in bytes..
184
  *
185
- * @return int The current memory limit in bytes.
186
  */
187
  protected function getMaxMemoryLimit()
188
  {
@@ -191,25 +215,28 @@ trait ResourceTrait
191
  return $this->memoryLimit;
192
  }
193
 
194
- $limit = wp_convert_hr_to_bytes(ini_get('memory_limit'));
195
 
196
  // No memory limit
197
- if ($limit == -1) {
198
- // 512MB
199
- $limit = 512 * 1000000;
200
- } else {
201
- // Unexpected memory limit
202
- if (!is_int($limit) || $limit < 64000000) {
203
- // 64MB
204
- $limit = 64000000;
205
- }
206
  }
207
 
208
- if ((bool)apply_filters('wpstg.resources.ignoreMemoryLimit', false)) {
209
- $limit = PHP_INT_MAX;
 
 
 
 
210
  }
211
 
212
- $this->memoryLimit = $limit;
 
 
 
 
 
 
213
 
214
  return $this->memoryLimit;
215
  }
@@ -243,10 +270,6 @@ trait ResourceTrait
243
  */
244
  protected function getCpuBoundMaxExecutionTime($cpuLoadSetting = null)
245
  {
246
- if ($this->isBackupRestoreJob) {
247
- return self::$backupRestoreCpuLoadTimeInSeconds;
248
- }
249
-
250
  // Early bail: Cache
251
  if (!isset($this->resourceTraitSettings)) {
252
  $this->resourceTraitSettings = json_decode(json_encode(get_option('wpstg_settings', [])));
13
  protected $executionTimeLimit;
14
  protected $memoryLimit;
15
  protected $scriptMemoryLimit;
 
16
 
17
  public static $defaultMaxExecutionTimeInSeconds = 30;
18
  public static $executionTimeGapInSeconds = 5;
19
+ // Set lower maximum execution time for backup restore to avoid 504 errors in large database
20
+ public static $backupRestoreMaxExecutionTimeInSeconds = 10;
21
 
22
  /** @var bool Whether this request is taking place in the context of a unit test. */
23
  protected $isUnitTest;
46
  return $isMemoryLimit || $isTimeLimit;
47
  }
48
 
49
+ /**
50
+ * @return bool
51
+ */
52
+ public function isDatabaseImportThreshold()
53
+ {
54
+ if ($this->isMemoryLimit() || $this->isDatabaseRestoreTimeLimit()) {
55
+ return true;
56
+ }
57
+ return false;
58
+ }
59
+
60
  /**
61
  * @see \Codeception\Module\WPLoader::_getConstants
62
  *
102
 
103
  return $this->getRunningTime() > $timeLimit;
104
  }
105
+
106
+ /**
107
+ * @return bool
108
+ */
109
+ public function isDatabaseRestoreTimeLimit()
110
+ {
111
+ $timeLimit = apply_filters('wpstg.resourceTrait.backupRestoreMaxExecutionTimeInSeconds', self::$backupRestoreMaxExecutionTimeInSeconds);
112
+ return $this->getRunningTime() > $timeLimit;
113
+ }
114
+
115
 
116
  /**
117
  * Returns the maximum allowed execution time limit.
135
  $cpuBoundMaxExecutionTime = static::$defaultMaxExecutionTimeInSeconds;
136
  }
137
 
138
+ // Never go over PHP own execution time limit, if set.
139
  if ($phpMaxExecutionTime > 0) {
 
140
  $cpuBoundMaxExecutionTime = min($phpMaxExecutionTime, $cpuBoundMaxExecutionTime);
141
  }
142
 
143
  // Set a max of 30 seconds to avoid NGINX 504 timeouts that are beyond PHP's control, with a minimum of 10 seconds
144
  $this->executionTimeLimit = max(min($cpuBoundMaxExecutionTime - static::$executionTimeGapInSeconds, 30), 10);
145
 
146
+ // Allow overwriting of the max execution time limit.
147
+ // Important: Use a value lower than the actual PHP limit. (reduce it by 10seconds or more). Also adjust the nginx/php timeout limit
148
+ $this->executionTimeLimit = (int)apply_filters('wpstg.resources.executionTimeLimit', $this->executionTimeLimit);
149
+
150
+ // Allow disabling of the execution time limit
151
  if ((bool)apply_filters('wpstg.resources.ignoreTimeLimit', false)) {
152
  $this->executionTimeLimit = PHP_INT_MAX;
153
  }
204
  }
205
 
206
  /**
207
+ * Returns the current PHP memory limit in bytes.
208
  *
209
+ * @return int
210
  */
211
  protected function getMaxMemoryLimit()
212
  {
215
  return $this->memoryLimit;
216
  }
217
 
218
+ $memoryLimit = wp_convert_hr_to_bytes(ini_get('memory_limit'));
219
 
220
  // No memory limit
221
+ if ($memoryLimit == -1) {
222
+ $memoryLimit = 256 * MB_IN_BYTES;
 
 
 
 
 
 
 
223
  }
224
 
225
+ // Allow custom overwriting
226
+ $this->memoryLimit = apply_filters('wpstg.resources.memoryLimit', $memoryLimit);
227
+
228
+ // Unexpected memory limit after filter and also make sure it is never below 64MB
229
+ if (!is_int($this->memoryLimit) || $this->memoryLimit < (64 * MB_IN_BYTES)) {
230
+ $this->memoryLimit = 64 * MB_IN_BYTES;
231
  }
232
 
233
+ // Make sure it never exceeds 256MB
234
+ $this->memoryLimit = (min($this->memoryLimit, 256 * MB_IN_BYTES));
235
+
236
+ // Allow disabling the memory limit
237
+ if ((bool)apply_filters('wpstg.resources.ignoreMemoryLimit', false)) {
238
+ $this->memoryLimit = PHP_INT_MAX;
239
+ }
240
 
241
  return $this->memoryLimit;
242
  }
270
  */
271
  protected function getCpuBoundMaxExecutionTime($cpuLoadSetting = null)
272
  {
 
 
 
 
273
  // Early bail: Cache
274
  if (!isset($this->resourceTraitSettings)) {
275
  $this->resourceTraitSettings = json_decode(json_encode(get_option('wpstg_settings', [])));
Framework/Utils/Cache/BufferedCache.php CHANGED
@@ -332,7 +332,7 @@ class BufferedCache extends AbstractCache
332
  $file->fseek(max($file->getSize() - $negativeOffset, 0), SEEK_SET);
333
 
334
  do {
335
- $lastLine = $file->fgets();
336
  } while (!$file->eof());
337
 
338
  return $lastLine;
332
  $file->fseek(max($file->getSize() - $negativeOffset, 0), SEEK_SET);
333
 
334
  do {
335
+ $lastLine = $file->readAndMoveNext();
336
  } while (!$file->eof());
337
 
338
  return $lastLine;
Framework/Utils/Times.php CHANGED
@@ -11,6 +11,7 @@ namespace WPStaging\Framework\Utils;
11
  use DateInterval;
12
  use DateTime;
13
  use DateTimeImmutable;
 
14
 
15
  /**
16
  * Class Times
@@ -19,13 +20,53 @@ use DateTimeImmutable;
19
  */
20
  class Times
21
  {
22
- public function findNextHour()
 
 
 
 
 
 
 
 
 
 
 
 
23
  {
24
- $dateTime = new \DateTime('now', wp_timezone());
25
- $dateTime->add(new \DateInterval('PT1H'))
26
- ->setTime($dateTime->format('H'), '00');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- return $dateTime;
 
 
 
 
 
 
 
 
 
29
  }
30
 
31
  /**
@@ -34,11 +75,11 @@ class Times
34
  * This method is similar, in concept, to the PHP Core `range` method
35
  * where the entity is changed from numeric values to Dates.
36
  *
37
- * @param DateTime|DateTimeImmutable|string $start Either a Date object or a valid date definition to start
38
  * the range from.
39
- * @param DateTime|DateTimeImmutable|string $end Either a Date object or a valid date definition to end
40
  * the range at, inclusively.
41
- * @param DateInterval|string $step The step definition, as either an Interval object, or as
42
  * a valid DateInterval definition.
43
  *
44
  * @return array<DateTimeImmutable> A list of generated Dates between the start and end.
@@ -53,14 +94,14 @@ class Times
53
  } else {
54
  $startDateObject = $start instanceof DateTime ?
55
  DateTimeImmutable::createFromMutable($start)
56
- : new DateTimeImmutable($start, wp_timezone());
57
  }
58
  if ($end instanceof DateTimeImmutable) {
59
  $endDateObject = $end;
60
  } else {
61
  $endDateObject = $end instanceof DateTime ?
62
  DateTimeImmutable::createFromMutable($end)
63
- : new DateTimeImmutable($end, wp_timezone());
64
  }
65
  $stepInterval = $step instanceof DateInterval ?
66
  $step
11
  use DateInterval;
12
  use DateTime;
13
  use DateTimeImmutable;
14
+ use DateTimeZone;
15
 
16
  /**
17
  * Class Times
20
  */
21
  class Times
22
  {
23
+
24
+ /**
25
+ * Ports wp core wp_timezone_string() function for compatibility with WordPress < 5.3
26
+ * Retrieves the timezone from site settings as a string.
27
+ *
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
  {
37
+ // Early bail: Let's use WordPress core function if it is available.
38
+ if (function_exists('wp_timezone_string')) {
39
+ return wp_timezone_string();
40
+ }
41
+
42
+ $timezone_string = get_option('timezone_string');
43
+
44
+ if ($timezone_string) {
45
+ return $timezone_string;
46
+ }
47
+
48
+ $offset = (float)get_option('gmt_offset');
49
+ $hours = (int)$offset;
50
+ $minutes = ($offset - $hours);
51
+
52
+ $sign = ($offset < 0) ? '-' : '+';
53
+ $abs_hour = abs($hours);
54
+ $abs_mins = abs($minutes * 60);
55
+ $tz_offset = sprintf('%s%02d:%02d', $sign, $abs_hour, $abs_mins);
56
+
57
+ return $tz_offset;
58
+ }
59
 
60
+ /**
61
+ * Retrieves the timezone from site settings as a `DateTimeZone` object.
62
+ * Timezone can be based on a PHP timezone string or a ±HH:MM offset.
63
+ * This is copied from wordpress core wp_timezone() which exists since WordPress 5.3.0 for backward compatibility
64
+ *
65
+ * @return DateTimeZone Timezone object.
66
+ */
67
+ public function getSiteTimezoneObject()
68
+ {
69
+ return new DateTimeZone($this->getSiteTimezoneString());
70
  }
71
 
72
  /**
75
  * This method is similar, in concept, to the PHP Core `range` method
76
  * where the entity is changed from numeric values to Dates.
77
  *
78
+ * @param DateTime|DateTimeImmutable|string $start Either a Date object or a valid date definition to start
79
  * the range from.
80
+ * @param DateTime|DateTimeImmutable|string $end Either a Date object or a valid date definition to end
81
  * the range at, inclusively.
82
+ * @param DateInterval|string $step The step definition, as either an Interval object, or as
83
  * a valid DateInterval definition.
84
  *
85
  * @return array<DateTimeImmutable> A list of generated Dates between the start and end.
94
  } else {
95
  $startDateObject = $start instanceof DateTime ?
96
  DateTimeImmutable::createFromMutable($start)
97
+ : new DateTimeImmutable($start, $this->getSiteTimezoneObject());
98
  }
99
  if ($end instanceof DateTimeImmutable) {
100
  $endDateObject = $end;
101
  } else {
102
  $endDateObject = $end instanceof DateTime ?
103
  DateTimeImmutable::createFromMutable($end)
104
+ : new DateTimeImmutable($end, $this->getSiteTimezoneObject());
105
  }
106
  $stepInterval = $step instanceof DateInterval ?
107
  $step
constantsFree.php CHANGED
@@ -2,10 +2,10 @@
2
 
3
  // WP STAGING version number
4
  if (!defined('WPSTG_VERSION')) {
5
- define('WPSTG_VERSION', '2.9.2');
6
  }
7
 
8
  // Compatible up to WordPress Version
9
  if (!defined('WPSTG_COMPATIBLE')) {
10
- define('WPSTG_COMPATIBLE', '5.8.2');
11
  }
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
9
  if (!defined('WPSTG_COMPATIBLE')) {
10
+ define('WPSTG_COMPATIBLE', '5.8.3');
11
  }
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.2';
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.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);
readme.txt CHANGED
@@ -6,13 +6,13 @@ Contributors: ReneHermi, WP-Staging
6
  Donate link: https://wp-staging.com/#pricing
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
- Tags: backup, backups, staging, duplication, clone
10
  Requires at least: 3.6+
11
  Tested up to: 5.8
12
- Stable tag: 2.9.2
13
  Requires PHP: 5.6
14
 
15
- A backup & duplicator plugin - clone, move, duplicate & migrate websites to staging, backup, and development sites for authorized users only.
16
 
17
  == Description ==
18
 
@@ -140,13 +140,13 @@ Duplicator is best placed to be a tool for the first-time creation of your produ
140
  If you have created a local or web-hosted development site and you need to migrate this site the first time to your production domain then you are doing nothing wrong with using
141
  the Duplicator plugin! If you need all your latest production data like posts, updated plugins, theme data, and styles in a testing environment or want to create a quick backup before testing out something then we recommend using WP STAGING instead!
142
 
143
- If speed, performance, and code quality are a matter for you as well, give WP STAGING | PRO a try.
144
 
145
  = I can not log in to the staging / backup site =
146
  If you are using a security plugin like Wordfence, iThemes Security, All In One WP Security & Firewall, or a plugin that hides the WordPress default login URL make sure that you have installed the latest version of WP STAGING to access your cloned backup site.
147
  If you can still not log in to your staging / backup site, you can go to WP STAGING > settings and disable there the WP STAGING extra authentication. Your admin dashboard will still be protected and not accessible to public users.
148
 
149
- = Can I activate permalinks on the staging site? =
150
 
151
  Permalinks are disabled on the staging / backup site after first time cloning / backup creation
152
  [Read here](https://wp-staging.com/docs/activate-permalinks-staging-site/ "activate permalinks on staging site") how to activate permalinks on the staging site.
@@ -156,7 +156,7 @@ The pro version of WP STAGING can backup your whole WordPress website. (In the f
156
  With this backup function, you can backup and copy your entire WordPress website to another domain, new host, or new server very easily, and often faster and more reliable than with any other existing backup plugins.
157
  Have a look at [https://wp-staging.com/docs/how-to-migrate-your-wordpress-site-to-a-new-host/](this article) which gives a good introduction into the backup feature.
158
 
159
- = Can I give you some feedback? =
160
  This plugin has been created in thousands of hours and works even with the smallest shared web hosting package.
161
  We also use enterprise-level approved testing coding environment to make sure that the cloning and backup process run rock-solid on your system.
162
  If you are a developer you will probably like to hear that we use Codeception and PHPUnit for our backup software.
@@ -168,8 +168,8 @@ please open a [support request](https://wp-staging.com/support/ "support request
168
  https://wp-staging.com
169
 
170
  == Installation ==
171
- 2. Upload and install it via the WordPress plugin backend wp-admin > Plugins > Add New > uploads
172
- 3. Activate the plugin through the 'Plugins' menu in WordPress.
173
 
174
  == Screenshots ==
175
 
@@ -185,6 +185,19 @@ https://wp-staging.com
185
 
186
  == Changelog ==
187
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  = 2.9.2 =
189
  * Hotfix: Fix CLONE PUSH BACKUP on Medium and High CPU Load on WP STAGING 2.9.1. Improve Performance of database backup #1492
190
 
@@ -389,57 +402,22 @@ https://wp-staging.com
389
  * Dev: Refactor Job(s) implementation to use the Resources Trait #765
390
  * Dev: Add internal documentation to versioning and hotfixes #780
391
 
392
- = 2.8.2 =
393
- * Feat: Compatible up to WP 5.7
394
- * Feat: Check database connection in clone data edit #650
395
- * Feat: Exclude .wp-staging-cloneable file from cloning and update #718
396
- * Feat: Show notice if a user is using an outdated version of WP Staging Hooks plugin #716
397
- * Feat: Add single disabled items notice with better message #717
398
- * Feat: Add options to enable/disable staging sites cloning from UI #722
399
- * Enh: Use included directories instead of excluded directories to increase cloning speed #671
400
- * Enh: Stringify directories array var in $_POST to reduce $_POST size during cloning #671
401
- * Enh: Replace relative paths exclude to absolute and wildcard paths exclude during cloning #671
402
- * Enh: Detect snapshot tables using regex #694
403
- * Enh: Enable disable save button in clone data edit during database connection #700
404
- * Enh: Improve exclude filters for Push process #720
405
- * Enh: Move Backend/public/img to assets/img #719
406
- * Enh: Unify Single and Multisite Classes #713
407
- * Enh: Keep other staging behavior when the staging site is cloneable #722
408
- * Enh: Refactor search and replace jobs to use memory and time-consumption aware trait #702
409
- * Fix: Lost password link generation in staging sites #697
410
- * Fix: Fix cloning on multisite for PHP 5 #725
411
- * Fix: Skip symlink scanning during directory scan #736
412
- * Fix: Replace deprecated jQuery click method #730
413
- * Fix: Fix overlapping of sweetalert confirmation on push with sidebar #742
414
- * Fix: Exclude wp staging content folder during staging #741
415
- * Fix: Add sanitizing for path to fix comparing for Windows paths #751
416
- * Fix: Uninstall not possible if "delete all settings is activated" #756
417
- * Dev: Internal refactoring of database backup to avoid long-lived branch #624
418
- * Dev: Enforced changelog entries in CI #695
419
- * Dev: Refactored webdriver tests to make them faster #656
420
- * Dev: Refactor how the automated test workflows are generated. Add new webdriver test before release with default settings #712
421
- * Dev: Increased default file batch limits for faster development environment and CI #706
422
- * Dev: Renamed all code and UI references of Snapshot to Backup #715
423
- * Dev: Add helper to manage clone settings #717
424
- * Dev: Internal code refactoring, renaming classes for better readability #721
425
- * Dev: Add infrastructure support for wp-cli and background processing #728
426
- * Dev: Update php-scoper and other development dependencies #744
427
- * Dev: Build javascript when building the distributable version of the plugin #750
428
-
429
- = 2.8.1 =
430
- * Feat: Show creator user name of staging site
431
- * Enh: Show notice if sending mails are disabled
432
- * Enh: Show message and stop execution if php version is lower than 5.5
433
- * Enh: Abort cloning process if table already exists in external database
434
- * Fix: Can not update database credentials in staging sites wp-config.php under rare circumstances
435
- * Fix: During the update process if options table was not selected it didn't get skipped
436
- * Fix: Error if WP is lower than 4.6
437
- * Fix: Can not delete entire staging site on error
438
- * Fix: Activating pro version does not properly disable free version
439
-
440
- Full changelog: [https://wp-staging.com/wp-staging-changelog](https://wp-staging.com/wp-staging-changelog)
441
 
442
  == Upgrade Notice ==
 
 
 
 
 
 
 
 
 
 
 
 
443
  * New: If cpu load setting is low make use of the file copy limit for pushing process to increase copy speed #1485
444
  * Enh: Add warning notice if WP_CRON_DISABLED is set to true as BG Processing depends upon it #1467
445
  * Fix: Add own implementation of get_current_network_id() for backward compatibility #1438
6
  Donate link: https://wp-staging.com/#pricing
7
  License: GPLv2 or later
8
  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.
16
 
17
  == Description ==
18
 
140
  If you have created a local or web-hosted development site and you need to migrate this site the first time to your production domain then you are doing nothing wrong with using
141
  the Duplicator plugin! If you need all your latest production data like posts, updated plugins, theme data, and styles in a testing environment or want to create a quick backup before testing out something then we recommend using WP STAGING instead!
142
 
143
+ If code quality, speed, backup & cloning performance are important to you, give WP STAGING a try.
144
 
145
  = I can not log in to the staging / backup site =
146
  If you are using a security plugin like Wordfence, iThemes Security, All In One WP Security & Firewall, or a plugin that hides the WordPress default login URL make sure that you have installed the latest version of WP STAGING to access your cloned backup site.
147
  If you can still not log in to your staging / backup site, you can go to WP STAGING > settings and disable there the WP STAGING extra authentication. Your admin dashboard will still be protected and not accessible to public users.
148
 
149
+ = Can I activate permalinks on the staging / backup site? =
150
 
151
  Permalinks are disabled on the staging / backup site after first time cloning / backup creation
152
  [Read here](https://wp-staging.com/docs/activate-permalinks-staging-site/ "activate permalinks on staging site") how to activate permalinks on the staging site.
156
  With this backup function, you can backup and copy your entire WordPress website to another domain, new host, or new server very easily, and often faster and more reliable than with any other existing backup plugins.
157
  Have a look at [https://wp-staging.com/docs/how-to-migrate-your-wordpress-site-to-a-new-host/](this article) which gives a good introduction into the backup feature.
158
 
159
+ = Can I give you some feedback for WP STAGING Backup & Cloning? =
160
  This plugin has been created in thousands of hours and works even with the smallest shared web hosting package.
161
  We also use enterprise-level approved testing coding environment to make sure that the cloning and backup process run rock-solid on your system.
162
  If you are a developer you will probably like to hear that we use Codeception and PHPUnit for our backup software.
168
  https://wp-staging.com
169
 
170
  == Installation ==
171
+ 2. Upload and install the backup & clone plugin WP STAGING on the WordPress plugin page wp-admin > Plugins > Add New > uploads
172
+ 3. Activate the WP STAGING backup plugin through the 'Plugins' menu in WordPress.
173
 
174
  == Screenshots ==
175
 
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
191
+ * New: Add filter for overwriting max execution for database backup restore #1512
192
+ * New: Add filter to allow overwriting of the maximum allowed request time to make sure backup restore works for huge files. (19.000.000M database rows) #1510
193
+ * Tweak: Show custom uploads folder in tooltip description and explain better that changing a symlink image will also change the image on the production site. #1495
194
+ * Fix: If cloning a multisite subsite into external database, it does not clone / backup wp_users and wp_usermeta #1515
195
+ * Fix: Skip tmp single file plugin during backup PUSH copy process #1491
196
+ * Fix: Preserve table selection during PUSH and UPDATE even when all backup tables unselected #1488
197
+ * Fix: Make sure maximum memory consumption during cloning or backup is never higher than 256MB #1502
198
+ * Fix: Use custom implementation of wp_timezone() for backward compatibility to WordPress older than 5.3 #1505
199
+ * Fix: Override FileObject::fgets to make them behave exactly from SplFileObject of PHP < 8.0.1 #1506
200
+
201
  = 2.9.2 =
202
  * Hotfix: Fix CLONE PUSH BACKUP on Medium and High CPU Load on WP STAGING 2.9.1. Improve Performance of database backup #1492
203
 
402
  * Dev: Refactor Job(s) implementation to use the Resources Trait #765
403
  * Dev: Add internal documentation to versioning and hotfixes #780
404
 
405
+ WP STAGING Backup & Cloning | Full changelog:
406
+ [https://wp-staging.com/wp-staging-changelog](https://wp-staging.com/wp-staging-changelog)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
 
408
  == Upgrade Notice ==
409
+ * New: Add support for WordPress 5.8.3
410
+ * New: Add filter for excluding files during cloning / backup #1494
411
+ * New: Add filter for overwriting max execution for database backup restore #1512
412
+ * New: Add filter to allow overwriting of the maximum allowed request time to make sure backup restore works for huge files. (19.000.000M database rows) #1510
413
+ * Fix: If cloning a multisite subsite into external database, it does not clone / backup wp_users and wp_usermeta #1515
414
+ * Fix: Skip tmp single file plugin during backup PUSH copy process #1491
415
+ * Fix: Preserve table selection during PUSH and UPDATE even when all backup tables unselected #1488
416
+ * Fix: Make sure maximum memory consumption during cloning or backup is never higher than 256MB #1502
417
+ * Fix: Use custom implementation of wp_timezone() for backward compatibility to WordPress older than 5.3 #1505
418
+ * Fix: Override FileObject::fgets to make them behave exactly from SplFileObject of PHP < 8.0.1 #1506
419
+ * Tweak: Show custom uploads folder in tooltip description and explain better that changing a symlink image will also change the image on the production site. #1495
420
+
421
  * New: If cpu load setting is low make use of the file copy limit for pushing process to increase copy speed #1485
422
  * Enh: Add warning notice if WP_CRON_DISABLED is set to true as BG Processing depends upon it #1467
423
  * Fix: Add own implementation of get_current_network_id() for backward compatibility #1438
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.2
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.3
11
  * Text Domain: wp-staging
12
  * Domain Path: /languages/
13
  *