PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 3.6.0
WP STAGING – WordPress Backup, Restore, Migration & Clone v3.6.0
4.9.3 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 / Backup / Task / FileRestoreTask.php
wp-staging / Backup / Task Last commit date
RestoreFileHandlers 2 years ago Tasks 2 years ago AbstractTask.php 2 years ago BackupTask.php 3 years ago FileBackupTask.php 2 years ago FileRestoreTask.php 2 years ago RestoreTask.php 2 years ago
FileRestoreTask.php
272 lines
1 <?php
2
3 namespace WPStaging\Backup\Task;
4
5 use WPStaging\Framework\Adapter\Directory;
6 use WPStaging\Framework\Filesystem\Filesystem;
7 use WPStaging\Framework\Filesystem\MissingFileException;
8 use WPStaging\Framework\Filesystem\PathIdentifier;
9 use WPStaging\Framework\Queue\FinishedQueueException;
10 use WPStaging\Framework\Queue\SeekableQueueInterface;
11 use WPStaging\Framework\Utils\Cache\Cache;
12 use WPStaging\Backup\Dto\StepsDto;
13 use WPStaging\Backup\Entity\BackupMetadata;
14 use WPStaging\Backup\Task\RestoreFileHandlers\RestoreFileProcessor;
15 use WPStaging\Framework\SiteInfo;
16 use WPStaging\Vendor\Psr\Log\LoggerInterface;
17
18 /**
19 * Class FileRestoreTask
20 *
21 * This is an abstract class for the filesystem-based restore actions of restoring a site,
22 * such as plugins, themes, mu-plugins and uploads files.
23 *
24 * It's main philosophy is to control the individual queue of what needs to be processed
25 * from each of the concrete restores. It delegates actual processing of the queue to a separate class.
26 *
27 * @package WPStaging\Backup\Abstracts\Task
28 */
29 abstract class FileRestoreTask extends RestoreTask
30 {
31 /**
32 * @var Filesystem
33 */
34 protected $filesystem;
35
36 /**
37 * @var Directory
38 */
39 protected $directory;
40
41 /**
42 * @var RestoreFileProcessor
43 */
44 private $restoreFileProcessor;
45
46 /**
47 * @var int
48 */
49 protected $processedNow;
50
51 /**
52 * @var PathIdentifier
53 */
54 protected $pathIdentifier;
55
56 /**
57 * @var bool
58 */
59 protected $isSiteHostedOnWordPressCom = false;
60
61 public function __construct(
62 LoggerInterface $logger,
63 Cache $cache,
64 StepsDto $stepsDto,
65 SeekableQueueInterface $taskQueue,
66 Filesystem $filesystem,
67 Directory $directory,
68 RestoreFileProcessor $restoreFileProcessor,
69 PathIdentifier $pathIdentifier,
70 SiteInfo $siteInfo
71 ) {
72 parent::__construct($logger, $cache, $stepsDto, $taskQueue);
73 $this->filesystem = $filesystem;
74 $this->directory = $directory;
75 $this->restoreFileProcessor = $restoreFileProcessor;
76 $this->pathIdentifier = $pathIdentifier;
77 $this->isSiteHostedOnWordPressCom = $siteInfo->isHostedOnWordPressCom();
78 }
79
80 public function prepareFileRestore()
81 {
82 if ($this->stepsDto->getTotal() === 0) {
83 $this->buildQueue();
84 $this->taskQueue->seek(0);
85
86 // Just an arbitrary number, when there are no more items in the Queue we call stepsDto->finish()
87 $this->stepsDto->setTotal(100);
88 }
89 }
90
91 /**
92 * @return \WPStaging\Backup\Dto\TaskResponseDto
93 */
94 public function execute()
95 {
96 try {
97 $this->checkMissingParts();
98 } catch (MissingFileException $ex) {
99 $this->stepsDto->finish();
100 $this->logger->warning(sprintf(esc_html__('%s Skipped!', 'wp-staging'), static::getTaskTitle()));
101 return $this->generateResponse(false);
102 }
103
104 $this->prepareFileRestore();
105
106 try {
107 while (!$this->isThreshold()) {
108 $this->processNextItemInQueue();
109 $this->processedNow++;
110 }
111 } catch (FinishedQueueException $e) {
112 $this->stepsDto->finish();
113 }
114
115 $this->logger->info(sprintf(esc_html__('%s (processed %d items)', 'wp-staging'), static::getTaskTitle(), (int)$this->processedNow));
116
117 return $this->generateResponse(false);
118 }
119
120 protected function getOriginalSuffix()
121 {
122 return '_wpstg_tmp';
123 }
124
125 /**
126 * Concrete classes of the FileRestoreTask must build
127 * the queue once, enqueuing everything that needs
128 * to be moved or deleted, using $this->enqueueMove
129 * or $this->enqueueDelete.
130 *
131 * @return void
132 */
133 abstract protected function buildQueue();
134
135 /**
136 * Skip the task if part is missing for this task
137 *
138 * @return array
139 */
140 abstract protected function getParts();
141
142 /**
143 * Skip the task if part is missing for this task
144 *
145 * @throws MissingFileException
146 */
147 protected function checkMissingParts()
148 {
149 if (!$this->jobDataDto->getBackupMetadata()->getIsMultipartBackup()) {
150 return;
151 }
152
153 $parts = $this->getParts();
154
155 $backupDir = $this->directory->getBackupDirectory();
156
157 foreach ($parts as $part) {
158 $filepath = $backupDir . $part;
159 if (!file_exists($filepath)) {
160 throw new MissingFileException();
161 }
162 }
163 }
164
165 /**
166 * Executes the next item in the queue.
167 */
168 protected function processNextItemInQueue()
169 {
170 $nextInQueueRaw = $this->taskQueue->dequeue();
171
172 if (is_null($nextInQueueRaw)) {
173 throw new FinishedQueueException();
174 }
175
176 // Skip blank lines
177 if ($nextInQueueRaw === '') {
178 return;
179 }
180
181 $nextInQueue = json_decode($nextInQueueRaw, true);
182
183 // Make sure we read expected data from the queue
184 if (!is_array($nextInQueue)) {
185 $this->logger->warning(sprintf(
186 __('%s: An internal error occurred that prevented this item from being restored. Skipping it... (Error Code: INVALID_QUEUE_ITEM)', 'wp-staging'),
187 static::getTaskTitle()
188 ));
189 $this->logger->debug($nextInQueueRaw);
190
191 return;
192 }
193
194 // Make sure data is in the expected format
195 array_map(function ($requiredKey) use ($nextInQueue, $nextInQueueRaw) {
196 if (!array_key_exists($requiredKey, $nextInQueue)) {
197 $this->logger->warning(sprintf(
198 __('%s: An internal error occurred that prevented this item from being restored. Skipping it... (Error Code: INVALID_QUEUE_ITEM)', 'wp-staging'),
199 static::getTaskTitle()
200 ));
201 $this->logger->debug($nextInQueueRaw);
202
203 return;
204 }
205 }, ['action', 'source', 'destination']);
206
207 $source = $nextInQueue['source'];
208
209 // Make sure destination is within WordPress
210 // @todo Test backup in Windows and restoring in Linux and vice-versa
211 $destination = $nextInQueue['destination'];
212 $destination = wp_normalize_path($destination);
213
214 // Executes the action
215 $this->restoreFileProcessor->handle($nextInQueue['action'], $source, $destination, $this, $this->logger);
216 }
217
218 /**
219 * @param string $source Source path to move.
220 * @param string $destination Where to move source to.
221 */
222 public function enqueueMove($source, $destination)
223 {
224 $this->enqueue([
225 'action' => 'move',
226 'source' => wp_normalize_path($source),
227 'destination' => wp_normalize_path($destination),
228 ]);
229 }
230
231 /**
232 * @param string $path The path to delete. Can be a folder, which will be deleted recursively.
233 */
234 public function enqueueDelete($path)
235 {
236 $this->enqueue([
237 'action' => 'delete',
238 'source' => '',
239 'destination' => wp_normalize_path($path),
240 ]);
241 }
242
243 /**
244 * Use to retry last action in next request,
245 * if it wasn't completed in current request.
246 */
247 public function retryLastActionInNextRequest()
248 {
249 $this->taskQueue->retry($dequeue = false);
250 }
251
252 /**
253 * @return bool
254 */
255 protected function isRestoreOnSubsite(): bool
256 {
257 if (!is_multisite()) {
258 return false;
259 }
260
261 return $this->jobDataDto->getBackupMetadata()->getBackupType() !== BackupMetadata::BACKUP_TYPE_MULTISITE;
262 }
263
264 /**
265 * @param array $action An array of actions to perform.
266 */
267 private function enqueue($action)
268 {
269 $this->taskQueue->enqueue(json_encode($action));
270 }
271 }
272