PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 3.8.2
WP STAGING – WordPress Backup, Restore, Migration & Clone v3.8.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 2 years ago Database 1 year ago AbstractServiceProvider.php 2 years ago Archiver.php 2 years ago BackupAssets.php 2 years ago BackupMetadataEditor.php 3 years ago BackupSigner.php 2 years ago BackupsFinder.php 2 years ago Extractor.php 2 years ago FileBackupService.php 2 years ago FileBackupServiceProvider.php 2 years ago ServiceInterface.php 2 years ago ZlibCompressor.php 2 years ago
BackupsFinder.php
221 lines
1 <?php
2
3 namespace WPStaging\Backup\Service;
4
5 use Exception;
6 use RuntimeException;
7 use SplFileInfo;
8 use Throwable;
9 use WPStaging\Core\WPStaging;
10 use WPStaging\Framework\Adapter\Directory;
11 use WPStaging\Framework\Filesystem\DirectoryListing;
12 use WPStaging\Framework\Filesystem\FileObject;
13 use WPStaging\Framework\Filesystem\Filesystem;
14 use WPStaging\Backup\BackupValidator;
15 use WPStaging\Backup\Entity\BackupMetadata;
16 use WPStaging\Backup\Exceptions\BackupRuntimeException;
17 use WPStaging\Backup\WithBackupIdentifier;
18
19 use function WPStaging\functions\debug_log;
20
21 /**
22 * Class BackupsFinder
23 *
24 * Finds the .wpstg backups in the filesystem.
25 *
26 * @package WPStaging\Backup
27 */
28 class BackupsFinder
29 {
30 use WithBackupIdentifier;
31
32 private $directory;
33 private $filesystem;
34 private $filteredBackupsDirectory;
35 private $directoryListing;
36
37 public function __construct(Directory $directory, Filesystem $filesystem, DirectoryListing $directoryListing)
38 {
39 $this->directory = $directory;
40 $this->filesystem = $filesystem;
41 $this->directoryListing = $directoryListing;
42 }
43
44 /**
45 * @param bool $refresh
46 * @return string
47 * @throws BackupRuntimeException
48 */
49 public function getBackupsDirectory(bool $refresh = false): string
50 {
51 if ($refresh || $this->filteredBackupsDirectory === null) {
52 $defaultBackupUploadsDirectory = $this->directory->getPluginUploadsDirectory($refresh = true) . Archiver::BACKUP_DIR_NAME;
53
54 /**
55 * Allows filtering the path to the directory Backups will be written to and read from.
56 *
57 * Note: changing this directory while there are backups in the previous location will, in
58 * fact, hide those Backups from the plugin. The task of moving the Backups left in the previous
59 * location(s) is left to the user.
60 *
61 * By default it uses the folder ABSPATH/wp-content/uploads/wp-staging/backups
62 * You can overwrite the path with the filter wpstg.backup.directory.
63 * The filtered provided path needs to be an absolute path that is inside the WordPress root (ABSPATH)
64 * E.g. If ABSPATH: '/var/www/example.com' then filtered path can be '/var/www/example.com/backups'. It can not be '/var/www/backups'
65
66 *
67 * @param string $defaultBackupUploadsDirectory The default path to the directory Backups will be read from and
68 * written to.
69 */
70 $directory = apply_filters('wpstg.backup.directory', $defaultBackupUploadsDirectory);
71
72 $directory = trailingslashit(wp_normalize_path($directory));
73
74 if (!$this->filesystem->mkdir($directory, true)) {
75 throw BackupRuntimeException::cannotCreateBackupsDirectory($directory);
76 }
77
78 if (!is_readable($directory)) {
79 throw BackupRuntimeException::backupsDirectoryNotReadable($directory);
80 }
81
82 if (!is_writeable($directory)) {
83 throw BackupRuntimeException::backupsDirectoryNotWriteable($directory);
84 }
85
86 $this->directoryListing->maybeUpdateOldHtaccessWebConfig($directory);
87
88 $this->filteredBackupsDirectory = $directory;
89 }
90
91 return $this->filteredBackupsDirectory;
92 }
93
94 /**
95 * @return array An array of SplFileInfo objects of .wpstg backup files.
96 */
97 public function findBackups(): array
98 {
99 try {
100 $it = new \DirectoryIterator($this->getBackupsDirectory(true));
101 } catch (\Exception $e) {
102 \WPStaging\functions\debug_log('WP STAGING: Could not find backup directory ' . $e->getMessage());
103
104 return [];
105 }
106
107 $backups = [];
108
109 $this->clearListedMultipartBackups();
110
111 /** @var SplFileInfo $file */
112 foreach ($it as $file) {
113 if (($file->getExtension() === 'wpstg' || $file->getExtension() === 'sql') && !$file->isLink()) {
114 if ($this->isBackupPart($file->getFilename()) && $this->isListedMultipartBackup($file->getFilename())) {
115 continue;
116 }
117
118 $backups[] = clone $file;
119 }
120 }
121
122 return $backups;
123 }
124
125 /**
126 * @param string $md5
127 *
128 * @return SplFileInfo
129 */
130 public function findBackupByMd5Hash(string $md5): SplFileInfo
131 {
132 $backup = array_filter($this->findBackups(), function ($splFileInfo) use ($md5) {
133 return md5($splFileInfo->getBasename()) === $md5;
134 });
135
136 if (empty($backup)) {
137 throw new \UnexpectedValueException('WP STAGING: Could not find backup by hash ' . $md5);
138 }
139
140 return array_shift($backup);
141 }
142
143 /**
144 * @param string $scheduleId
145 *
146 * @return SplFileInfo[]
147 */
148 public function findBackupByScheduleId(string $scheduleId): array
149 {
150 $backups = array_filter($this->findBackups(), function ($splFileInfo) use ($scheduleId) {
151 $backupFile = $splFileInfo->getPathname();
152 try {
153 $metadata = (new BackupMetadata())->hydrateByFilePath($backupFile);
154
155 return $metadata->getScheduleId() == $scheduleId;
156 } catch (Throwable $ex) {
157 debug_log("WP STAGING: Could not find backup by schedule ID {$scheduleId} - File: {$backupFile} - " . $ex->getMessage());
158
159 return false;
160 }
161 });
162
163 if (empty($backups)) {
164 return [];
165 }
166
167 return $backups;
168 }
169
170 /**
171 * @return bool
172 * @throws \WPStaging\Backup\Exceptions\DiskNotWritableException
173 */
174 public function hasInvalidFileIndex(): bool
175 {
176 $backupFiles = $this->findBackups();
177 $hasInvalidFilesIndexBackup = false;
178
179 /** @var BackupValidator */
180 $backupValidator = WPStaging::make(BackupValidator::class);
181
182 /** @var SplFileInfo $file */
183 foreach ($backupFiles as $file) {
184 if ($file->getExtension() !== 'wpstg') {
185 continue;
186 }
187
188 $isValidFileIndex = false;
189 try {
190 $isValidFileIndex = $this->validateBackupFileIndex($backupValidator, $file->getRealPath());
191 } catch (RuntimeException $ex) {
192 debug_log('Backup Validation: ' . $ex->getMessage());
193 continue;
194 }
195
196 if (!$isValidFileIndex) {
197 $hasInvalidFilesIndexBackup = true;
198 break;
199 }
200 }
201
202 return $hasInvalidFilesIndexBackup;
203 }
204
205 /**
206 * @param BackupValidator $backupValidator
207 * @param string $backupPath
208 * @return bool
209 *
210 * @throws RuntimeException
211 */
212 protected function validateBackupFileIndex(BackupValidator $backupValidator, string $backupPath): bool
213 {
214 $backupMetadata = new BackupMetadata();
215 $backupMetadata = $backupMetadata->hydrateByFilePath($backupPath);
216 $fileObject = new FileObject($backupPath);
217
218 return $backupValidator->validateFileIndex($fileObject, $backupMetadata);
219 }
220 }
221