PHP ftp_systype() 函数

ftp_systype() 函数用于获取远程 FTP 服务器的系统类型。

该函数通过发送 FTP 的 SYST 命令来获取服务器操作系统的类型信息,这对于调整文件传输行为(如行结束符处理)非常有用。

提示:返回的系统类型字符串是服务器自行报告的,其格式和内容因服务器实现而异。常见的返回值包括 "UNIX"、"Windows"、"MACOS" 等。

语法

ftp_systype ( resource $ftp_stream ) : string|false

参数

参数 类型 描述
$ftp_stream resource 必需的。FTP 连接的标识符,由 ftp_connect()ftp_ssl_connect() 返回。

返回值

返回值 描述
string 成功时返回一个字符串,表示远程服务器的系统类型。
false 失败时返回 false。失败原因可能包括:
  • FTP 服务器不支持 SYST 命令
  • 连接已断开
  • 权限不足
  • 网络问题

常见的系统类型返回值

不同的 FTP 服务器可能返回不同的系统类型字符串。以下是一些常见的返回值:

返回值 描述
UNIXUNIX Type: L8 Unix/Linux 系统。这是最常见的返回值之一。
Windows_NTWIN32Windows Windows 系统。
MACOSMACOS Peter's Server MacOS 系统。
NetWare Novell NetWare 系统。
VMS VMS 操作系统。
OS/2 IBM OS/2 系统。
MVS IBM 主机系统。
空字符串 某些服务器可能返回空字符串。
自定义字符串 一些 FTP 服务器软件允许自定义系统类型字符串。

示例

示例 1:基本使用 - 获取系统类型

以下示例展示了如何获取 FTP 服务器的系统类型。

<?php
// 连接 FTP 服务器
$ftp_server = "ftp.example.com";
$ftp_user = "username";
$ftp_pass = "password";

$conn = ftp_connect($ftp_server);
if (!$conn) {
    die("无法连接到 $ftp_server");
}

// 登录
if (!@ftp_login($conn, $ftp_user, $ftp_pass)) {
    die("登录失败");
}

// 获取系统类型
$system_type = ftp_systype($conn);

if ($system_type !== false) {
    echo "FTP 服务器系统类型: " . $system_type . "\n";

    // 根据系统类型调整行为
    switch (strtoupper($system_type)) {
        case 'UNIX':
        case 'UNIX TYPE: L8':
            echo "检测到 Unix/Linux 系统\n";
            echo "建议使用二进制模式传输可执行文件\n";
            break;
        case 'WINDOWS_NT':
        case 'WIN32':
        case 'WINDOWS':
            echo "检测到 Windows 系统\n";
            echo "注意行结束符差异(CRLF vs LF)\n";
            break;
        case 'MACOS':
            echo "检测到 MacOS 系统\n";
            break;
        default:
            echo "未知系统类型,使用默认设置\n";
    }
} else {
    echo "无法获取系统类型。服务器可能不支持 SYST 命令。\n";
}

// 关闭连接
ftp_close($conn);
?>

示例 2:根据系统类型选择传输模式

根据服务器系统类型自动选择合适的传输模式。

<?php
$conn = ftp_connect("ftp.example.com");
ftp_login($conn, "username", "password");

/**
 * 根据系统类型确定文件传输模式
 * @param resource $conn FTP 连接
 * @param string $filename 文件名
 * @return int 传输模式常量(FTP_ASCII 或 FTP_BINARY)
 */
function determine_transfer_mode($conn, $filename) {
    $system_type = ftp_systype($conn);

    if ($system_type === false) {
        // 如果无法获取系统类型,则根据文件扩展名判断
        return determine_mode_by_extension($filename);
    }

    $system_type_upper = strtoupper($system_type);

    // Windows 系统通常需要 ASCII 模式处理文本文件
    if (strpos($system_type_upper, 'WINDOWS') !== false ||
        strpos($system_type_upper, 'WIN32') !== false ||
        strpos($system_type_upper, 'WIN') !== false) {

        // 对于文本文件使用 ASCII 模式
        if (is_text_file($filename)) {
            return FTP_ASCII;
        }
    }

    // 其他系统通常使用二进制模式
    return FTP_BINARY;
}

/**
 * 根据文件扩展名判断是否为文本文件
 */
function is_text_file($filename) {
    $text_extensions = ['txt', 'html', 'htm', 'php', 'js', 'css', 'xml', 'json', 'csv'];
    $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));

    return in_array($extension, $text_extensions);
}

/**
 * 根据文件扩展名判断传输模式(备用方法)
 */
