PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.9.2
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.9.2
4.9.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 / Service / BackupsFinder.php
wp-staging / Backup / Service Last commit date
Compression 4 months ago Database 2 weeks ago AbstractBackupsFinder.php 1 year ago AbstractExtractor.php 1 day ago AbstractServiceProvider.php 2 years ago Archiver.php 2 weeks ago BackupAssets.php 1 month ago BackupContent.php 1 year ago BackupMetadataEditor.php 1 year ago BackupMetadataReader.php 5 months ago BackupSigner.php 7 months ago BackupsDirectoryResolver.php 2 weeks ago BackupsFinder.php 2 weeks ago Extractor.php 2 weeks ago FileBackupService.php 1 day ago FileBackupServiceProvider.php 2 years ago ServiceInterface.php 2 years ago TmpBackupCleaner.php 2 weeks ago ZlibCompressor.php 11 months ago
BackupsFinder.php
193 lines
1 <?php
2
3 namespace WPStaging\Backup\Service;
4
5 use RuntimeException;
6 use SplFileInfo;
7 use Throwable;
8 use WPStaging\Core\WPStaging;
9 use WPStaging\Framework\Adapter\Directory;
10 use WPStaging\Framework\Filesystem\DirectoryListing;
11 use WPStaging\Framework\Filesystem\FileObject;
12 use WPStaging\Framework\Filesystem\Filesystem;
13 use WPStaging\Backup\BackupValidator;
14 use WPStaging\Backup\Entity\BackupMetadata;
15 use WPStaging\Backup\Exceptions\BackupRuntimeException;
16 use WPStaging\Framework\Network\RemoteDownloader;
17
18 use function WPStaging\functions\debug_log;
19
20 /**
21 * Class BackupsFinder
22 *
23 * Finds the .wpstg backups in the filesystem.
24 *
25 * @package WPStaging\Backup
26 */
27 class BackupsFinder extends AbstractBackupsFinder
28 {
29 /** @var string */
30 const FILTER_BACKUP_DIRECTORY = 'wpstg.backup.directory';
31
32 /** @var Directory */
33 private $directory;
34
35 /** @var Filesystem */
36 private $filesystem;
37
38 /** @var DirectoryListing */
39 private $directoryListing;
40
41 /** @var BackupsDirectoryResolver */
42 private $backupsDirectoryResolver;
43
44 public function __construct(Directory $directory, Filesystem $filesystem, DirectoryListing $directoryListing, BackupsDirectoryResolver $backupsDirectoryResolver)
45 {
46 $this->directory = $directory;
47 $this->filesystem = $filesystem;
48 $this->directoryListing = $directoryListing;
49 $this->backupsDirectoryResolver = $backupsDirectoryResolver;
50 }
51
52 /**
53 * @param bool $refresh
54 * @return string
55 * @throws BackupRuntimeException
56 */
57 public function getBackupsDirectory(bool $refresh = false): string
58 {
59 if ($refresh || $this->backupsDirectory === null) {
60 $pluginUploadsDirectory = $this->directory->getPluginUploadsDirectory($refresh = true);
61 $directory = $this->backupsDirectoryResolver->resolveFromPluginUploadsDirectory($pluginUploadsDirectory);
62
63 if (!$this->filesystem->mkdir($directory, true)) {
64 throw BackupRuntimeException::cannotCreateBackupsDirectory($directory);
65 }
66
67 if (!is_readable($directory)) {
68 throw BackupRuntimeException::backupsDirectoryNotReadable($directory);
69 }
70
71 if (!is_writeable($directory)) {
72 throw BackupRuntimeException::backupsDirectoryNotWriteable($directory);
73 }
74
75 $this->directoryListing->maybeUpdateOldHtaccessWebConfig($directory);
76
77 $this->backupsDirectory = $directory;
78 }
79
80 return $this->backupsDirectory;
81 }
82
83 /**
84 * Delete temp backup files associated with a job ID.
85 * This includes the temp backup file, any in-progress upload file,
86 * and the renamed .wpstg file from remote sync downloads.
87 *
88 * @param string $jobId
89 * @return void
90 */
91 public function deleteTempBackupByJobId(string $jobId)
92 {
93 $backupsDir = $this->getBackupsDirectory();
94
95 // Delete the temp backup file (e.g., {jobId}.wpstgtmp)
96 $tempBackupPath = $backupsDir . $jobId . '.' . Archiver::TMP_BACKUP_EXTENSION;
97 if (file_exists($tempBackupPath)) {
98 $this->filesystem->delete($tempBackupPath);
99 }
100
101 // Delete the in-progress upload file (e.g., {jobId}.wpstgtmp.uploading)
102 $uploadingPath = $tempBackupPath . '.' . RemoteDownloader::UPLOADING_EXTENSION;
103 if (file_exists($uploadingPath)) {
104 $this->filesystem->delete($uploadingPath);
105 }
106
107 // Delete the renamed backup file from remote sync (e.g., {jobId}.wpstg)
108 // This happens when a download "completes" but may be corrupt/incomplete
109 $renamedBackupPath = $backupsDir . $jobId . '.' . Archiver::BACKUP_EXTENSION;
110 if (file_exists($renamedBackupPath)) {
111 $this->filesystem->delete($renamedBackupPath);
112 }
113 }
114
115 /**
116 * @param string $scheduleId
117 *
118 * @return SplFileInfo[]
119 */
120 public function findBackupByScheduleId(string $scheduleId): array
121 {
122 $backups = array_filter($this->findBackups(), function ($splFileInfo) use ($scheduleId) {
123 $backupFile = $splFileInfo->getPathname();
124 try {
125 $metadata = (new BackupMetadata())->hydrateByFilePath($backupFile);
126
127 return $metadata->getScheduleId() == $scheduleId;
128 } catch (Throwable $ex) {
129 debug_log("WP STAGING: Could not find backup by schedule ID {$scheduleId} - File: {$backupFile} - " . $ex->getMessage());
130
131 return false;
132 }
133 });
134
135 if (empty($backups)) {
136 return [];
137 }
138
139 return $backups;
140 }
141
142 /**
143 * @return bool
144 * @throws \WPStaging\Framework\Job\Exception\DiskNotWritableException
145 */
146 public function hasInvalidFileIndex(): bool
147 {
148 $backupFiles = $this->findBackups();
149 $hasInvalidFilesIndexBackup = false;
150
151 /** @var BackupValidator */
152 $backupValidator = WPStaging::make(BackupValidator::class);
153
154 /** @var SplFileInfo $file */
155 foreach ($backupFiles as $file) {
156 if ($file->getExtension() !== 'wpstg') {
157 continue;
158 }
159
160 $isValidFileIndex = false;
161 try {
162 $isValidFileIndex = $this->validateBackupFileIndex($backupValidator, $file->getRealPath());
163 } catch (RuntimeException $ex) {
164 debug_log('Backup Validation: ' . $ex->getMessage());
165 continue;
166 }
167
168 if (!$isValidFileIndex) {
169 $hasInvalidFilesIndexBackup = true;
170 break;
171 }
172 }
173
174 return $hasInvalidFilesIndexBackup;
175 }
176
177 /**
178 * @param BackupValidator $backupValidator
179 * @param string $backupPath
180 * @return bool
181 *
182 * @throws RuntimeException
183 */
184 protected function validateBackupFileIndex(BackupValidator $backupValidator, string $backupPath): bool
185 {
186 $backupMetadata = new BackupMetadata();
187 $backupMetadata = $backupMetadata->hydrateByFilePath($backupPath);
188 $fileObject = new FileObject($backupPath);
189
190 return $backupValidator->validateFileIndex($fileObject, $backupMetadata);
191 }
192 }
193