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