PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backups, Restore, Migration & Clone / 4.11.1
WP STAGING – WordPress Backups, Restore, Migration & Clone v4.11.1
4.11.1 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 / Ajax / ExploreCache.php
wp-staging / Backup / Ajax Last commit date
Backup 1 week ago FileList 1 week ago Restore 1 week ago Backup.php 1 week ago BackupDownloader.php 1 week ago BackupSizeCalculator.php 1 week ago BackupSpeedIndex.php 1 week ago BaseFileList.php 1 week ago BaseListing.php 1 week ago Delete.php 1 week ago Edit.php 6 days ago Explore.php 1 week ago ExploreCache.php 1 week ago FileInfo.php 1 week ago FileList.php 1 week ago Listing.php 1 week ago Parts.php 1 week ago Restore.php 1 week ago ScheduleList.php 1 week ago Upload.php 1 week ago
ExploreCache.php
266 lines
1 <?php
2
3 namespace WPStaging\Backup\Ajax;
4
5 use WPStaging\Backup\BackupFileIndex;
6 use WPStaging\Backup\Entity\BackupMetadata;
7 use WPStaging\Backup\FileHeader;
8 use WPStaging\Core\WPStaging;
9 use WPStaging\Framework\Filesystem\FileObject;
10 use WPStaging\Framework\Filesystem\Filesystem;
11 use WPStaging\Framework\Filesystem\PathIdentifier;
12 use WPStaging\Framework\Utils\Cache\Cache;
13
14
15
16
17
18
19
20
21 class ExploreCache
22 {
23
24 const LIFETIME = 3600;
25
26
27 const MAX_CACHE_SIZE = 20 * 1024 * 1024;
28
29
30 private $cache;
31
32
33 private $pathIdentifier;
34
35
36 private $filesystem;
37
38 public function __construct(Cache $cache, PathIdentifier $pathIdentifier, Filesystem $filesystem)
39 {
40 $this->cache = $cache;
41 $this->pathIdentifier = $pathIdentifier;
42 $this->filesystem = $filesystem;
43 }
44
45
46
47
48
49
50
51
52 public function getOrBuild(string $backupFile, BackupMetadata $metadata)
53 {
54 $this->configureCache($backupFile);
55
56 $cached = $this->read($backupFile);
57 if ($cached !== null) {
58 return $cached;
59 }
60
61 $tree = $this->buildTree($backupFile, $metadata);
62 if ($tree === null) {
63 return null;
64 }
65
66 $this->write($backupFile, $tree);
67
68 return $tree;
69 }
70
71
72
73
74 private function configureCache(string $backupFile)
75 {
76 $this->cache->setLifetime(self::LIFETIME);
77 $this->cache->setFilename('backup_explore_' . md5($backupFile));
78 }
79
80
81
82
83
84
85
86 private function read(string $backupFile)
87 {
88 if (!$this->cache->isValid(false)) {
89 return null;
90 }
91
92 $filePath = $this->cache->getFilePath();
93 if (filesize($filePath) > self::MAX_CACHE_SIZE) {
94 return null;
95 }
96
97 $data = $this->cache->get();
98 if (!is_array($data) || !isset($data['mtime'], $data['tree'])) {
99 return null;
100 }
101
102 $currentMtime = @filemtime($backupFile);
103 if ($currentMtime === false || (int)$data['mtime'] !== $currentMtime) {
104 return null;
105 }
106
107 return $data['tree'];
108 }
109
110
111
112
113
114
115
116 private function write(string $backupFile, array $tree)
117 {
118 $mtime = @filemtime($backupFile);
119 if ($mtime === false) {
120 return;
121 }
122
123 $this->cache->save([
124 'mtime' => $mtime,
125 'tree' => $tree,
126 ]);
127 }
128
129
130
131
132
133
134
135
136 private function buildTree(string $backupFile, BackupMetadata $metadata)
137 {
138 $indexLineDto = $this->createIndexLineDto($metadata);
139 $fileObject = new FileObject($backupFile, FileObject::MODE_READ);
140 $fileObject->fseek((int)$metadata->getHeaderStart());
141
142
143 $tree = ['' => ['dirs' => [], 'files' => []]];
144
145 $dirChildren = [];
146 $dirHasSubdirs = [];
147
148 while ($fileObject->valid() && $fileObject->ftell() < (int)$metadata->getHeaderEnd()) {
149 $indexOffset = $fileObject->ftell();
150 $rawIndexFile = $fileObject->readAndMoveNext();
151 if (!$indexLineDto->isIndexLine($rawIndexFile)) {
152 continue;
153 }
154
155 $backupFileIndex = $indexLineDto->readIndexLine($rawIndexFile);
156 $relativePath = $this->pathIdentifier->transformIdentifiableToRelativePath($backupFileIndex->getIdentifiablePath());
157 $relativePath = $this->filesystem->normalizePath($relativePath);
158
159 if ($relativePath === '' || $relativePath === '/') {
160 continue;
161 }
162
163 $lastSlash = strrpos($relativePath, '/');
164 $parentDir = $lastSlash === false ? '' : substr($relativePath, 0, $lastSlash);
165 $fileName = $lastSlash === false ? $relativePath : substr($relativePath, $lastSlash + 1);
166
167 if ($fileName === '') {
168 continue;
169 }
170
171
172 if (!isset($tree[$parentDir])) {
173 $tree[$parentDir] = ['dirs' => [], 'files' => []];
174 }
175
176
177
178 $parts = explode('/', $relativePath);
179 $depth = count($parts);
180
181 if ($depth > 1) {
182
183 $currentPath = '';
184 for ($i = 0; $i < $depth - 1; $i++) {
185 $dirName = $parts[$i];
186 $childPath = $currentPath === '' ? $dirName : $currentPath . '/' . $dirName;
187
188 if (!isset($tree[$currentPath])) {
189 $tree[$currentPath] = ['dirs' => [], 'files' => []];
190 }
191
192
193 if (!isset($tree[$currentPath]['dirs'][$dirName])) {
194 $tree[$currentPath]['dirs'][$dirName] = [
195 'name' => $dirName,
196 'path' => $childPath,
197 'hasChildren' => false,
198 ];
199 }
200
201
202 if ($i + 1 < $depth - 1) {
203
204 $dirHasSubdirs[$childPath] = true;
205 }
206
207
208 $nextPart = $parts[$i + 1];
209 $dirChildren[$childPath][$nextPart] = true;
210
211 $currentPath = $childPath;
212 }
213 }
214
215
216 $size = (int)$backupFileIndex->getUncompressedSize();
217 $tree[$parentDir]['files'][] = [
218 'name' => $fileName,
219 'path' => $relativePath,
220 'size' => $size,
221 'offset' => (int)$indexOffset,
222 ];
223 }
224
225 $fileObject = null;
226
227
228 foreach ($tree as $folderPath => &$bucket) {
229 $dirArray = [];
230 foreach ($bucket['dirs'] as $dirName => $dirData) {
231 $dirPath = $dirData['path'];
232 $dirData['hasChildren'] = isset($dirChildren[$dirPath]) && count($dirChildren[$dirPath]) > 0;
233 $dirData['items'] = isset($dirChildren[$dirPath]) ? count($dirChildren[$dirPath]) : 0;
234 $dirArray[] = $dirData;
235 }
236
237 usort($dirArray, function ($a, $b) {
238 return strcasecmp($a['name'], $b['name']);
239 });
240
241 $bucket['dirs'] = $dirArray;
242
243 usort($bucket['files'], function ($a, $b) {
244 return strcasecmp($a['name'], $b['name']);
245 });
246 }
247
248 unset($bucket);
249
250 return $tree;
251 }
252
253
254
255
256
257 private function createIndexLineDto(BackupMetadata $metadata)
258 {
259 if ($metadata->getIsBackupFormatV1()) {
260 return new BackupFileIndex();
261 }
262
263 return WPStaging::make(FileHeader::class);
264 }
265 }
266