PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.9.2
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.9.2
4.9.2 4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Backend / Modules / Jobs / PreserveDataSecondStep.php
wp-staging / Backend / Modules / Jobs Last commit date
Cleaners 3 months ago Exceptions 5 years ago Cancel.php 7 months ago CancelUpdate.php 7 months ago Cloning.php 2 weeks ago CloningProcess.php 1 year ago Data.php 7 months ago Database.php 3 months ago Delete.php 4 months ago Directories.php 4 months ago Files.php 1 week ago Finish.php 2 months ago Job.php 2 weeks ago JobExecutable.php 7 months ago Logs.php 3 years ago PreserveDataFirstStep.php 1 month ago PreserveDataSecondStep.php 1 month ago ProcessLock.php 1 year ago Scan.php 4 months ago SearchReplace.php 5 months ago TotalStepsAreNumberOfTables.php 5 years ago Updating.php 2 weeks ago
PreserveDataSecondStep.php
268 lines
1 <?php
2
3 namespace WPStaging\Backend\Modules\Jobs;
4
5 use WPStaging\Backup\Storage\Providers;
6 use WPStaging\Core\WPStaging;
7 use WPStaging\Framework\Adapter\SourceDatabase;
8 use WPStaging\Staging\CloneOptions;
9 use WPStaging\Staging\Sites;
10 use WPStaging\Staging\FirstRun;
11 use WPStaging\Backend\Modules\Jobs\Job as MainJob;
12
13 use function WPStaging\functions\debug_log;
14
15 /**
16 * Copy wpstg_tmp_data back to wpstg_staging_sites after cloning with class::PreserveDataSecondStep
17 * @package WPStaging\Backend\Modules\Jobs
18 */
19 class PreserveDataSecondStep extends JobExecutable
20 {
21 /** @var \wpdb */
22 private $stagingDb;
23
24 /** @var \wpdb */
25 private $productionDb;
26
27 /** @var string */
28 private $stagingPrefix;
29
30 /** @var object */
31 private $preservedData;
32
33 protected function calculateTotalSteps()
34 {
35 $this->options->totalSteps = 1;
36 }
37
38 /** @return object */
39 public function start()
40 {
41 $this->run();
42 $this->saveOptions();
43
44 return (object)$this->response;
45 }
46
47 /** @return false */
48 protected function execute()
49 {
50 $db = new SourceDatabase($this->options);
51
52 $this->stagingDb = $db->getDatabase();
53 $this->productionDb = WPStaging::getInstance()->get("wpdb");
54 $this->stagingPrefix = $this->options->prefix;
55
56 if ($db->isExternalDatabase()) {
57 $this->stagingPrefix = $this->options->databasePrefix;
58 }
59
60 $this->copyToStaging();
61 $this->prepareResponse(true, true);
62
63 return false;
64 }
65
66 /** @return true */
67 public function copyToStaging()
68 {
69 // Early bail if table doesn't exist
70 if (!$this->tableExists($this->stagingPrefix . "options")) {
71 return true;
72 }
73
74 // Get wpstg_tmp_data from production database
75 $result = $this->productionDb->get_var(
76 $this->productionDb->prepare(
77 "SELECT `option_value` FROM " . $this->productionDb->prefix . "options WHERE `option_name` = %s",
78 "wpstg_tmp_data"
79 )
80 );
81
82 // Nothing to do
83 if (!$result) {
84 return true;
85 }
86
87 // Make sure this is compatible with Free Version
88 // @see \WPStaging\Backup\BackupScheduler::OPTION_BACKUP_SCHEDULES
89 $backupSchedulesOption = 'wpstg_backup_schedules';
90
91 // Delete wpstg_tmp_data from the production site
92 $deleteTmpData = $this->productionDb->query(
93 $this->productionDb->prepare("DELETE FROM " . $this->productionDb->prefix . "options WHERE `option_name` = %s", "wpstg_tmp_data")
94 );
95
96 if ($deleteTmpData === false) {
97 $this->log("Preserve Data Second Step: Failed to delete wpstg_tmp_data from the production site");
98 }
99
100 $this->preservedData = maybe_unserialize($result);
101
102 // Preserve wpstg_staging_sites in staging database
103 $this->preserveStagingOption(Sites::STAGING_SITES_OPTION, $this->preservedData->stagingSites, 'existing clones');
104 // Preserve wpstg_settings in staging database (preserved during update only, not during reset)
105 if ($this->options->mainJob === MainJob::UPDATE && isset($this->preservedData->settings)) {
106 $this->preserveStagingOption("wpstg_settings", $this->preservedData->settings, 'settings');
107 }
108
109 // Preserve wpstg_login_link_settings in staging database
110 $this->preserveStagingOption("wpstg_login_link_settings", $this->preservedData->loginLinkSettings, 'login settings');
111
112 // Preserve wpstg_clone_options in staging database
113 $this->updateCloneOptions();
114 $this->preserveStagingOption(CloneOptions::WPSTG_CLONE_SETTINGS_KEY, $this->preservedData->cloneOptions, 'clone options');
115
116 // Preserve backup schedules
117 $this->preserveStagingOption($backupSchedulesOption, $this->preservedData->backupSchedules, 'backup schedules');
118
119 foreach (Providers::STORAGE_LABELS as $identifier => $label) {
120 $optionName = 'wpstg_' . $identifier;
121 $value = $this->getPreservedStorageValue($identifier);
122 if ($value !== null) {
123 $this->preserveStagingOption($optionName, $value, $label . ' settings');
124 } else {
125 $this->deleteStagingSiteOption($optionName);
126 }
127 }
128
129 return true;
130 }
131
132 /**
133 * @param string $optionName
134 * @param string $optionValue
135 * @param bool $autoload
136 */
137 protected function preserveStagingOption($optionName, $optionValue, $logEntity, $autoload = false)
138 {
139 $isDeleted = $this->deleteStagingSiteOption($optionName);
140
141 if ($isDeleted === false) {
142 $this->log("Preserve Data Second Step: Failed to delete " . $optionName . " from the staging site");
143 }
144
145 $isInserted = $this->insertOptionIntoStagingSite($optionName, $optionValue, $autoload);
146
147 if ($isInserted === false) {
148 $this->log("Preserve Data Second Step: Failed to insert preserved " . $logEntity . " into " . $optionName . " of the staging site");
149 }
150 }
151
152 /**
153 * @param string $optionName
154 *
155 * @return bool|int Number of rows affected. Boolean false on error
156 */
157 protected function deleteStagingSiteOption($optionName)
158 {
159 return $this->stagingDb->query(
160 $this->stagingDb->prepare("DELETE FROM " . $this->stagingPrefix . "options WHERE `option_name` = %s", $optionName)
161 );
162 }
163
164 /**
165 * @param string $optionName
166 * @param string $optionValue
167 * @param bool $autoload
168 *
169 * @return bool|int Number of rows affected. Boolean false on error
170 */
171 protected function insertOptionIntoStagingSite($optionName, $optionValue, $autoload = false)
172 {
173 $autoload = $autoload ? 'yes' : 'no';
174
175 return $this->stagingDb->query(
176 $this->stagingDb->prepare(
177 "INSERT INTO `" . $this->stagingPrefix . "options` ( `option_id`, `option_name`, `option_value`, `autoload` ) VALUES ( NULL , %s, %s, %s )",
178 $optionName,
179 $optionValue,
180 $autoload
181 )
182 );
183 }
184
185 /**
186 * Get the preserved storage value, checking both the new hyphenated and legacy camelCase property names.
187 * This ensures backward compatibility if wpstg_tmp_data was created by an older plugin version.
188 *
189 * @param string $identifier The new hyphenated storage identifier
190 * @return string|null The preserved value, or null if not found
191 */
192 protected function getPreservedStorageValue(string $identifier)
193 {
194 if ($this->propertyExists($identifier)) {
195 return $this->preservedData->{$identifier};
196 }
197
198 $legacyProperty = isset(Providers::LEGACY_PROPERTY_MAP[$identifier]) ? Providers::LEGACY_PROPERTY_MAP[$identifier] : $identifier;
199 if ($legacyProperty !== $identifier && $this->propertyExists($legacyProperty)) {
200 return $this->preservedData->{$legacyProperty};
201 }
202
203 return null;
204 }
205
206 /**
207 * @param string $property
208 *
209 * @return bool
210 */
211 protected function propertyExists($property)
212 {
213 if (!is_object($this->preservedData)) {
214 return false;
215 }
216
217 if (!property_exists($this->preservedData, $property)) {
218 return false;
219 }
220
221 return !empty($this->preservedData->{$property});
222 }
223
224 /**
225 * Check if table exists
226 * @param string $table
227 * @return bool
228 */
229 private function tableExists($table)
230 {
231 return !($table != $this->stagingDb->get_var("SHOW TABLES LIKE '{$table}'"));
232 }
233
234 /**
235 * Update wpstg_clone_options before restoring it.
236 *
237 * @return void
238 */
239 private function updateCloneOptions()
240 {
241 if ($this->getOptions()->mainJob !== MainJob::UPDATE) {
242 return;
243 }
244
245 $cloneOptions = $this->preservedData->cloneOptions;
246
247 $data = maybe_unserialize($cloneOptions);
248 if (empty($cloneOptions)) {
249 $data = new \stdClass();
250 }
251
252 // Should not happen, but if it happens just return earlier without changing anything.
253 if (!is_object($data)) {
254 debug_log('Fail to update clone options before restore.');
255 return;
256 }
257
258 // clean up old option wpstg_woo_scheduler_disabled if exists
259 if (property_exists($data, 'wpstg_woo_scheduler_disabled')) {
260 unset($data->wpstg_woo_scheduler_disabled);
261 }
262
263 $schedulerKey = FirstRun::WOO_SCHEDULER_ENABLED_KEY;
264 $data->{$schedulerKey} = empty($this->getOptions()->isWooSchedulerEnabled) ? false : true;
265 $this->preservedData->cloneOptions = maybe_serialize($data);
266 }
267 }
268