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