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