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