PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.0.0
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.0.0
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 / BackupHeader.php
wp-staging / Backup Last commit date
Ajax 1 year ago BackgroundProcessing 1 year ago Dto 1 year ago Entity 1 year ago Exceptions 1 year ago Interfaces 1 year ago Job 1 year ago Request 2 years ago Service 1 year ago Storage 1 year ago Task 1 year ago AfterRestore.php 1 year ago BackupDeleter.php 1 year ago BackupDownload.php 1 year ago BackupFileIndex.php 1 year ago BackupHeader.php 1 year ago BackupRepairer.php 1 year ago BackupRetentionHandler.php 1 year ago BackupScheduler.php 1 year ago BackupServiceProvider.php 1 year ago BackupValidator.php 1 year ago FileHeader.php 1 year ago FileHeaderAttribute.php 2 years ago WithBackupIdentifier.php 1 year ago
BackupHeader.php
363 lines
1 <?php
2
3 namespace WPStaging\Backup;
4
5 use WPStaging\Framework\Filesystem\FileObject;
6 use WPStaging\Framework\Utils\DataEncoder;
7 use WPStaging\Framework\Utils\Version;
8
9 /**
10 * Backup Header class
11 * It will generate the header of the backup file
12 * and read the header of the backup file
13 * and update the header of the backup file
14 */
15 class BackupHeader
16 {
17 /** @var string */
18 const WPSTG_SQL_BACKUP_DUMP_HEADER = "-- WP Staging SQL Backup Dump\n";
19
20 /**
21 * In Length
22 * @var int
23 */
24 const HEADER_SIZE = 512;
25
26 /**
27 * @var string
28 */
29 const HEADER_IN_USE_HEX_FORMAT = '48888';
30
31 /**
32 * File magic
33 * should not exceed 8 characters
34 * @var string
35 */
36 const MAGIC = "wpstg";
37
38 /**
39 * Magic size in length
40 * @var int
41 */
42 const MAGIC_SIZE = 8;
43
44 /**
45 * Minimum Backup version that support this new header
46 *
47 * @var string
48 */
49 const MIN_BACKUP_VERSION = '2.0.0';
50
51 /**
52 * Backup version
53 * Should not exceed 4-bytes unsigned limit 4294967295
54 * In the format X.Y.Z
55 * Where X is the major version and can be upto 429495 :)
56 * Where Y is the minor version and can be upto 99
57 * Where Z is the patch version and can be upto 99
58 *
59 * @var string
60 */
61 const BACKUP_VERSION = '2.0.0';
62
63 /**
64 * Original string should not exceed 64 characters for consistency
65 * Generated from running bin2hex(str_pad("orignalString", 64 "\0", STR_PAD_RIGHT)) to 128 characters hex string
66 * To retrieve original string run hex2bin(this.constant)
67 * @var string
68 */
69 const COPYRIGHT_TEXT = '57502053746167696e672066696c6520666f726d61742062792052656e65204865726d656e617520262048617373616e20536861666971756520323032342f30';
70
71 /**
72 * Copyright text size
73 * @var int
74 */
75 const COPYRIGHT_TEXT_SIZE = 128;
76
77 /**
78 * @var string
79 */
80 private $magic;
81
82 /**
83 * @var int
84 */
85 private $backupVersion;
86
87 /**
88 * @var int
89 */
90 private $filesIndexStartOffset = 0;
91
92 /**
93 * @var int
94 */
95 private $filesIndexEndOffset = 0;
96
97 /**
98 * @var int
99 */
100 private $metadataStartOffset = 0;
101
102 /**
103 * @var int
104 */
105 private $metadataEndOffset = 0;
106
107 /**
108 * @var string
109 */
110 private $copyrightText;
111
112 /** @var DataEncoder */
113 private $encoder;
114
115 /** @var Version */
116 private $versionUtil;
117
118 /**
119 * BackupHeader constructor.
120 * @param DataEncoder $encoder
121 * @param Version $versionUtil
122 */
123 public function __construct(DataEncoder $encoder, Version $versionUtil)
124 {
125 $this->encoder = $encoder;
126 $this->versionUtil = $versionUtil;
127 $this->backupVersion = $this->versionUtil->convertStringFormatToIntFormat(self::BACKUP_VERSION);
128 }
129
130 /**
131 * Get backup version in XYYZZ integer format
132 *
133 * Where ZZ is the patch version from 00 to 99
134 * Where YY is the minor version from 00 to 99
135 * Where X is the major version from 0 to 429495
136 *
137 * @return int
138 */
139 public function getBackupVersion(): int
140 {
141 return $this->backupVersion;
142 }
143
144 /**
145 * Get backup version in X.Y.Z string format
146 *
147 * Where Z is the patch version from 0 to 99
148 * Where Y is the minor version from 0 to 99
149 * Where X is the major version from 0 to 429495
150 *
151 * @return string
152 */
153 public function getFormattedBackupVersion(): string
154 {
155 return $this->versionUtil->convertIntFormatToStringFormat($this->backupVersion);
156 }
157
158 public function getMetadataStartOffset(): int
159 {
160 return $this->metadataStartOffset;
161 }
162
163 public function setMetadataStartOffset(int $metadataStartOffset): BackupHeader
164 {
165 $this->metadataStartOffset = $metadataStartOffset;
166 return $this;
167 }
168
169 public function getMetadataEndOffset(): int
170 {
171 return $this->metadataEndOffset;
172 }
173
174 public function setMetadataEndOffset(int $metadataEndOffset): BackupHeader
175 {
176 $this->metadataEndOffset = $metadataEndOffset;
177 return $this;
178 }
179
180 public function getFilesIndexStartOffset(): int
181 {
182 return $this->filesIndexStartOffset;
183 }
184
185 public function setFilesIndexStartOffset(int $filesIndexStartOffset): BackupHeader
186 {
187 $this->filesIndexStartOffset = $filesIndexStartOffset;
188 return $this;
189 }
190
191 public function getFilesIndexEndOffset(): int
192 {
193 return $this->filesIndexEndOffset;
194 }
195
196 public function setFilesIndexEndOffset(int $filesIndexEndOffset): BackupHeader
197 {
198 $this->filesIndexEndOffset = $filesIndexEndOffset;
199 return $this;
200 }
201
202 /**
203 * @param string $backupFilePath
204 * @return BackupHeader
205 *
206 * @throws \RuntimeException
207 */
208 public function readFromPath(string $backupFilePath): BackupHeader
209 {
210 if (!file_exists($backupFilePath)) {
211 throw new \RuntimeException('Backup file not found');
212 }
213
214 $file = new FileObject($backupFilePath, FileObject::MODE_READ);
215 return $this->readFromFileObject($file);
216 }
217
218 /**
219 * @param FileObject $file
220 * @return BackupHeader
221 *
222 * @throws \RuntimeException
223 */
224 public function readFromFileObject(FileObject $file): BackupHeader
225 {
226 if ($file->getSize() < self::HEADER_SIZE) {
227 throw new \RuntimeException('Invalid v2 format backup file');
228 }
229
230 $file->seek(0);
231 $rawHeader = $file->fread(self::HEADER_SIZE);
232
233 return $this->setupBackupHeaderFromRaw($rawHeader);
234 }
235
236 /**
237 * @param string $rawHeader
238 *
239 * @throws InvalidArgumentException
240 * @return BackupHeader
241 */
242 public function setupBackupHeaderFromRaw(string $rawHeader): BackupHeader
243 {
244 $this->magic = rtrim(substr($rawHeader, 0, self::MAGIC_SIZE));
245 $this->copyrightText = substr($rawHeader, self::HEADER_SIZE - self::COPYRIGHT_TEXT_SIZE, self::COPYRIGHT_TEXT_SIZE); // Don't trim this, because it's fixed length with null characters
246
247 // Dynamic part of header currently in use
248 $dynamicHeader = substr($rawHeader, self::MAGIC_SIZE, $this->getHeaderInUseSize());
249 $headerIntData = $this->encoder->hexToIntArray(self::HEADER_IN_USE_HEX_FORMAT, $dynamicHeader);
250 // Change the below code into [$a, $b, $c, $d, $e] = $array format when min php is 7.1
251 $this->backupVersion = $headerIntData[0];
252 $this->filesIndexStartOffset = $headerIntData[1];
253 $this->filesIndexEndOffset = $headerIntData[2];
254 $this->metadataStartOffset = $headerIntData[3];
255 $this->metadataEndOffset = $headerIntData[4];
256
257 return $this;
258 }
259
260 public function isValidBackupHeader(): bool
261 {
262 if ($this->magic !== self::MAGIC) {
263 return false;
264 }
265
266 if ($this->copyrightText !== self::COPYRIGHT_TEXT) {
267 return false;
268 }
269
270 return version_compare($this->getFormattedBackupVersion(), self::MIN_BACKUP_VERSION, '>=');
271 }
272
273 public function getHeader(): string
274 {
275 return sprintf(
276 '%s%s%s%s',
277 str_pad(self::MAGIC, self::MAGIC_SIZE, "\0", STR_PAD_RIGHT), // let write magic as it is without converting to hex
278 $this->encoder->intArrayToHex(
279 self::HEADER_IN_USE_HEX_FORMAT, // 36-bytes of hex data
280 [
281 $this->backupVersion,
282 $this->filesIndexStartOffset,
283 $this->filesIndexEndOffset,
284 $this->metadataStartOffset,
285 $this->metadataEndOffset
286 ]
287 ),
288 bin2hex(str_pad("", $this->getUnusedBytesSize(), "\0", STR_PAD_RIGHT)),
289 self::COPYRIGHT_TEXT // 64-bytes of fixed hex data
290 );
291 }
292
293 /**
294 * @param string $backupFilePath
295 * @return void
296 */
297 public function updateHeader(string $backupFilePath)
298 {
299 $header = $this->getHeader();
300 $file = new FileObject($backupFilePath, 'r+');
301 $file->seek(0);
302 $file->fwrite($header);
303 $file = null;
304 }
305
306 /**
307 * Validate Old Backup Header
308 * @param string $content
309 * @return bool
310 */
311 public function verifyV1FormatHeader(string $content): bool
312 {
313 if (empty($content)) {
314 return false;
315 }
316
317 $wpstgBackupHeaderFileContent = self::WPSTG_SQL_BACKUP_DUMP_HEADER;
318 $headerToVerifyLength = strlen($wpstgBackupHeaderFileContent);
319 if (substr($wpstgBackupHeaderFileContent, 0, $headerToVerifyLength) === substr($content, 0, $headerToVerifyLength)) {
320 return true;
321 }
322
323 $wpstgBackupHeaderFile = WPSTG_RESOURCES_DIR . 'wpstgBackupHeader.txt';
324 if (!file_exists($wpstgBackupHeaderFile)) {
325 return true;
326 }
327
328 $wpstgBackupHeaderFileContent = file_get_contents($wpstgBackupHeaderFile);
329 $headerToVerifyLength = self::HEADER_SIZE;
330 if (!empty($wpstgBackupHeaderFileContent) && substr($wpstgBackupHeaderFileContent, 0, $headerToVerifyLength) === substr($content, 0, $headerToVerifyLength)) {
331 return true;
332 }
333
334 return false;
335 }
336
337 public function getV1FormatHeader(): string
338 {
339 $wpstgBackupHeaderFile = WPSTG_RESOURCES_DIR . 'wpstgBackupHeader.txt';
340 // Should not happen
341 if (!file_exists($wpstgBackupHeaderFile)) {
342 return "";
343 }
344
345 return file_get_contents($wpstgBackupHeaderFile);
346 }
347
348 private function getHeaderInUseSize(): int
349 {
350 $size = 0;
351 for ($i = 0; $i < strlen(self::HEADER_IN_USE_HEX_FORMAT); $i++) {
352 $size += intval(substr(self::HEADER_IN_USE_HEX_FORMAT, $i, 1));
353 }
354
355 return $size * 2;
356 }
357
358 private function getUnusedBytesSize(): int
359 {
360 return (self::HEADER_SIZE - $this->getHeaderInUseSize() - self::MAGIC_SIZE - self::COPYRIGHT_TEXT_SIZE) / 2;
361 }
362 }
363