function determine_mode_by_extension($filename) {
    $binary_extensions = ['exe', 'zip', 'rar', 'pdf', 'jpg', 'jpeg', 'png', 'gif', 'mp3', 'mp4'];
    $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));

    if (in_array($extension, $binary_extensions)) {
        return FTP_BINARY;
    } else {
        return FTP_ASCII;
    }
}

// 使用示例
$filename = "document.txt";
$transfer_mode = determine_transfer_mode($conn, $filename);

echo "文件: $filename\n";
echo "传输模式: " . ($transfer_mode == FTP_ASCII ? "ASCII" : "BINARY") . "\n";
echo "系统类型: " . ftp_systype($conn) . "\n";

ftp_close($conn);
?>

示例 3:检查多个服务器的系统类型

批量检查多个 FTP 服务器的系统类型。

<?php
/**
 * 检查多个 FTP 服务器的系统类型
 */
class FTPChecker {
    private $servers = [];

    public function addServer($hostname, $username, $password, $port = 21) {
        $this->servers[] = [
            'hostname' => $hostname,
            'username' => $username,
            'password' => $password,
            'port' => $port
        ];
    }

    public function checkAll() {
        $results = [];

        foreach ($this->servers as $server) {
            echo "检查服务器: " . $server['hostname'] . "...\n";

            $result = [
                'hostname' => $server['hostname'],
                'success' => false,
                'system_type' => null,
                'error' => null
            ];

            // 尝试连接
            $conn = @ftp_connect($server['hostname'], $server['port'], 10);

            if (!$conn) {
                $result['error'] = "连接失败";
                $results[] = $result;
                continue;
            }

            // 尝试登录
            if (!@ftp_login($conn, $server['username'], $server['password'])) {
                $result['error'] = "登录失败";
                ftp_close($conn);
                $results[] = $result;
                continue;
            }

            // 获取系统类型
            $system_type = ftp_systype($conn);

            if ($system_type !== false) {
                $result['success'] = true;
                $result['system_type'] = $system_type;
            } else {
                $result['error'] = "无法获取系统类型";
            }

            // 关闭连接
            ftp_close($conn);

            $results[] = $result;

            // 添加延迟,避免请求过快
            sleep(1);
        }

        return $results;
    }

    public function generateReport($results) {
        echo "\n========== FTP 服务器系统类型报告 ==========\n\n";

        $success_count = 0;
        $type_counts = [];

        foreach ($results as $result) {
            echo "服务器: " . $result['hostname'] . "\n";

            if ($result['success']) {
                $success_count++;
                $system_type = $result['system_type'];
                echo "系统类型: " . $system_type . "\n";

                // 统计系统类型
                if (!isset($type_counts[$system_type])) {
                    $type_counts[$system_type] = 0;
                }
                $type_counts[$system_type]++;
            } else {
                echo "状态: 失败\n";
                echo "错误: " . $result['error'] . "\n";
            }

            echo str_repeat("-", 40) . "\n";
        }

        // 统计信息
        echo "\n========== 统计信息 ==========\n";
        echo "总共检查: " . count($results) . " 个服务器\n";
        echo "成功获取: " . $success_count . " 个服务器\n";
        echo "失败: " . (count($results) - $success_count) . " 个服务器\n";

        if (!empty($type_counts)) {
            echo "\n系统类型分布:\n";
            foreach ($type_counts as $type => $count) {
                echo "  " . $type . ": " . $count . " 个\n";
            }
        }
    }
}

// 使用示例
$checker = new FTPChecker();

// 添加要检查的服务器
$checker->addServer('ftp.example1.com', 'user1', 'pass1');
$checker->addServer('ftp.example2.com', 'user2', 'pass2');
$checker->addServer('ftp.example3.com', 'user3', 'pass3');

// 执行检查
$results = $checker->checkAll();

// 生成报告
$checker->generateReport($results);
?>

示例 4:检测系统类型并调整目录列表解析

根据系统类型调整目录列表的解析方式。

<?php
$conn = ftp_connect("ftp.example.com");
ftp_login($conn, "username", "password");

/**
 * 根据系统类型解析目录列表
 * @param resource $conn FTP 连接
 * @param string $directory 要列出的目录
 * @return array 解析后的文件列表
 */
