PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backups, Restore, Migration & Clone / 4.11.0
WP STAGING – WordPress Backups, Restore, Migration & Clone v4.11.0
4.11.0 4.10.0 4.9.5 4.9.4 4.9.3 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 / BeforeUpdateBackupsService.php
wp-staging / Backup / Service Last commit date
Compression 1 day ago Database 1 day ago DirectoryExplorer 1 day ago AbstractBackgroundBackupRequest.php 1 day ago AbstractBackupsFinder.php 1 day ago AbstractExtractor.php 1 day ago AbstractServiceProvider.php 1 day ago Archiver.php 1 day ago BackupAssets.php 1 day ago BackupContent.php 1 day ago BackupMetadataEditor.php 1 day ago BackupMetadataReader.php 1 day ago BackupSigner.php 1 day ago BackupsDirectoryResolver.php 1 day ago BackupsFinder.php 1 day ago BeforeUpdateBackupRequest.php 1 day ago BeforeUpdateBackupsService.php 1 day ago Extractor.php 1 day ago FileBackupService.php 1 day ago FileBackupServiceProvider.php 1 day ago ServiceInterface.php 1 day ago TmpBackupCleaner.php 1 day ago UpdateProtectionHealth.php 1 day ago UpdateProtectionSettings.php 1 day ago ZlibCompressor.php 1 day ago
BeforeUpdateBackupsService.php
265 lines
1 <?php
2
3 namespace WPStaging\Backup\Service;
4
5 use SplFileInfo;
6 use Throwable;
7 use WPStaging\Backup\Entity\BackupMetadata;
8 use WPStaging\Backup\Utils\BackupPathResolver;
9 use WPStaging\Framework\Facades\Hooks;
10
11 use function WPStaging\functions\debug_log;
12
13
14
15
16
17
18
19
20 class BeforeUpdateBackupsService
21 {
22
23
24
25
26 const FILTER_KEEP_COUNT = 'wpstg.backup.beforeUpdate.keepCount';
27
28
29
30
31
32 const FILTER_REUSE_WINDOW = 'wpstg.backup.beforeUpdate.reuseWindowSeconds';
33
34
35 const DEFAULT_KEEP_COUNT = 3;
36
37
38 const DEFAULT_REUSE_WINDOW = 900;
39
40
41
42
43
44 const SCOPE_KEYS = [
45 'isExportingPlugins',
46 'isExportingMuPlugins',
47 'isExportingThemes',
48 'isExportingUploads',
49 'isExportingOtherWpContentFiles',
50 'isExportingOtherWpRootFiles',
51 'isExportingDatabase',
52 ];
53
54
55 private $backupsFinder;
56
57
58 private $backupPathResolver;
59
60
61 private $backups = null;
62
63
64
65
66
67 public function __construct(BackupsFinder $backupsFinder, BackupPathResolver $backupPathResolver)
68 {
69 $this->backupsFinder = $backupsFinder;
70 $this->backupPathResolver = $backupPathResolver;
71 }
72
73
74
75
76
77
78
79
80 public function findReusableBackup(array $requiredScope = []): array
81 {
82 $now = time();
83 $oldest = $now - $this->getReuseWindow();
84
85 foreach ($this->findBackups() as $backup) {
86 if ($backup['dateCreated'] < $oldest) {
87 return [];
88 }
89
90 if ($backup['dateCreated'] > $now || !$this->covers($backup['scope'], $requiredScope)) {
91 continue;
92 }
93
94 return [
95 'name' => $backup['name'],
96 'ageMinutes' => (int)floor(($now - $backup['dateCreated']) / MINUTE_IN_SECONDS),
97 ];
98 }
99
100 return [];
101 }
102
103
104
105
106
107
108 private function covers(array $scope, array $requiredScope): bool
109 {
110 foreach (self::SCOPE_KEYS as $key) {
111 if (empty($requiredScope[$key])) {
112 continue;
113 }
114
115 if (empty($scope[$key])) {
116 return false;
117 }
118 }
119
120 return true;
121 }
122
123
124
125
126
127
128
129 public function prune(int $roomFor = 0): int
130 {
131 $keep = max(0, $this->getKeepCount() - $roomFor);
132
133 $backups = $this->findBackups();
134 if (count($backups) <= $keep) {
135 return 0;
136 }
137
138 $deleted = 0;
139 foreach (array_slice($backups, $keep) as $backup) {
140 if ($this->delete($backup['file'], $backup['parts'])) {
141 $deleted++;
142 }
143 }
144
145 $this->backups = null;
146
147 return $deleted;
148 }
149
150
151
152
153 private function findBackups(): array
154 {
155 if ($this->backups !== null) {
156 return $this->backups;
157 }
158
159 $backups = [];
160 foreach ($this->backupsFinder->findBackups() as $splFileInfo) {
161 $metadata = $this->readMetadata($splFileInfo);
162 if ($metadata === null || !$metadata->getIsBeforeUpdateBackup()) {
163 continue;
164 }
165
166 $backups[] = [
167 'file' => $splFileInfo,
168 'name' => $metadata->getName(),
169 'dateCreated' => (int)$metadata->getDateCreated(),
170 'parts' => $this->getParts($metadata),
171 'scope' => [
172 'isExportingPlugins' => $metadata->getIsExportingPlugins(),
173 'isExportingMuPlugins' => $metadata->getIsExportingMuPlugins(),
174 'isExportingThemes' => $metadata->getIsExportingThemes(),
175 'isExportingUploads' => $metadata->getIsExportingUploads(),
176 'isExportingOtherWpContentFiles' => $metadata->getIsExportingOtherWpContentFiles(),
177 'isExportingOtherWpRootFiles' => $metadata->getIsExportingOtherWpRootFiles(),
178 'isExportingDatabase' => $metadata->getIsExportingDatabase(),
179 ],
180 ];
181 }
182
183 usort($backups, function ($left, $right) {
184 return $right['dateCreated'] - $left['dateCreated'];
185 });
186
187 $this->backups = $backups;
188
189 return $backups;
190 }
191
192
193
194
195
196 private function readMetadata(SplFileInfo $splFileInfo)
197 {
198 try {
199 return (new BackupMetadata())->hydrateByFilePath($splFileInfo->getPathname());
200 } catch (Throwable $e) {
201 debug_log('WP STAGING: Could not read metadata while pruning backup-before-update backups - File: ' . $splFileInfo->getPathname() . ' - ' . $e->getMessage());
202
203 return null;
204 }
205 }
206
207
208
209
210
211 private function getParts(BackupMetadata $metadata): array
212 {
213 if (!$metadata->getIsMultipartBackup()) {
214 return [];
215 }
216
217 return $metadata->getMultipartMetadata()->getBackupParts();
218 }
219
220
221
222
223
224
225 private function delete(SplFileInfo $backup, array $parts): bool
226 {
227 foreach ($parts as $part) {
228 $partPath = $this->backupPathResolver->resolveBackupPartPath($part, $backup->getFilename());
229 if ($partPath === '' || !file_exists($partPath)) {
230 continue;
231 }
232
233 if (!unlink($partPath)) {
234 debug_log('WP STAGING: Could not delete backup part while pruning backup-before-update backups: ' . $partPath);
235
236 return false;
237 }
238 }
239
240 if (!unlink($backup->getPathname())) {
241 debug_log('WP STAGING: Could not delete backup while pruning backup-before-update backups: ' . $backup->getPathname());
242
243 return false;
244 }
245
246 return true;
247 }
248
249
250
251
252 private function getKeepCount(): int
253 {
254 return (int)Hooks::applyFilters(self::FILTER_KEEP_COUNT, self::DEFAULT_KEEP_COUNT);
255 }
256
257
258
259
260 private function getReuseWindow(): int
261 {
262 return (int)Hooks::applyFilters(self::FILTER_REUSE_WINDOW, self::DEFAULT_REUSE_WINDOW);
263 }
264 }
265