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

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