PluginProbe
WPIDE – File Manager & Code Editor / 3.4.1
WPIDE – File Manager & Code Editor v3.4.1
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.1, at App/Services/Storage/Filesystem.php

468 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
308 foreach ($excluded as $exclude) {
309
310 $force_include = str_contains($exclude, "!");
311 $exclude = str_replace("!", "", $exclude);
312
313 $pattern = '/'.str_replace("\*", "[^\/]+", preg_quote($exclude, '/')).'/';
314 if(str_contains($path, $exclude) || preg_match($pattern, $path, $matches) === 1) {
315
316 if($force_include) {
317 return false;
318 }
319
320 return true;
321 }
322 }
323
324 return false;
325 }
326
327 public function dirInfo($path): array
328 {
329
330 $path = $this->applyPathPrefix($path);
331 $content = $this->listContents($path);
332
333 $has_dirs = false;
334 $has_files = false;
335
336 foreach($content as $entry) {
337
338 if(!$has_dirs && $entry['type'] === 'dir') {
339 $has_dirs = true;
340 }
341 if(!$has_files && $entry['type'] === 'files') {
342 $has_files = true;
343 }
344
345 if($has_dirs && $has_files) {
346 break;
347 }
348 }
349
350 return [
351 'is_empty' => !count($content),
352 'has_dirs' => $has_dirs,
353 'has_files' => $has_files
354 ];
355 }
356
357 /**
358 * @throws Exception
359 */
360 public function getDirSize($path): int
361 {
362
363 $size = 0;
364
365 foreach ($this->getDirectoryCollection($path)->all() as $entry) {
366
367 if($entry['type'] === 'back') {
368 continue;
369 }
370
371 $size += $entry['type'] === 'file' && isset($entry['size']) ? $entry['size'] : $this->getDirSize($entry['path']);
372 }
373
374 return $size;
375 }
376
377 protected function upcountCallback($matches): string
378 {
379 $index = isset($matches[1]) ? intval($matches[1]) + 1 : 1;
380 $ext = isset($matches[2]) ? $matches[2] : '';
381
382 return ' ('.$index.')'.$ext;
383 }
384
385 public function upcountName($name)
386 {
387 return preg_replace_callback(
388 '/(?:(?: \(([\d]+)\))?(\.[^.]+))?$/',
389 [$this, 'upcountCallback'],
390 $name,
391 1
392 );
393 }
394
395 private function applyPathPrefix(string $path): string
396 {
397 if ($path == '..'
398 || strpos($path, '..'.$this->separator) !== false
399 || strpos($path, $this->separator.'..') !== false
400 ) {
401 $path = $this->separator;
402 }
403 return $this->joinPaths($this->getPathPrefix(), $path);
404 }
405
406 private function stripPathPrefix(string $path): string
407 {
408 $path = $this->separator.ltrim($path, $this->separator);
409
410 if (substr($path, 0, strlen($this->getPathPrefix())) == $this->getPathPrefix()) {
411 $path = $this->separator.substr($path, strlen($this->getPathPrefix()));
412 }
413
414 return $path;
415 }
416
417 private function addSeparators(string $dir): string
418 {
419 if (! $dir || $dir == $this->separator || ! trim($dir, $this->separator)) {
420 return $this->separator;
421 }
422
423 return $this->separator.trim($dir, $this->separator).$this->separator;
424 }
425
426 private function joinPaths(string $path1, string $path2): string
427 {
428 if (! $path2 || ! trim($path2, $this->separator)) {
429 return $this->addSeparators($path1);
430 }
431
432 return $this->addSeparators($path1).ltrim($path2, $this->separator);
433 }
434
435 private function getParent(string $dir): string
436 {
437 if (! $dir || $dir == $this->separator || ! trim($dir, $this->separator)) {
438 return $this->separator;
439 }
440
441 $tmp = explode($this->separator, trim($dir, $this->separator));
442 array_pop($tmp);
443
444 return $this->separator.trim(implode($this->separator, $tmp), $this->separator);
445 }
446
447 private function getBaseName(string $path): string
448 {
449 if (! $path || $path == $this->separator || ! trim($path, $this->separator)) {
450 return $this->separator;
451 }
452
453 $tmp = explode($this->separator, trim($path, $this->separator));
454
455 return (string) array_pop($tmp);
456 }
457
458 private function getMimetype(string $path): string
459 {
460 if (! $path || $path == $this->separator || ! trim($path, $this->separator)) {
461 return $this->separator;
462 }
463
464 $mime = $this->storage->getMimetype($path);
465 return !empty($mime) && is_string($mime) ? explode('/', $mime)[0] : false;
466 }
467 }
468