v2.2.0: includes/Tools/FileManagerTools.php

This commit is contained in:
2026-05-29 12:44:09 +00:00
parent 28b9e0c28c
commit 3d1464f8b4
+104
View File
@@ -0,0 +1,104 @@
<?php
namespace AskTamMCP\Tools;
defined('ABSPATH') || exit;
/**
* FileManagerTools — sandboxed to /wp-content/uploads/
*/
class FileManagerTools {
private static function safePath(string $rel): string {
$uploads = wp_upload_dir();
$base_dir = rtrim($uploads['basedir'], '/');
// Strip leading slashes and normalize
$rel = ltrim(str_replace(['..', '\\'], '', $rel), '/');
$full = $base_dir . '/' . $rel;
// Double-check it's still inside uploads
if (strpos(realpath($full) ?: $full, $base_dir) !== 0) {
throw new \RuntimeException("Path outside uploads directory.");
}
return $full;
}
public static function listDir(array $args): array {
$rel = $args['path'] ?? '';
$path = self::safePath($rel);
if (!is_dir($path)) throw new \RuntimeException("Directory not found: $rel");
$items = [];
foreach (scandir($path) as $entry) {
if ($entry === '.' || $entry === '..') continue;
$full = $path . '/' . $entry;
$items[] = [
'name' => $entry,
'type' => is_dir($full) ? 'dir' : 'file',
'size' => is_file($full) ? filesize($full) : null,
'modified' => date('Y-m-d H:i:s', filemtime($full)),
'url' => is_file($full) ? wp_upload_dir()['baseurl'] . '/' . ltrim($rel . '/' . $entry, '/') : null,
];
}
usort($items, fn($a,$b) => ($a['type'] === $b['type']) ? strcmp($a['name'],$b['name']) : ($a['type']==='dir'?-1:1));
return ['path'=>$rel?:'/','items'=>$items,'count'=>count($items)];
}
public static function readFile(array $args): array {
$rel = $args['path'] ?? '';
$path = self::safePath($rel);
if (!is_file($path)) throw new \RuntimeException("File not found: $rel");
$mime = mime_content_type($path) ?: 'application/octet-stream';
$size = filesize($path);
$text_mimes = ['text/','application/json','application/xml','application/javascript'];
$is_text = array_filter($text_mimes, fn($m) => str_starts_with($mime, $m));
return [
'path' => $rel,
'size' => $size,
'mime' => $mime,
'modified' => date('Y-m-d H:i:s', filemtime($path)),
'content' => $is_text ? file_get_contents($path) : base64_encode(file_get_contents($path)),
'encoding' => $is_text ? 'text' : 'base64',
];
}
public static function deleteFile(array $args): array {
$rel = $args['path'] ?? '';
$path = self::safePath($rel);
if (!file_exists($path)) throw new \RuntimeException("File not found: $rel");
if (is_dir($path)) throw new \RuntimeException("Use delete_directory to remove directories.");
if (!unlink($path)) throw new \RuntimeException("Failed to delete: $rel");
return ['path'=>$rel,'deleted'=>true];
}
public static function deleteDirectory(array $args): array {
$rel = $args['path'] ?? '';
if (empty($rel)) throw new \RuntimeException("Cannot delete uploads root.");
$path = self::safePath($rel);
if (!is_dir($path)) throw new \RuntimeException("Directory not found: $rel");
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($files as $f) {
$f->isDir() ? rmdir($f->getRealPath()) : unlink($f->getRealPath());
}
rmdir($path);
return ['path'=>$rel,'deleted'=>true];
}
public static function getUploadsInfo(array $args): array {
$uploads = wp_upload_dir();
$base = $uploads['basedir'];
$size = 0;
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($base)) as $f) {
if ($f->isFile()) $size += $f->getSize();
}
return [
'base_dir' => $base,
'base_url' => $uploads['baseurl'],
'total_size' => $size,
'total_size_human' => size_format($size),
];
}
}