PluginProbe
WPIDE – File Manager & Code Editor / 3.5.8
WPIDE – File Manager & Code Editor v3.5.8
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 / Adapters / WPFileSystem.php

WPFileSystem.php in WPIDE – File Manager & Code Editor 3.5.8, at App/Services/Storage/Adapters/WPFileSystem.php

577 lines 14.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace WPIDE\App\Services\Storage\Adapters;
3
4 use DirectoryIterator;
5 use FilesystemIterator;
6 use League\Flysystem\Exception;
7 use League\Flysystem\NotSupportedException;
8 use League\Flysystem\UnreadableFileException;
9 use League\Flysystem\Util;
10 use League\Flysystem\Adapter\AbstractAdapter;
11 use League\MimeTypeDetection\ExtensionMimeTypeDetector;
12 use League\MimeTypeDetection\FinfoMimeTypeDetector;
13 use LogicException;
14 use RecursiveDirectoryIterator;
15 use RecursiveIteratorIterator;
16 use SplFileInfo;
17 use WP_Filesystem_Base;
18
19 class WPFileSystem extends AbstractAdapter
20 {
21 /**
22 * @var int
23 */
24 const SKIP_LINKS = 0001;
25
26 /**
27 * @var int
28 */
29 const DISALLOW_LINKS = 0002;
30
31 /**
32 * @var array
33 */
34 protected static $permissions = [
35 'file' => [
36 'public' => 0644,
37 'private' => 0600,
38 ],
39 'dir' => [
40 'public' => 0755,
41 'private' => 0700,
42 ],
43 ];
44
45 /**
46 * @var string
47 */
48 protected $pathSeparator = DIRECTORY_SEPARATOR;
49
50 /**
51 * @var array
52 */
53 protected $permissionMap;
54
55 /**
56 * @var int
57 */
58 private $linkHandling;
59
60 /**
61 * @var WP_Filesystem_Base
62 */
63 protected $fs;
64
65 /**
66 * Constructor.
67 *
68 * @param string $root
69 * @param WP_Filesystem_Base $wp_fs
70 * @param int $linkHandling
71 * @param array $permissions
72 *
73 * @throws LogicException
74 * @throws Exception
75 */
76 public function __construct($root, $wp_fs, $linkHandling = self::DISALLOW_LINKS, array $permissions = [])
77 {
78
79 $this->fs = $wp_fs;
80
81 $root = is_link($root) ? realpath($root) : $root;
82 $this->permissionMap = array_replace_recursive(static::$permissions, $permissions);
83 $this->ensureDirectory($root);
84
85 if ( ! $this->fs->is_dir($root) || ! $this->fs->is_readable($root)) {
86 throw new LogicException('The root path ' . $root . ' is not readable.');
87 }
88
89 $this->setPathPrefix($root);
90 $this->linkHandling = $linkHandling;
91 }
92
93 /**
94 * Attempts to use the correct path for the FS method being used
95 *
96 * @param string $abs_path
97 *
98 * @return string
99 */
100 protected function getRelativePath( $abs_path ): string
101 {
102 return str_replace( ABSPATH, $this->fs->abspath(), $abs_path );
103 }
104
105 /**
106 * Ensure the root directory exists.
107 *
108 * @param string $root root directory path
109 *
110 * @return void
111 *
112 * @throws Exception in case the root directory can not be created
113 */
114 protected function ensureDirectory($root)
115 {
116 $root = $this->getRelativePath( $root );
117
118 if ( ! $this->fs->is_dir($root)) {
119 $umask = umask(0);
120
121 if ( ! wp_mkdir_p($root) ) {
122 $mkdirError = error_get_last();
123 }
124
125 umask($umask);
126 clearstatcache(false, $root);
127
128 if ( ! $this->fs->is_dir($root)) {
129 $errorMessage = isset($mkdirError['message']) ? $mkdirError['message'] : '';
130 throw new Exception(sprintf('Impossible to create the root directory "%s". %s', $root, $errorMessage));
131 }
132 }
133 }
134
135 /**
136 * @inheritdoc
137 */
138 public function has($path)
139 {
140 $path = $this->getRelativePath( $path );
141 $location = $this->applyPathPrefix($path);
142
143 return $this->fs->exists($location);
144 }
145
146 /**
147 * @inheritdoc
148 * @throws Exception
149 */
150 public function write($path, $contents, $config)
151 {
152 $path = $this->getRelativePath( $path );
153 $location = $this->applyPathPrefix($path);
154 $this->ensureDirectory(dirname($location));
155
156 if (($size = $this->fs->put_contents($location, $contents)) === false) {
157 return false;
158 }
159
160 $type = 'file';
161 $result = compact('contents', 'type', 'size', 'path');
162
163 if ($visibility = $config->get('visibility')) {
164 $result['visibility'] = $visibility;
165 $this->setVisibility($path, $visibility);
166 }
167
168 return $result;
169 }
170
171 /**
172 * @inheritdoc
173 * @throws Exception
174 */
175 public function writeStream($path, $resource, $config)
176 {
177 $path = $this->getRelativePath( $path );
178 $location = $this->applyPathPrefix($path);
179 $this->ensureDirectory(dirname($location));
180 $stream = @fopen($location, 'w+b');
181
182 if ( ! $stream || stream_copy_to_stream($resource, $stream) === false || ! fclose($stream)) {
183 return false;
184 }
185
186 $type = 'file';
187 $result = compact('type', 'path');
188
189 if ($visibility = $config->get('visibility')) {
190 $this->setVisibility($path, $visibility);
191 $result['visibility'] = $visibility;
192 }
193
194 return $result;
195 }
196
197 /**
198 * @inheritdoc
199 */
200 public function readStream($path)
201 {
202 $path = $this->getRelativePath( $path );
203 $location = $this->applyPathPrefix($path);
204 $stream = @fopen($location, 'rb');
205
206 return ['type' => 'file', 'path' => $path, 'stream' => $stream];
207 }
208
209 /**
210 * @inheritdoc
211 */
212 public function updateStream($path, $resource, $config)
213 {
214 $path = $this->getRelativePath( $path );
215 return $this->writeStream($path, $resource, $config);
216 }
217
218 /**
219 * @inheritdoc
220 */
221 public function update($path, $contents, $config)
222 {
223 $path = $this->getRelativePath( $path );
224 $location = $this->applyPathPrefix($path);
225
226 $size = $this->fs->put_contents($location, $contents);
227
228 if ($size === false) {
229 return false;
230 }
231
232 $type = 'file';
233
234 $result = compact('type', 'path', 'size', 'contents');
235
236 if ($visibility = $config->get('visibility')) {
237 $this->setVisibility($path, $visibility);
238 $result['visibility'] = $visibility;
239 }
240
241 return $result;
242 }
243
244 /**
245 * @inheritdoc
246 */
247 public function read($path)
248 {
249 $path = $this->getRelativePath( $path );
250 $location = $this->applyPathPrefix($path);
251 $contents = $this->fs->get_contents($location);
252
253 if ($contents === false) {
254 return false;
255 }
256
257 return ['type' => 'file', 'path' => $path, 'contents' => $contents];
258 }
259
260 /**
261 * @inheritdoc
262 * @throws Exception
263 */
264 public function rename($path, $newpath)
265 {
266 $path = $this->getRelativePath( $path );
267 $location = $this->applyPathPrefix($path);
268 $destination = $this->applyPathPrefix($newpath);
269 $parentDirectory = $this->applyPathPrefix(Util::dirname($newpath));
270 $this->ensureDirectory($parentDirectory);
271
272 return $this->fs->move($location, $destination);
273 }
274
275 /**
276 * @inheritdoc
277 * @throws Exception
278 */
279 public function copy($path, $newpath): bool
280 {
281 $path = $this->getRelativePath( $path );
282 $location = $this->applyPathPrefix($path);
283 $destination = $this->applyPathPrefix($newpath);
284 $this->ensureDirectory(dirname($destination));
285
286 return $this->fs->copy($location, $destination);
287 }
288
289 /**
290 * @inheritdoc
291 */
292 public function delete($path): bool
293 {
294 $path = $this->getRelativePath( $path );
295 $location = $this->applyPathPrefix($path);
296
297 return $this->fs->delete($location);
298 }
299
300 /**
301 * @inheritdoc
302 */
303 public function listContents($directory = '', $recursive = false): array
304 {
305
306 $result = [];
307 $directory = $this->getRelativePath( $directory );
308 $location = $this->applyPathPrefix($directory);
309
310 if ( ! $this->fs->is_dir($location)) {
311 return [];
312 }
313
314 $iterator = $recursive ? $this->getRecursiveDirectoryIterator($location) : $this->getDirectoryIterator($location);
315
316 foreach ($iterator as $file) {
317 $path = $this->getFilePath($file);
318
319 if (preg_match('#(^|/|\\\\)\.{1,2}$#', $path)) {
320 continue;
321 }
322
323 $result[] = $this->normalizeFileInfo($file);
324 }
325
326 unset($iterator);
327
328 return array_filter($result);
329 }
330
331 /**
332 * @inheritdoc
333 */
334 public function getMetadata($path)
335 {
336 $path = $this->getRelativePath( $path );
337 $location = $this->applyPathPrefix($path);
338 clearstatcache(false, $location);
339 $info = new SplFileInfo($location);
340
341 return $this->normalizeFileInfo($info);
342 }
343
344 /**
345 * @inheritdoc
346 */
347 public function getSize($path)
348 {
349 $path = $this->getRelativePath( $path );
350 return $this->getMetadata($path);
351 }
352
353 /**
354 * @inheritdoc
355 */
356 public function getMimetype($path)
357 {
358 $path = $this->getRelativePath( $path );
359 $location = $this->applyPathPrefix($path);
360
361 if(extension_loaded('fileinfo')) {
362 $mimeDetector = new FinfoMimeTypeDetector();
363 }else{
364 $mimeDetector = new ExtensionMimeTypeDetector();
365 }
366 $mimetype = $mimeDetector->detectMimeTypeFromPath($location);
367
368 return ['path' => $path, 'type' => 'file', 'mimetype' => $mimetype];
369 }
370
371 /**
372 * @inheritdoc
373 */
374 public function getTimestamp($path)
375 {
376 $path = $this->getRelativePath( $path );
377 return $this->getMetadata($path);
378 }
379
380 /**
381 * @inheritdoc
382 */
383 public function getVisibility($path)
384 {
385 $path = $this->getRelativePath( $path );
386 $location = $this->applyPathPrefix($path);
387 clearstatcache(false, $location);
388 $permissions = octdec(substr(sprintf('%o', fileperms($location)), -4));
389 $type = $this->fs->is_dir($location) ? 'dir' : 'file';
390
391 foreach ($this->permissionMap[$type] as $visibility => $visibilityPermissions) {
392 if ($visibilityPermissions == $permissions) {
393 return compact('path', 'visibility');
394 }
395 }
396
397 $visibility = substr(sprintf('%o', fileperms($location)), -4);
398
399 return compact('path', 'visibility');
400 }
401
402 /**
403 * @inheritdoc
404 */
405 public function setVisibility($path, $visibility)
406 {
407 $path = $this->getRelativePath( $path );
408 $location = $this->applyPathPrefix($path);
409 $type = $this->fs->is_dir($location) ? 'dir' : 'file';
410 $success = $this->fs->chmod($location, $this->permissionMap[$type][$visibility]);
411
412 if ($success === false) {
413 return false;
414 }
415
416 return compact('path', 'visibility');
417 }
418
419 /**
420 * @inheritdoc
421 */
422 public function createDir($dirname, $config)
423 {
424
425 $dirname = $this->getRelativePath( $dirname );
426 $location = $this->applyPathPrefix($dirname);
427 $umask = umask(0);
428 $return = ['path' => $dirname, 'type' => 'dir'];
429
430 if ( ! is_dir($location)) {
431 if (!wp_mkdir_p($location)) {
432 error_log('cannot create dir '.$location);
433 $return = false;
434 }
435 }
436
437 umask($umask);
438
439 return $return;
440 }
441
442 /**
443 * @inheritdoc
444 */
445 public function deleteDir($dirname): bool
446 {
447 $dirname = $this->getRelativePath( $dirname );
448 $location = $this->applyPathPrefix($dirname);
449
450 if ( ! $this->fs->is_dir($location)) {
451 return false;
452 }
453
454 $contents = $this->getRecursiveDirectoryIterator($location, RecursiveIteratorIterator::CHILD_FIRST);
455
456 /** @var SplFileInfo $file */
457 foreach ($contents as $file) {
458 $this->guardAgainstUnreadableFileInfo($file);
459 $this->deleteFileInfoObject($file);
460 }
461
462 unset($contents);
463
464 return $this->fs->rmdir($location);
465 }
466
467 /**
468 * @param SplFileInfo $file
469 */
470 protected function deleteFileInfoObject(SplFileInfo $file)
471 {
472 switch ($file->getType()) {
473 case 'dir':
474 $this->fs->rmdir($file->getRealPath());
475 break;
476 case 'link':
477 $this->fs->delete($file->getPathname());
478 break;
479 default:
480 $this->fs->delete($file->getRealPath());
481 }
482 }
483
484 /**
485 * Normalize the file info.
486 *
487 * @param SplFileInfo $file
488 *
489 * @return array|void
490 *
491 * @throws NotSupportedException
492 */
493 protected function normalizeFileInfo(SplFileInfo $file)
494 {
495 if ( ! $file->isLink()) {
496 return $this->mapFileInfo($file);
497 }
498
499 if ($this->linkHandling & self::DISALLOW_LINKS) {
500 throw NotSupportedException::forLink($file);
501 }
502 }
503
504 /**
505 * Get the normalized path from a SplFileInfo object.
506 *
507 * @param SplFileInfo $file
508 *
509 * @return string
510 */
511 protected function getFilePath(SplFileInfo $file): string
512 {
513 $location = $file->getPathname();
514 $path = $this->removePathPrefix($location);
515
516 return trim(str_replace('\\', '/', $path), '/');
517 }
518
519 /**
520 * @param string $path
521 * @param int $mode
522 *
523 * @return RecursiveIteratorIterator
524 */
525 protected function getRecursiveDirectoryIterator($path, $mode = RecursiveIteratorIterator::SELF_FIRST)
526 {
527 return new RecursiveIteratorIterator(
528 new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
529 $mode
530 );
531 }
532
533 /**
534 * @param string $path
535 *
536 * @return DirectoryIterator
537 */
538 protected function getDirectoryIterator($path): DirectoryIterator
539 {
540 $path = $this->getRelativePath( $path );
541 return new DirectoryIterator($path);
542 }
543
544 /**
545 * @param SplFileInfo $file
546 *
547 * @return array
548 */
549 protected function mapFileInfo(SplFileInfo $file): array
550 {
551 $normalized = [
552 'type' => $file->getType(),
553 'path' => $this->getFilePath($file),
554 ];
555
556 $normalized['timestamp'] = $file->getMTime();
557
558 if ($normalized['type'] === 'file') {
559 $normalized['size'] = $file->getSize();
560 }
561
562 return $normalized;
563 }
564
565 /**
566 * @param SplFileInfo $file
567 *
568 * @throws UnreadableFileException
569 */
570 protected function guardAgainstUnreadableFileInfo(SplFileInfo $file)
571 {
572 if ( ! $file->isReadable()) {
573 throw UnreadableFileException::forFileInfo($file);
574 }
575 }
576 }
577