PluginProbe
WPIDE – File Manager & Code Editor / 3.4.2
WPIDE – File Manager & Code Editor v3.4.2
3.5.8 3.5.7 2.0.14 2.0.15 2.0.16 2.0.2 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1 2.2 2.3 2.3.1 2.3.2 2.4.0 2.5 2.6 3.0 3.1 3.2 3.3 3.4 All 54 releases
wpide / App / Services / Storage / Filesystem.php

Filesystem.php in WPIDE – File Manager & Code Editor 3.4.2, at App/Services/Storage/Filesystem.php

469 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPIDE\App\Services\Storage;
4
5 use Exception;
6 use WPIDE\App\Services\Service;
7 use League\Flysystem\Filesystem as Flysystem;
8
9 class Filesystem implements Service
10 {
11 protected $root;
12 protected $separator;
13 protected $excluded_dirs;
14 protected $excluded_files;
15
16 protected $storage;
17
18 protected $path_prefix;
19
20 public function init(array $config = [])
21 {
22 $this->separator = $config['separator'] ?? '/';
23 $this->root = $config['root'] ?? $this->separator;
24 $this->excluded_dirs = $config['excluded_dirs'] ?? [];
25 $this->excluded_files = $config['excluded_files'] ?? [];
26
27 $this->path_prefix = $this->separator;
28
29 $adapter = $config['adapter'];
30
31 $config = $config['config'] ?? [];
32
33 $this->storage = new Flysystem($adapter(), $config);
34
35 }
36
37 public function createDir(string $path, string $name)
38 {
39 $destination = $this->joinPaths($this->applyPathPrefix($path), $name);
40
41 while (! empty($this->listContents($destination, true))) {
42 $destination = $this->upcountName($destination);
43 }
44
45 return $this->storage->createDir($destination);
46 }
47
48 public function createFile(string $path, string $name)
49 {
50 $destination = $this->joinPaths($this->applyPathPrefix($path), $name);
51
52 while ($this->storage->has($destination)) {
53 $destination = $this->upcountName($destination);
54 }
55
56 $this->storage->put($destination, '');
57 }
58
59 public function fileExists(string $path)
60 {
61 $path = $this->applyPathPrefix($path);
62
63 return $this->storage->has($path);
64 }
65
66 public function isDir(string $path): bool
67 {
68 $path = $this->applyPathPrefix($path);
69
70 try {
71 return $this->storage->getSize($path) === false;
72 }catch (\Exception $error) {
73 return false;
74 }
75 }
76
77 public function copyFile(string $source, string $destination)
78 {
79 $source = $this->applyPathPrefix($source);
80 $destination = $this->joinPaths($this->applyPathPrefix($destination), $this->getBaseName($source));
81
82 while ($this->storage->has($destination)) {
83 $destination = $this->upcountName($destination);
84 }
85
86 return $this->storage->copy($source, $destination);
87 }
88
89 public function copyDir(string $source, string $destination)
90 {
91 $source = $this->applyPathPrefix($this->addSeparators($source));
92 $destination = $this->applyPathPrefix($this->addSeparators($destination));
93 $source_dir = $this->getBaseName($source);
94 $real_destination = $this->joinPaths($destination, $source_dir);
95
96 while (! empty($this->listContents($real_destination, true))) {
97 $real_destination = $this->upcountName($real_destination);
98 }
99
100 $contents = $this->listContents($source, true);
101
102 if (empty($contents)) {
103 $this->storage->createDir($real_destination);
104 }
105
106 foreach ($contents as $file) {
107 $source_path = $this->separator.ltrim($file['path'], $this->separator);
108 $path = substr($source_path, strlen($source), strlen($source_path));
109
110 if ($file['type'] == 'dir') {
111
112 $this->storage->createDir($this->joinPaths($real_destination, $path));
113
114 continue;
115 }
116
117 if ($file['type'] == 'file') {
118 $this->storage->copy($file['path'], $this->joinPaths($real_destination, $path));
119 }
120 }
121 }
122
123 public function deleteDir(string $path)
124 {
125 return $this->storage->deleteDir($this->applyPathPrefix($path));
126 }
127
128 public function deleteFile(string $path)
129 {
130 return $this->storage->delete($this->applyPathPrefix($path));
131 }
132
133 public function readStream(string $path): array
134 {
135 if ($this->isDir($path)) {
136 throw new Exception('Cannot stream directory');
137 }
138
139 $path = $this->applyPathPrefix($path);
140
141 return [
142 'filename' => $this->getBaseName($path),
143 'stream' => $this->storage->readStream($path),
144 'filesize' => $this->storage->getSize($path),
145 ];
146 }
147
148 public function read(string $path): array
149 {
150 if ($this->isDir($path)) {
151 throw new Exception('Cannot read directory');
152 }
153
154 $path = $this->applyPathPrefix($path);
155
156 return [
157 'filename' => $this->getBaseName($path),
158 'contents' => $this->storage->read($path),
159 'filesize' => $this->storage->getSize($path),
160 ];
161 }
162
163 public function move(string $from, string $to): bool
164 {
165 $from = $this->applyPathPrefix($from);
166 $to = $this->applyPathPrefix($to);
167
168 while ($this->storage->has($to)) {
169 $to = $this->upcountName($to);
170 }
171
172 return $this->storage->rename($from, $to);
173 }
174
175 public function rename(string $destination, string $from, string $to): bool
176 {
177 $from = $this->joinPaths($this->applyPathPrefix($destination), $from);
178 $to = $this->joinPaths($this->applyPathPrefix($destination), $to);
179
180 while ($this->storage->has($to)) {
181 $to = $this->upcountName($to);
182 }
183
184 return $this->storage->rename($from, $to);
185 }
186
187 public function store(string $path, string $name, $content, bool $overwrite = false): bool
188 {
189 $destination = $this->joinPaths($this->applyPathPrefix($path), $name);
190
191 while ($this->storage->has($destination)) {
192 if ($overwrite) {
193 $this->storage->delete($destination);
194 } else {
195 $destination = $this->upcountName($destination);
196 }
197 }
198
199 return $this->storage->put($destination, $content);
200 }
201
202 public function storeStream(string $path, string $name, $resource, bool $overwrite = false): bool
203 {
204 $destination = $this->joinPaths($this->applyPathPrefix($path), $name);
205
206 while ($this->storage->has($destination)) {
207 if ($overwrite) {
208 $this->storage->delete($destination);
209 } else {
210 $destination = $this->upcountName($destination);
211 }
212 }
213
214 return $this->storage->putStream($destination, $resource);
215 }
216
217 public function storeStreamFromContent(string $path, string $name, $content, bool $overwrite = false): bool
218 {
219 $stream = tmpfile();
220 fwrite($stream, $content);
221 rewind($stream);
222
223 $res = $this->storeStream($path, $name, $stream, $overwrite);
224
225 if (is_resource($stream)) {
226 fclose($stream);
227 }
228
229 return $res;
230 }
231
232 public function setPathPrefix(string $path_prefix)
233 {
234 $this->path_prefix = $this->addSeparators($path_prefix);
235 }
236
237 public function getSeparator()
238 {
239 return $this->separator;
240 }
241
242 public function getPathPrefix(): string
243 {
244 return $this->path_prefix;
245 }
246
247 public function listContents($path, $recursive = false): array
248 {
249
250 $results = $this->storage->listContents($path, $recursive);
251
252 if(!empty($this->excluded_dirs) || !empty($this->excluded_files)) {
253
254 $results = array_filter($results, function ($item) {
255
256 $item_path = realpath($this->root . $this->stripPathPrefix($item['path']));
257
258 if ($item['type'] === 'dir' && !empty($this->excluded_dirs)) {
259
260 $item_path .= $this->separator;
261
262 return !$this->isPathExcluded($item_path, $this->excluded_dirs);
263
264 }else if ($item['type'] === 'file' && !empty($this->excluded_files)) {
265
266 return !$this->isPathExcluded($item_path, $this->excluded_files);
267 }
268
269 });
270 }
271
272 return $results;
273 }
274
275 /**
276 * @throws Exception
277 */
278 public function getDirectoryCollection(string $path, bool $recursive = false): DirectoryCollection
279 {
280 $collection = new DirectoryCollection($path);
281
282 foreach ($this->listContents($this->applyPathPrefix($path), $recursive) as $entry) {
283
284 // By default, only 'path' and 'type' is present
285
286 $is_dir = $entry['type'] === 'dir';
287 $name = $this->getBaseName($entry['path']);
288 $user_path = $this->stripPathPrefix($entry['path']);
289 $size = isset($entry['size']) ? $entry['size'] : 0;
290 $timestamp = isset($entry['timestamp']) ? $entry['timestamp'] : 0;
291
292 $mime = $this->getMimetype($entry['path']);
293 $dir_info = $is_dir ? $this->dirInfo($entry['path']) : [];
294
295 $collection->addFile($entry['type'], $dir_info, $user_path, $path, $mime, $name, $size, $timestamp);
296 }
297
298 if (! $recursive && $this->addSeparators($path) !== $this->separator) {
299 $collection->addFile('back', [], $this->getParent($path), $path, '', '..', 0, 0);
300 }
301
302 return $collection;
303 }
304
305 protected function isPathExcluded($path, $excluded): bool
306 {
307 $path = wp_normalize_path($path);
308
309 foreach ($excluded as $exclude) {
310
311 $force_include = str_contains($exclude, "!");
312 $exclude = str_replace("!", "", $exclude);
313
314 $pattern = '/'.str_replace("\*", "[^\/]+", preg_quote($exclude, '/')).'/';
315 if(str_contains($path, $exclude) || preg_match($pattern, $path, $matches) === 1) {
316
317 if($force_include) {
318 return false;
319 }
320
321 return true;
322 }
323 }
324
325 return false;
326 }
327
328 public function dirInfo($path): array
329 {
330
331 $path = $this->applyPathPrefix($path);
332 $content = $this->listContents($path);
333
334 $has_dirs = false;
335 $has_files = false;
336
337 foreach($content as $entry) {
338
339 if(!$has_dirs && $entry['type'] === 'dir') {
340 $has_dirs = true;
341 }
342 if(!$has_files && $entry['type'] === 'files') {
343 $has_files = true;
344 }
345
346 if($has_dirs && $has_files) {
347 break;
348 }
349 }
350
351 return [
352 'is_empty' => !count($content),
353 'has_dirs' => $has_dirs,
354 'has_files' => $has_files
355 ];
356 }
357
358 /**
359 * @throws Exception
360 */
361 public function getDirSize($path): int
362 {
363
364 $size = 0;
365
366 foreach ($this->getDirectoryCollection($path)->all() as $entry) {
367
368 if($entry['type'] === 'back') {
369 continue;
370 }
371
372 $size += $entry['type'] === 'file' && isset($entry['size']) ? $entry['size'] : $this->getDirSize($entry['path']);
373 }
374
375 return $size;
376 }
377
378 protected function upcountCallback($matches): string
379 {
380 $index = isset($matches[1]) ? intval($matches[1]) + 1 : 1;
381 $ext = isset($matches[2]) ? $matches[2] : '';
382
383 return ' ('.$index.')'.$ext;
384 }
385
386 public function upcountName($name)
387 {
388 return preg_replace_callback(
389 '/(?:(?: \(([\d]+)\))?(\.[^.]+))?$/',
390 [$this, 'upcountCallback'],
391 $name,
392 1
393 );
394 }
395
396 private function applyPathPrefix(string $path): string
397 {
398 if ($path == '..'
399 || strpos($path, '..'.$this->separator) !== false
400 || strpos($path, $this->separator.'..') !== false
401 ) {
402 $path = $this->separator;
403 }
404 return $this->joinPaths($this->getPathPrefix(), $path);
405 }
406
407 private function stripPathPrefix(string $path): string
408 {
409 $path = $this->separator.ltrim($path, $this->separator);
410
411 if (substr($path, 0, strlen($this->getPathPrefix())) == $this->getPathPrefix()) {
412 $path = $this->separator.substr($path, strlen($this->getPathPrefix()));
413 }
414
415 return $path;
416 }
417
418 private function addSeparators(string $dir): string
419 {
420 if (! $dir || $dir == $this->separator || ! trim($dir, $this->separator)) {
421 return $this->separator;
422 }
423
424 return $this->separator.trim($dir, $this->separator).$this->separator;
425 }
426
427 private function joinPaths(string $path1, string $path2): string
428 {
429 if (! $path2 || ! trim($path2, $this->separator)) {
430 return $this->addSeparators($path1);
431 }
432
433 return $this->addSeparators($path1).ltrim($path2, $this->separator);
434 }
435
436 private function getParent(string $dir): string
437 {
438 if (! $dir || $dir == $this->separator || ! trim($dir, $this->separator)) {
439 return $this->separator;
440 }
441
442 $tmp = explode($this->separator, trim($dir, $this->separator));
443 array_pop($tmp);
444
445 return $this->separator.trim(implode($this->separator, $tmp), $this->separator);
446 }
447
448 private function getBaseName(string $path): string
449 {
450 if (! $path || $path == $this->separator || ! trim($path, $this->separator)) {
451 return $this->separator;
452 }
453
454 $tmp = explode($this->separator, trim($path, $this->separator));
455
456 return (string) array_pop($tmp);
457 }
458
459 private function getMimetype(string $path): string
460 {
461 if (! $path || $path == $this->separator || ! trim($path, $this->separator)) {
462 return $this->separator;
463 }
464
465 $mime = $this->storage->getMimetype($path);
466 return !empty($mime) && is_string($mime) ? explode('/', $mime)[0] : false;
467 }
468 }
469