PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 3.7.1
WP STAGING – WordPress Backup, Restore, Migration & Clone v3.7.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 / BackupValidator.php
wp-staging / Backup Last commit date
Ajax 2 years ago BackgroundProcessing 2 years ago Dto 2 years ago Entity 2 years ago Exceptions 2 years ago Interfaces 2 years ago Job 2 years ago Request 2 years ago Service 2 years ago Storage 2 years ago Task 2 years ago AfterRestore.php 3 years ago BackupDeleter.php 3 years ago BackupDownload.php 2 years ago BackupFileIndex.php 2 years ago BackupHeader.php 2 years ago BackupProcessLock.php 2 years ago BackupRepairer.php 3 years ago BackupRetentionHandler.php 2 years ago BackupScheduler.php 2 years ago BackupServiceProvider.php 2 years ago BackupValidator.php 2 years ago FileHeader.php 2 years ago FileHeaderAttribute.php 2 years ago WithBackupIdentifier.php 2 years ago wpstgBackupHeader.txt 3 years ago
BackupValidator.php
261 lines
1 <?php
2
3 namespace WPStaging\Backup;
4
5 use WPStaging\Framework\Filesystem\FileObject;
6 use WPStaging\Backup\Entity\BackupMetadata;
7 use WPStaging\Backup\Exceptions\BackupRuntimeException;
8 use WPStaging\Backup\Service\BackupsFinder;
9 use WPStaging\Framework\Utils\Strings;
10 use WPStaging\Backup\Task\Tasks\JobRestore\RestoreRequirementsCheckTask;
11
12 use function WPStaging\functions\debug_log;
13
14 class BackupValidator
15 {
16 /** @var BackupsFinder */
17 private $backupsFinder;
18
19 /** @var array */
20 protected $missingPartIssues = [];
21
22 /** @var array */
23 protected $partSizeIssues = [];
24
25 /** @var string */
26 protected $backupDir;
27
28 /** @var array */
29 protected $existingParts = [];
30
31 /** @var string */
32 protected $error = '';
33
34 /** @var string[] */
35 private $lineBreaks;
36
37 /** @var Strings */
38 private $strings;
39
40 public function __construct(BackupsFinder $backupsFinder, Strings $strings)
41 {
42 $this->partSizeIssues = [];
43 $this->missingPartIssues = [];
44 $this->backupsFinder = $backupsFinder;
45 $this->backupDir = '';
46 $this->strings = $strings;
47
48 $this->lineBreaks = [
49 "\r",
50 "\n",
51 "\r\n",
52 "\n\r",
53 PHP_EOL
54 ];
55 }
56
57 /** @return array */
58 public function getMissingPartIssues()
59 {
60 return $this->missingPartIssues;
61 }
62
63 /** @return array */
64 public function getPartSizeIssues()
65 {
66 return $this->partSizeIssues;
67 }
68
69 /** @return string */
70 public function getErrorMessage()
71 {
72 return $this->error;
73 }
74
75 /**
76 * @param FileObject $file
77 * @param BackupMetadata $metadata
78 * @return bool
79 */
80 public function validateFileIndex(FileObject $file, BackupMetadata $metadata)
81 {
82 // Early bail if not wpstg file
83 if ($file->getExtension() !== 'wpstg') {
84 return true;
85 }
86
87 $start = $metadata->getHeaderStart();
88 $end = $metadata->getHeaderEnd();
89 if ($end - $start < 4) {
90 $error = sprintf(esc_html('File Index of %s not found!'), $file->getFilename());
91 debug_log($error);
92 $this->error = $error;
93
94 return false;
95 }
96
97 if (!$this->validateFileIndexFirstLine($file, $metadata)) {
98 return false;
99 }
100
101 $file->fseek($start);
102 $count = 0;
103 while ($file->valid() && $file->ftell() < $end) {
104 $line = $file->readAndMoveNext();
105 if (empty($line) || in_array($line, $this->lineBreaks)) {
106 continue;
107 }
108
109 $count++;
110 }
111
112 $totalFiles = $metadata->getTotalFiles();
113 if ($count !== $totalFiles && !$metadata->getIsMultipartBackup()) {
114 $error = sprintf(esc_html('File Index of %s is invalid! Actual number of files in the backup index: %s. Expected number of files: %s'), $file->getFilename(), $count, $totalFiles);
115 $this->error = $error;
116 debug_log($error);
117
118 return false;
119 }
120
121 if (!$metadata->getIsMultipartBackup()) {
122 return true;
123 }
124
125 $totalFiles = $metadata->getMultipartMetadata()->getTotalFiles();
126 if ($count !== $totalFiles && $metadata->getIsMultipartBackup()) {
127 $error = sprintf(esc_html('File Index of %s multipart backup is invalid! Actual number of files in the backup index: %s. Expected number of files: %s'), $file->getFilename(), $count, $totalFiles);
128 $this->error = $error;
129 debug_log($error);
130
131 return false;
132 }
133
134 return true;
135 }
136
137 /**
138 * @param FileObject $file
139 * @param BackupMetadata $metadata
140 * @return bool
141 */
142 public function validateFileIndexFirstLine(FileObject $file, BackupMetadata $metadata): bool
143 {
144 $version = $metadata->getBackupVersion();
145 if (version_compare($version, BackupHeader::MIN_BACKUP_VERSION, '>=')) {
146 return true;
147 }
148
149 $start = $metadata->getHeaderStart();
150 $file->fseek($start - 1);
151
152 if (!$file->valid()) {
153 return true;
154 }
155
156 $line = $file->readAndMoveNext();
157 if (in_array($line, $this->lineBreaks)) {
158 $line = $file->readAndMoveNext(); // first line is break line, that's fine, move to next then!
159 }
160
161 if (!$this->strings->startsWith($line, 'wpstg_')) {
162 $error = sprintf(esc_html('File Index of %s is invalid! The file index first line does not begin with `wpstg_`. The current first line is: %s'), $file->getFilename(), $line);
163 $this->error = $error;
164 debug_log($error);
165
166 return false;
167 }
168
169 return true;
170 }
171
172 /**
173 * @return bool
174 * @throws BackupRuntimeException
175 */
176 public function checkIfSplitBackupIsValid(BackupMetadata $metadata): bool
177 {
178 $this->partSizeIssues = [];
179 $this->missingPartIssues = [];
180
181 // Early bail if not split backup
182 if (!$metadata->getIsMultipartBackup()) {
183 return true;
184 }
185
186 $this->backupDir = wp_normalize_path($this->backupsFinder->getBackupsDirectory());
187
188 $splitMetadata = $metadata->getMultipartMetadata();
189
190 foreach ($splitMetadata->getPluginsParts() as $part) {
191 $this->validatePart($part, 'plugins');
192 }
193
194 foreach ($splitMetadata->getThemesParts() as $part) {
195 $this->validatePart($part, 'themes');
196 }
197
198 foreach ($splitMetadata->getUploadsParts() as $part) {
199 $this->validatePart($part, 'uploads');
200 }
201
202 foreach ($splitMetadata->getMuPluginsParts() as $part) {
203 $this->validatePart($part, 'muplugins');
204 }
205
206 foreach ($splitMetadata->getOthersParts() as $part) {
207 $this->validatePart($part, 'others');
208 }
209
210 foreach ($splitMetadata->getDatabaseParts() as $part) {
211 $this->validatePart($part, 'database');
212 }
213
214 return empty($this->partSizeIssues) && empty($this->missingPartIssues);
215 }
216
217 /**
218 * @param BackupMetadata $metadata
219 * @return bool
220 */
221 public function isUnsupportedBackupVersion(BackupMetadata $metadata): bool
222 {
223 $isCreatedOnPro = $metadata->getCreatedOnPro();
224 $version = $metadata->getWpstgVersion();
225 if (!$isCreatedOnPro) {
226 return false;
227 }
228
229 return version_compare($version, RestoreRequirementsCheckTask::BETA_VERSION_LIMIT_PRO, '<');
230 }
231
232 /**
233 * @param string $part contains part name
234 * @param string $type (plugins|themes|uploads|muplugins|others|database)
235 *
236 * @return void
237 */
238 private function validatePart(string $part, string $type)
239 {
240 $path = $this->backupDir . str_replace($this->backupDir, '', wp_normalize_path(untrailingslashit($part)));
241 if (!file_exists($path)) {
242 $this->missingPartIssues[] = [
243 'name' => $part,
244 'type' => $type
245 ];
246
247 return;
248 }
249
250 $metadata = new BackupMetadata();
251 $metadata = $metadata->hydrateByFilePath($path);
252
253 if (filesize($path) !== $metadata->getMultipartMetadata()->getPartSize()) {
254 $this->partSizeIssues[] = $part;
255 return;
256 }
257
258 $this->existingParts[] = $part;
259 }
260 }
261