function ftp_parsed_list($conn, $directory = ".") {
    $system_type = ftp_systype($conn);
    $raw_list = ftp_rawlist($conn, $directory);

    if ($raw_list === false) {
        return [];
    }

    $parsed_list = [];

    foreach ($raw_list as $line) {
        if (empty($line)) {
            continue;
        }

        // 根据系统类型选择解析方式
        if (strpos(strtoupper($system_type), 'UNIX') !== false) {
            // Unix/Linux 格式的列表
            $parsed = parse_unix_listing($line);
        } elseif (strpos(strtoupper($system_type), 'WINDOWS') !== false ||
                 strpos(strtoupper($system_type), 'WIN') !== false) {
            // Windows 格式的列表
            $parsed = parse_windows_listing($line);
        } else {
            // 默认解析(尝试自动检测)
            $parsed = parse_unknown_listing($line);
        }

        if ($parsed !== null) {
            $parsed_list[] = $parsed;
        }
    }

    return $parsed_list;
}

/**
 * 解析 Unix/Linux 格式的目录列表
 */
function parse_unix_listing($line) {
    // Unix 格式示例: "drwxr-xr-x  2 user  group  4096 Jan 12 10:30 directory"
    $pattern = '/^([d\-l])([rwx\-]{9})\s+(\d+)\s+(\S+)\s+(\S+)\s+(\d+)\s+(\w{3}\s+\d{1,2}\s+[\d:]+)\s+(.+)$/';

    if (preg_match($pattern, $line, $matches)) {
        return [
            'type' => $matches[1] == 'd' ? 'directory' : ($matches[1] == 'l' ? 'link' : 'file'),
            'permissions' => $matches[2],
            'links' => $matches[3],
            'owner' => $matches[4],
            'group' => $matches[5],
            'size' => $matches[6],
            'date' => $matches[7],
            'name' => $matches[8]
        ];
    }

    return null;
}

/**
 * 解析 Windows 格式的目录列表
 */
function parse_windows_listing($line) {
    // Windows 格式示例: "01-12-24  10:30AM                 directory"
    // 或: "01-12-24  10:30AM             4096 file.txt"

    $pattern = '/^(\d{2}-\d{2}-\d{2})\s+(\d{1,2}:\d{2}(?:AM|PM)?)\s+((?:|\d+))\s+(.+)$/i';

    if (preg_match($pattern, $line, $matches)) {
        return [
            'type' => strtoupper($matches[3]) == '' ? 'directory' : 'file',
            'date' => $matches[1],
            'time' => $matches[2],
            'size' => strtoupper($matches[3]) == '' ? 0 : $matches[3],
            'name' => $matches[4]
        ];
    }

    return null;
}

/**
 * 解析未知格式的目录列表
 */
function parse_unknown_listing($line) {
    // 尝试多种格式
    $parsed = parse_unix_listing($line);

    if ($parsed === null) {
        $parsed = parse_windows_listing($line);
    }

    return $parsed;
}

// 使用示例
$system_type = ftp_systype($conn);
echo "系统类型: " . ($system_type ?: '未知') . "\n";

$files = ftp_parsed_list($conn);
echo "找到 " . count($files) . " 个项目\n";

foreach ($files as $file) {
    echo ($file['type'] == 'directory' ? '[DIR] ' : '[FILE] ') . $file['name'];
    if (isset($file['size'])) {
        echo " (" . $file['size'] . ")";
    }
    echo "\n";
}

ftp_close($conn);
?>

注意事项

  • 服务器支持:不是所有的 FTP 服务器都支持 SYST 命令。较旧或配置受限的服务器可能不会响应。
  • 返回值格式:返回的字符串格式因服务器而异,不应依赖特定的格式或内容。
  • 安全性:系统类型信息可能被用于指纹识别攻击,某些服务器可能会返回虚假信息或禁用此命令。
  • 缓存:考虑缓存系统类型结果,避免重复调用该函数,特别是在同一会话中。
  • 错误处理:始终检查返回值是否为 false,并准备好处理不支持的情况。
  • 兼容性:在编写跨平台的 FTP 客户端时,系统类型信息可以帮助调整行为,但不是必须的。
  • 替代方案:如果无法获取系统类型,可以根据其他信息(如文件列表格式)推断服务器类型。

常见问题

问题 解决方案
始终返回 false 服务器可能不支持 SYST 命令。检查服务器配置或考虑使用其他方法检测系统类型。
返回空字符串 某些服务器返回空字符串表示未知。这是有效响应,不同于 false。
返回值不一致 不同的 FTP 服务器软件可能返回不同的字符串。编写代码时应灵活处理。
性能问题 每次调用都会向服务器发送命令。在单个会话中缓存结果以提高性能。
安全考虑 在生产环境中,考虑是否真的需要系统类型信息,以及是否可能暴露服务器信息。

相关函数