PHP ftp_rawlist() 函数用于获取FTP服务器上指定目录的详细列表,返回包含文件和目录完整信息的原始行数组。
LIST命令的原始响应,包含文件权限、所有者、组、大小、修改时间等详细信息,但不同服务器的格式可能不同。
ftp_rawlist(resource $ftp, string $directory, bool $recursive = false): array|false
| 参数 | 描述 |
|---|---|
ftp |
必需。FTP连接的标识符,由ftp_connect()或ftp_ssl_connect()返回 |
directory |
必需。要列出详细信息的目录路径。如果为空字符串,则列出当前目录 |
recursive |
可选。如果为true,则递归列出目录内容。注意:并非所有服务器都支持递归列表 |
falsels -l格式| 特性 | ftp_nlist() | ftp_rawlist() |
|---|---|---|
| 返回内容 | 仅文件名/目录名数组 | 原始服务器响应行数组,包含完整信息 |
| 信息量 | 简单,仅名称 | 详细,包括权限、所有者、大小、时间等 |
| 处理难度 | 简单,直接使用 | 复杂,需要解析服务器特定的格式 |
| 适用场景 | 只需文件名列表时 | 需要文件详细信息时 |
| 服务器兼容性 | 较好,标准化 | 较差,不同服务器格式不同 |
| 性能 | 较快 | 较慢,传输更多数据 |
| 服务器类型 | 典型格式 | 示例 |
|---|---|---|
| Unix/Linux | -rwxr-xr-x 1 user group 1234 Jan 1 12:00 filename |
-rw-r--r-- 1 ftp ftp 123456 Jan 1 12:00 index.htmldrwxr-xr-x 2 ftp ftp 4096 Jan 1 12:00 images
|
| Windows (IIS) | 01-01-24 12:00PM 123456 filename |
01-01-2024 12:00PM 123456 index.html01-01-2024 12:00PM <DIR> images
|
| Windows (FileZilla) | drwxr-xr-x 1 ftp ftp 0 Jan 1 12:00 directory |
-rwxr-xr-x 1 user group 4096 Jan 1 12:00 file.txt
|
| MS-DOS | 01-01-24 12:00 123456 FILENAME.EXT |
01-01-24 12:00 123456 INDEX.HTM
|
drwxr-xr-x 2 ftp ftp 4096 Jan 1 12:00 images
├──┬── ┬── ┬── ┬── ┬── ┬── ┬── ┬── ┬──
│ │ │ │ │ │ │ │ └── 文件名
│ │ │ │ │ │ │ └── 修改时间
│ │ │ │ │ │ └── 修改月份
│ │ │ │ │ └── 修改日期
│ │ │ │ └── 文件大小(字节)
│ │ │ └── 组名
│ │ └── 所有者
│ └── 链接数
└── 文件类型和权限
第一位:文件类型
-:普通文件d:目录l:符号链接c:字符设备b:块设备p:命名管道s:套接字后续9位:权限(3组)
每组:r(读) w(写) x(执行)
所有者 | 组 | 其他用户
<?php
// 连接FTP服务器
$ftp_server = "ftp.example.com";
$ftp_user = "username";
$ftp_pass = "password";
$ftp_conn = ftp_connect($ftp_server);
if (!$ftp_conn) {
die("无法连接到 $ftp_server");
}
// 登录
if (!ftp_login($ftp_conn, $ftp_user, $ftp_pass)) {
die("登录失败");
}
// 启用被动模式
ftp_pasv($ftp_conn, true);
// 获取当前目录的详细列表
$current_dir = ".";
echo "获取目录 '$current_dir' 的详细列表:\n";
$raw_list = ftp_rawlist($ftp_conn, $current_dir);
if ($raw_list !== false) {
echo "找到 " . count($raw_list) . " 个条目:\n";
echo "========================================\n";
foreach ($raw_list as $index => $line) {
echo "条目 " . ($index + 1) . ":\n";
echo " 原始数据: $line\n";
// 尝试解析Unix格式
if (preg_match('/^([d\-l])([rwx\-]{9})\s+(\d+)\s+(\S+)\s+(\S+)\s+(\d+)\s+(\w{3}\s+\d+\s+[\d:]+)\s+(.+)$/', $line, $matches)) {
echo " 解析结果:\n";
echo " 类型: " . ($matches[1] == 'd' ? '目录' : ($matches[1] == 'l' ? '链接' : '文件')) . "\n";
echo " 权限: {$matches[2]}\n";
echo " 链接数: {$matches[3]}\n";
echo " 所有者: {$matches[4]}\n";
echo " 组: {$matches[5]}\n";
echo " 大小: {$matches[6]} 字节\n";
echo " 修改时间: {$matches[7]}\n";
echo " 名称: {$matches[8]}\n";
} elseif (preg_match('/^(\d{2}-\d{2}-\d{2,4}\s+\d{2}:\d{2}[AP]?M?)\s+(<DIR>|\d+)\s+(.+)$/i', $line, $matches)) {
echo " 解析结果 (Windows格式):\n";
echo " 修改时间: {$matches[1]}\n";
echo " 类型: " . (strtoupper($matches[2]) == '<DIR>' ? '目录' : '文件') . "\n";
echo " 大小: " . (is_numeric($matches[2]) ? "{$matches[2]} 字节" : "目录") . "\n";
echo " 名称: {$matches[3]}\n";
} else {
echo " 无法解析的行格式\n";
}
echo "----------------------------------------\n";
}
} else {
echo "获取目录列表失败\n";
}
// 关闭连接
ftp_close($ftp_conn);
?>
<?php
class FTPListParser {
/**
* 解析FTP原始列表行
* @param string $line 原始行
* @return array 解析后的信息
*/
public static function parseLine($line) {
$result = [
'raw' => $line,
'type' => 'unknown',
'name' => '',
'size' => 0,
'permissions' => '',
'owner' => '',
'group' => '',
'modified' => 0,
'is_dir' => false,
'is_link' => false,
'link_target' => ''
];
// 移除行首尾空格
$line = trim($line);
// 尝试Unix格式解析
if (self::parseUnixFormat($line, $result)) {
return $result;
}
// 尝试Windows/IIS格式解析
if (self::parseWindowsFormat($line, $result)) {
return $result;
}
// 尝试MS-DOS格式解析
if (self::parseMSDOSFormat($line, $result)) {
return $result;
}
// 如果都无法解析,尝试提取文件名(假设是简单列表)
$result['name'] = basename($line);
return $result;
}
/**
* 解析Unix格式: drwxr-xr-x 1 user group 1234 Jan 1 12:00 filename
*/
private static function parseUnixFormat($line, &$result) {
// 正则匹配Unix格式
$pattern = '/^([d\-lcbps])([rwx\-]{9})\s+(\d+)\s+(\S+)\s+(\S+)\s+(\d+)\s+(\w{3}\s+\d{1,2}\s+[\d:]+|\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2})\s+(.+)$/';
if (preg_match($pattern, $line, $matches)) {
$result['type'] = self::getFileTypeFromChar($matches[1]);
$result['permissions'] = $matches[2];
$result['owner'] = $matches[4];
$result['group'] = $matches[5];
$result['size'] = intval($matches[6]);
$result['modified'] = self::parseUnixDate($matches[7]);
// 处理文件名(可能包含空格)
$name = $matches[8];
// 检查是否是符号链接
if ($matches[1] == 'l' && strpos($name, ' -> ') !== false) {
list($result['name'], $result['link_target']) = explode(' -> ', $name, 2);
$result['is_link'] = true;
} else {
$result['name'] = $name;
$result['is_link'] = false;
}
$result['is_dir'] = ($matches[1] == 'd');
return true;
}
return false;
}
/**
* 解析Windows/IIS格式: 01-01-24 12:00PM <DIR> filename
*/
private static function parseWindowsFormat($line, &$result) {
// 正则匹配Windows格式
$pattern = '/^(\d{1,2}-\d{1,2}-\d{2,4}\s+\d{1,2}:\d{2}(?:[AP]M)?)\s+(<DIR>|\d+)\s+(.+)$/i';
if (preg_match($pattern, $line, $matches)) {
$result['modified'] = self::parseWindowsDate($matches[1]);
if (strtoupper($matches[2]) == '<DIR>') {
$result['type'] = 'dir';
$result['is_dir'] = true;
$result['size'] = 0;
} else {
$result['type'] = 'file';
$result['is_dir'] = false;
$result['size'] = intval($matches[2]);
}
$result['name'] = trim($matches[3]);
return true;
}
return false;
}
/**
* 解析MS-DOS格式: 01-01-24 12:00 123456 FILENAME.EXT
*/
private static function parseMSDOSFormat($line, &$result) {
// 正则匹配MS-DOS格式
$pattern = '/^(\d{1,2}-\d{1,2}-\d{2,4}\s+\d{1,2}:\d{2}(?:[AP]M)?)\s+(\d+)\s+(\S.+)$/i';
if (preg_match($pattern, $line, $matches)) {
$result['modified'] = self::parseWindowsDate($matches[1]);
$result['type'] = 'file';
$result['is_dir'] = false;
$result['size'] = intval($matches[2]);
$result['name'] = trim($matches[3]);
return true;
}
return false;
}
/**
* 根据字符获取文件类型
*/
private static function getFileTypeFromChar($char) {
$types = [
'-' => 'file',
'd' => 'dir',
'l' => 'link',
'c' => 'char',
'b' => 'block',
'p' => 'pipe',
's' => 'socket'
];
return isset($types[$char]) ? $types[$char] : 'unknown';
}
/**
* 解析Unix日期格式
*/
private static function parseUnixDate($dateStr) {
$dateStr = trim($dateStr);
// 尝试解析 "Jan 1 12:00" 格式(当年)
if (preg_match('/^(\w{3})\s+(\d{1,2})\s+(\d{1,2}):(\d{2})$/', $dateStr, $matches)) {
$year = date('Y');
return strtotime("{$matches[1]} {$matches[2]} {$year} {$matches[3]}:{$matches[4]}");
}
// 尝试解析 "Jan 1 2024" 格式
if (preg_match('/^(\w{3})\s+(\d{1,2})\s+(\d{4})$/', $dateStr, $matches)) {
return strtotime("{$matches[1]} {$matches[2]} {$matches[3]}");
}
// 尝试解析 "2024-01-01 12:00" 格式
if (preg_match('/^(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2})$/', $dateStr, $matches)) {
return mktime($matches[4], $matches[5], 0, $matches[2], $matches[3], $matches[1]);
}
return strtotime($dateStr) ?: 0;
}
/**
* 解析Windows日期格式
*/
private static function parseWindowsDate($dateStr) {
$dateStr = trim($dateStr);
// 尝试常见格式
$formats = [
'm-d-y H:i', // 01-01-24 12:00
'm-d-y H:iA', // 01-01-24 12:00PM
'm-d-Y H:i', // 01-01-2024 12:00
'm-d-Y H:iA', // 01-01-2024 12:00PM
'd-m-y H:i', // 01-01-24 12:00 (欧洲格式)
'd-m-Y H:i' // 01-01-2024 12:00 (欧洲格式)
];
foreach ($formats as $format) {
$date = DateTime::createFromFormat($format, $dateStr);
if ($date !== false) {
return $date->getTimestamp();
}
}
return strtotime($dateStr) ?: 0;
}
/**
* 解析FTP原始列表
* @param array $rawList 原始列表数组
* @return array 解析后的文件信息数组
*/
public static function parseList($rawList) {
$parsed = [];
foreach ($rawList as $line) {
// 跳过空行和特殊行(如总计行)
if (empty(trim($line)) || strpos($line, 'total ') === 0) {
continue;
}
$parsed[] = self::parseLine($line);
}
return $parsed;
}
/**
* 格式化权限字符串为可读格式
*/
public static function formatPermissions($permString) {
if (empty($permString) || strlen($permString) != 9) {
return $permString;
}
$readable = '';
for ($i = 0; $i < 9; $i += 3) {
$group = substr($permString, $i, 3);
$readable .= ($group[0] == 'r' ? '读' : '-');
$readable .= ($group[1] == 'w' ? '写' : '-');
$readable .= ($group[2] == 'x' ? '执行' : '-');
if ($i < 6) $readable .= ' ';
}
return $readable;
}
/**
* 格式化文件大小为人类可读
*/
public static function formatSize($bytes) {
if ($bytes <= 0) return '0 B';
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$i = floor(log($bytes, 1024));
return round($bytes / pow(1024, $i), 2) . ' ' . $units[$i];
}
}
// 使用示例
$ftp_conn = ftp_connect('localhost');
if ($ftp_conn && ftp_login($ftp_conn, 'user', 'pass')) {
ftp_pasv($ftp_conn, true);
echo "=== FTP目录详细列表解析 ===\n\n";
// 获取原始列表
$raw_list = ftp_rawlist($ftp_conn, '.');
if ($raw_list !== false) {
// 解析列表
$parsed_list = FTPListParser::parseList($raw_list);
echo "找到 " . count($parsed_list) . " 个项目:\n";
echo "========================================\n";
foreach ($parsed_list as $item) {
$icon = $item['is_dir'] ? '📁' : ($item['is_link'] ? '🔗' : '📄');
$type = $item['is_dir'] ? '目录' : ($item['is_link'] ? '链接' : '文件');
$size = $item['is_dir'] ? '-' : FTPListParser::formatSize($item['size']);
$date = $item['modified'] > 0 ? date('Y-m-d H:i', $item['modified']) : '未知';
echo "$icon {$item['name']}\n";
echo " 类型: $type\n";
echo " 大小: $size\n";
echo " 修改时间: $date\n";
if (!empty($item['permissions'])) {
$perm_readable = FTPListParser::formatPermissions($item['permissions']);
echo " 权限: {$item['permissions']} ($perm_readable)\n";
}
if (!empty($item['owner'])) {
echo " 所有者: {$item['owner']}\n";
}
if ($item['is_link'] && !empty($item['link_target'])) {
echo " 链接目标: {$item['link_target']}\n";
}
echo "----------------------------------------\n";
}
// 统计信息
$dir_count = array_sum(array_map(function($item) {
return $item['is_dir'] ? 1 : 0;
}, $parsed_list));
$file_count = count($parsed_list) - $dir_count;
$total_size = array_sum(array_map(function($item) {
return $item['size'];
}, $parsed_list));
echo "\n统计信息:\n";
echo " 目录数: $dir_count\n";
echo " 文件数: $file_count\n";
echo " 总大小: " . FTPListParser::formatSize($total_size) . "\n";
} else {
echo "获取目录列表失败\n";
}
ftp_close($ftp_conn);
} else {
echo "无法连接FTP服务器\n";
}
?>
<?php
class FTPFileBrowser {
private $conn;
private $parser;
public function __construct($server, $username, $password) {
$this->conn = ftp_connect($server);
if (!$this->conn) {
throw new Exception("无法连接到FTP服务器");
}
if (!ftp_login($this->conn, $username, $password)) {
throw new Exception("FTP登录失败");
}
ftp_pasv($this->conn, true);
$this->parser = new FTPListParser();
}
public function browseDirectory($path = '', $recursive = false) {
$raw_list = ftp_rawlist($this->conn, $path, $recursive);
if ($raw_list === false) {
return [
'success' => false,
'error' => '无法获取目录列表',
'path' => $path
];
}
$parsed_items = $this->parser::parseList($raw_list);
// 按类型和名称排序
usort($parsed_items, function($a, $b) {
// 目录在前
if ($a['is_dir'] && !$b['is_dir']) return -1;
if (!$a['is_dir'] && $b['is_dir']) return 1;
// 按名称排序
return strcasecmp($a['name'], $b['name']);
});
// 提取目录信息
$dir_info = $this->getDirectoryInfo($path);
return [
'success' => true,
'path' => $path ?: '/',
'items' => $parsed_items,
'count' => count($parsed_items),
'dir_info' => $dir_info,
'recursive' => $recursive
];
}
public function getDirectoryInfo($path) {
$info = [
'path' => $path ?: '/',
'exists' => false,
'readable' => false,
'writable' => false,
'item_count' => 0,
'dir_count' => 0,
'file_count' => 0,
'total_size' => 0,
'largest_file' => null,
'newest_file' => null
];
$raw_list = ftp_rawlist($this->conn, $path);
if ($raw_list === false) {
return $info;
}
$info['exists'] = true;
$info['readable'] = true;
$parsed_items = $this->parser::parseList($raw_list);
$info['item_count'] = count($parsed_items);
foreach ($parsed_items as $item) {
if ($item['is_dir']) {
$info['dir_count']++;
} else {
$info['file_count']++;
$info['total_size'] += $item['size'];
// 最大文件
if (!$info['largest_file'] || $item['size'] > $info['largest_file']['size']) {
$info['largest_file'] = $item;
}
// 最新文件
if ($item['modified'] > 0) {
if (!$info['newest_file'] || $item['modified'] > $info['newest_file']['modified']) {
$info['newest_file'] = $item;
}
}
}
}
// 测试写入权限(尝试创建测试文件)
$info['writable'] = $this->testWritePermission($path);
return $info;
}
public function searchFiles($pattern, $search_path = '', $search_type = 'both') {
$results = [];
// 获取目录列表
$browse_result = $this->browseDirectory($search_path);
if (!$browse_result['success']) {
return $results;
}
foreach ($browse_result['items'] as $item) {
$matches = false;
// 检查类型
if ($search_type == 'both' ||
($search_type == 'dir' && $item['is_dir']) ||
($search_type == 'file' && !$item['is_dir'])) {
// 使用通配符匹配
if (fnmatch($pattern, $item['name'])) {
$matches = true;
}
}
if ($matches) {
$results[] = $item;
}
// 如果是目录,递归搜索(避免无限递归)
if ($item['is_dir'] && $item['name'] != '.' && $item['name'] != '..') {
$sub_path = rtrim($search_path, '/') . '/' . $item['name'];
$sub_results = $this->searchFiles($pattern, $sub_path, $search_type);
$results = array_merge($results, $sub_results);
}
}
return $results;
}
public function getFileTree($path = '', $max_depth = 3, $current_depth = 0) {
if ($current_depth >= $max_depth) {
return [
'path' => $path,
'name' => basename($path) ?: '/',
'type' => 'dir',
'truncated' => true
];
}
$tree = [
'path' => $path,
'name' => basename($path) ?: '/',
'type' => 'dir',
'contents' => []
];
$browse_result = $this->browseDirectory($path);
if (!$browse_result['success']) {
$tree['error'] = $browse_result['error'];
return $tree;
}
foreach ($browse_result['items'] as $item) {
// 跳过当前目录和上级目录
if ($item['name'] == '.' || $item['name'] == '..') {
continue;
}
if ($item['is_dir']) {
// 目录,递归获取子树
$sub_path = rtrim($path, '/') . '/' . $item['name'];
$subtree = $this->getFileTree($sub_path, $max_depth, $current_depth + 1);
$tree['contents'][] = $subtree;
} else {
// 文件
$tree['contents'][] = [
'path' => rtrim($path, '/') . '/' . $item['name'],
'name' => $item['name'],
'type' => 'file',
'size' => $item['size'],
'modified' => $item['modified'],
'permissions' => $item['permissions']
];
}
}
return $tree;
}
private function testWritePermission($path) {
$test_filename = '.write_test_' . time();
$test_path = rtrim($path, '/') . '/' . $test_filename;
// 创建临时文件内容
$temp_file = tempnam(sys_get_temp_dir(), 'ftp_test');
file_put_contents($temp_file, 'test');
// 尝试上传测试文件
$result = @ftp_put($this->conn, $test_path, $temp_file, FTP_ASCII);
// 清理
unlink($temp_file);
if ($result) {
// 删除测试文件
@ftp_delete($this->conn, $test_path);
return true;
}
return false;
}
public function close() {
if ($this->conn) {
ftp_close($this->conn);
}
}
public function __destruct() {
$this->close();
}
}
// 使用示例
try {
$browser = new FTPFileBrowser('localhost', 'user', 'pass');
echo "=== FTP文件浏览器 ===\n\n";
// 浏览当前目录
echo "1. 浏览当前目录:\n";
$result = $browser->browseDirectory('.');
if ($result['success']) {
echo "路径: {$result['path']}\n";
echo "项目数: {$result['count']}\n\n";
foreach ($result['items'] as $item) {
$icon = $item['is_dir'] ? '📁' : '📄';
$size = $item['is_dir'] ? '-' : FTPListParser::formatSize($item['size']);
echo " $icon {$item['name']} ($size)\n";
}
// 显示目录信息
$info = $result['dir_info'];
echo "\n目录信息:\n";
echo " 可读: " . ($info['readable'] ? '是' : '否') . "\n";
echo " 可写: " . ($info['writable'] ? '是' : '否') . "\n";
echo " 目录数: {$info['dir_count']}\n";
echo " 文件数: {$info['file_count']}\n";
echo " 总大小: " . FTPListParser::formatSize($info['total_size']) . "\n";
}
// 搜索文件
echo "\n2. 搜索文件:\n";
$search_results = $browser->searchFiles('*.txt', '.', 'file');
echo "找到 " . count($search_results) . " 个文本文件:\n";
foreach ($search_results as $file) {
echo " 📄 {$file['name']} (" . FTPListParser::formatSize($file['size']) . ")\n";
}
// 获取目录树
echo "\n3. 目录树结构:\n";
$tree = $browser->getFileTree('.', 2);
$this->printTree($tree);
$browser->close();
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
// 辅助函数:打印目录树
function printTree($tree, $indent = '', $last = true) {
$prefix = $indent . ($last ? '└── ' : '├── ');
$icon = $tree['type'] == 'dir' ? '📁 ' : '📄 ';
echo $prefix . $icon . $tree['name'];
if ($tree['type'] == 'file' && isset($tree['size'])) {
echo ' (' . FTPListParser::formatSize($tree['size']) . ')';
}
if (isset($tree['truncated'])) {
echo ' [...]';
}
echo "\n";
if (isset($tree['contents']) && is_array($tree['contents'])) {
$child_indent = $indent . ($last ? ' ' : '│ ');
$count = count($tree['contents']);
foreach ($tree['contents'] as $i => $child) {
$is_last = ($i == $count - 1);
printTree($child, $child_indent, $is_last);
}
}
}
?>
ftp_rawlist() 返回的格式取决于FTP服务器,不同服务器可能有不同格式recursive = true)并非所有服务器都支持total 1234)或其他元数据行linkname -> target)ftp_systype()确定服务器类型,选择合适的解析策略.和..条目ftp_rawlist()处理不同服务器格式的策略:
ftp_systype()获取服务器类型// 示例:自动选择解析器
function autoParseFTPList($rawList) {
// 尝试检测服务器类型
$firstLine = isset($rawList[0]) ? $rawList[0] : '';
if (preg_match('/^[d\-l][rwx\-]{9}/', $firstLine)) {
return parseUnixFormat($rawList);
} elseif (preg_match('/<DIR>/i', $firstLine)) {
return parseWindowsFormat($rawList);
} elseif (preg_match('/^\d{1,2}-\d{1,2}-\d{2,4}/', $firstLine)) {
return parseDOSFormat($rawList);
} else {
// 降级到简单文件名提取
return extractFilenames($rawList);
}
}
ftp_rawlist() 的性能考虑:
| 性能因素 | 影响 | 优化策略 |
|---|---|---|
| 目录大小 | 文件越多,传输和解析时间越长 | 分页显示,懒加载,虚拟滚动 |
| 网络延迟 | 高延迟显著增加获取时间 | 缓存结果,减少请求次数 |
| 递归列表 | 可能返回大量数据,消耗大量内存 | 避免深度递归,限制返回数量 |
| 解析复杂度 | 复杂解析消耗CPU时间 | 优化解析算法,异步解析 |
| 服务器负载 | 繁忙服务器响应慢 | 添加超时和重试机制 |
// 示例:分页获取目录列表
function getDirectoryPage($ftp, $path, $page = 1, $pageSize = 50) {
// 获取完整列表
$fullList = ftp_rawlist($ftp, $path);
if ($fullList === false) return false;
// 解析列表
$parsedList = parseFTPList($fullList);
// 分页
$total = count($parsedList);
$offset = ($page - 1) * $pageSize;
$pageItems = array_slice($parsedList, $offset, $pageSize);
return [
'items' => $pageItems,
'page' => $page,
'page_size' => $pageSize,
'total' => $total,
'total_pages' => ceil($total / $pageSize)
];
}
处理文件权限和所有者信息的注意事项:
// 示例:解析Unix权限字符串
function parseUnixPermissions($permString) {
if (strlen($permString) != 10) return null;
$type = $permString[0];
$perms = substr($permString, 1);
$octal = 0;
if ($perms[0] == 'r') $octal += 0400; // 所有者读
if ($perms[1] == 'w') $octal += 0200; // 所有者写
if ($perms[2] == 'x') $octal += 0100; // 所有者执行
if ($perms[3] == 'r') $octal += 0040; // 组读
if ($perms[4] == 'w') $octal += 0020; // 组写
if ($perms[5] == 'x') $octal += 0010; // 组执行
if ($perms[6] == 'r') $octal += 0004; // 其他读
if ($perms[7] == 'w') $octal += 0002; // 其他写
if ($perms[8] == 'x') $octal += 0001; // 其他执行
// 检查特殊权限位
if ($perms[2] == 's') $octal += 04000; // setuid
if ($perms[5] == 's') $octal += 02000; // setgid
if ($perms[8] == 't') $octal += 01000; // sticky bit
return [
'type' => $type,
'string' => $permString,
'octal' => sprintf('%04o', $octal),
'human' => self::formatPermissions($perms)
];
}
// 使用示例
$perms = parseUnixPermissions('-rwxr-xr-x');
// 返回:
// [
// 'type' => '-',
// 'string' => '-rwxr-xr-x',
// 'octal' => '0755',
// 'human' => '所有者: 读写执行, 组: 读执行, 其他: 读执行'
// ]
ftp_nlist() - 获取简单的文件名列表ftp_raw() - 发送原始FTP命令ftp_systype() - 获取FTP服务器系统类型ftp_size() - 获取文件大小ftp_mdtm() - 获取文件修改时间ftp_chdir() - 改变当前目录ftp_pwd() - 获取当前目录ftp_exec() - 在FTP服务器上执行命令