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 | 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 +4 -2
- Backend/Modules/Jobs/Cloning.php +2 -2
- Backend/Modules/Jobs/Database.php +17 -12
- Backend/Modules/Jobs/Files.php +1 -1
- Backend/Modules/SystemInfo.php +3 -3
- Backend/views/backup/modal/export.php +6 -6
- Backend/views/clone/ajax/custom-directory.php +5 -4
- Framework/Adapter/Directory.php +3 -0
- Framework/CloningProcess/Database/DatabaseCloningService.php +34 -9
- Framework/Filesystem/DebugLogReader.php +1 -1
- Framework/Filesystem/FileObject.php +61 -14
- Framework/Queue/FileSeekableQueue.php +1 -1
- Framework/SiteInfo.php +0 -32
- Framework/Traits/ResourceTrait.php +47 -24
- Framework/Utils/Cache/BufferedCache.php +1 -1
- Framework/Utils/Times.php +51 -10
- constantsFree.php +2 -2
- opcacheBootstrap.php +1 -1
- readme.txt +35 -57
- wp-staging.php +1 -1
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("
|
|
|
|
|
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',
|
99 |
<br>
|
100 |
-
<?php _e(sprintf('Site Timezone: %s',
|
101 |
</span>
|
102 |
</div>
|
103 |
</label>
|
104 |
<select name="backupScheduleTime" id="backupScheduleTime">
|
105 |
-
<?php foreach ($recurrenceTimes as $
|
106 |
-
<option value="<?php echo esc_attr($
|
107 |
-
<?php echo esc_html($
|
108 |
</option>
|
109 |
<?php endforeach; ?>
|
110 |
</select>
|
111 |
-
<span id="backup-schedule-current-time"><?php _e(sprintf('Current Time: %s', (new DateTime('now',
|
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:
|
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
|
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
|
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
|
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
|
301 |
-
|
302 |
|
303 |
// Build full qualified statement for table [prefix_]users from main site e.g. wp_users
|
304 |
-
if ($this->
|
305 |
-
$statement = str_replace($tableName, $productionDb->
|
306 |
}
|
307 |
// Build full qualified statement for table [prefix_]usermeta from main site e.g. wp_usermeta
|
308 |
-
if ($this->
|
309 |
-
$statement = str_replace($tableName, $productionDb->
|
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->
|
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(
|
170 |
return;
|
171 |
}
|
172 |
|
173 |
-
|
174 |
-
$offset += 1;
|
175 |
-
}
|
176 |
|
177 |
if ($this->totalLines !== null && $offset >= $this->totalLines) {
|
178 |
-
$offset
|
179 |
}
|
180 |
|
181 |
$originalFlags = $this->getFlags();
|
182 |
$newFlags = $originalFlags & ~self::READ_AHEAD;
|
183 |
$this->setFlags($newFlags);
|
184 |
|
185 |
-
$
|
186 |
-
|
187 |
-
|
188 |
-
$this->
|
189 |
-
|
190 |
-
|
191 |
-
|
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->
|
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 |
-
//
|
21 |
-
public static $
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
186 |
*/
|
187 |
protected function getMaxMemoryLimit()
|
188 |
{
|
@@ -191,25 +215,28 @@ trait ResourceTrait
|
|
191 |
return $this->memoryLimit;
|
192 |
}
|
193 |
|
194 |
-
$
|
195 |
|
196 |
// No memory limit
|
197 |
-
if ($
|
198 |
-
|
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 |
-
|
209 |
-
|
|
|
|
|
|
|
|
|
210 |
}
|
211 |
|
212 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
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->
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
{
|
24 |
-
|
25 |
-
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
38 |
* the range from.
|
39 |
-
* @param DateTime|DateTimeImmutable|string $end
|
40 |
* the range at, inclusively.
|
41 |
-
* @param DateInterval|string
|
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,
|
57 |
}
|
58 |
if ($end instanceof DateTimeImmutable) {
|
59 |
$endDateObject = $end;
|
60 |
} else {
|
61 |
$endDateObject = $end instanceof DateTime ?
|
62 |
DateTimeImmutable::createFromMutable($end)
|
63 |
-
: new DateTimeImmutable($end,
|
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.
|
6 |
}
|
7 |
|
8 |
// Compatible up to WordPress Version
|
9 |
if (!defined('WPSTG_COMPATIBLE')) {
|
10 |
-
define('WPSTG_COMPATIBLE', '5.8.
|
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.
|
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,
|
10 |
Requires at least: 3.6+
|
11 |
Tested up to: 5.8
|
12 |
-
Stable tag: 2.9.
|
13 |
Requires PHP: 5.6
|
14 |
|
15 |
-
|
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
|
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
|
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 |
-
|
393 |
-
|
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.
|
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 |
*
|