PHP ftp_get_option() 函数

ftp_get_option() 函数获取FTP连接的各种运行时选项。

注意:该函数用于查询FTP连接的当前配置,常与ftp_set_option()配合使用。

语法

ftp_get_option(resource $ftp_stream, int $option): mixed

参数说明

参数 描述
$ftp_stream 必需。FTP连接的资源标识符,由ftp_connect()ftp_ssl_connect()函数返回。
$option 必需。要获取的选项名称。可以是以下常量:
  • FTP_TIMEOUT_SEC - 网络操作的超时时间(秒)
  • FTP_AUTOSEEK - 自动寻找位置(布尔值)
  • FTP_USEPASVADDRESS - 是否使用PASV命令返回的地址(布尔值)

返回值

  • 成功时返回选项的值
  • 失败时返回 false(如果给定的选项不支持,则返回false并生成警告)

选项常量详解

可用选项常量:

常量 描述 默认值
FTP_TIMEOUT_SEC 0 网络操作的超时时间(秒)。设置FTP操作的超时时间,包括连接、数据传输等。 90秒
FTP_AUTOSEEK 1 自动寻找位置。当恢复上传或下载时,是否自动寻找文件中的正确位置。 true
FTP_USEPASVADDRESS 2 是否使用PASV命令返回的地址。有些FTP服务器在被动模式下返回错误的IP地址,可以设置为false来解决。 true

示例

示例1:基本使用

获取FTP连接的各种选项:

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

// 建立FTP连接
$conn_id = ftp_connect($ftp_server);
if ($conn_id === false) {
    die("无法连接到FTP服务器");
}

// 登录FTP服务器
if (!ftp_login($conn_id, $ftp_user, $ftp_pass)) {
    ftp_close($conn_id);
    die("FTP登录失败");
}

echo "FTP连接选项信息:\n";
echo "=====================\n";

// 获取超时设置
$timeout = ftp_get_option($conn_id, FTP_TIMEOUT_SEC);
if ($timeout !== false) {
    echo "FTP_TIMEOUT_SEC: {$timeout} 秒\n";
} else {
    echo "无法获取超时设置\n";
}

// 获取AUTOSEEK设置
$autoseek = ftp_get_option($conn_id, FTP_AUTOSEEK);
if ($autoseek !== false) {
    echo "FTP_AUTOSEEK: " . ($autoseek ? "启用" : "禁用") . "\n";
}

// 获取USEPASVADDRESS设置
$usepasvaddress = ftp_get_option($conn_id, FTP_USEPASVADDRESS);
if ($usepasvaddress !== false) {
    echo "FTP_USEPASVADDRESS: " . ($usepasvaddress ? "启用" : "禁用") . "\n";
}

// 尝试获取不支持的选项
$invalid_option = ftp_get_option($conn_id, 999);
if ($invalid_option === false) {
    echo "\n不支持的选项返回: false\n";
}

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

示例2:诊断连接问题

<?php
/**
 * FTP连接诊断工具
 */
class FTPDiagnosticTool {
    private $conn;

    public function __construct($host, $username, $password, $port = 21, $timeout = 30) {
        $this->conn = ftp_connect($host, $port, $timeout);
        if (!$this->conn) {
            throw new Exception("无法连接到FTP服务器");
        }

        if (!ftp_login($this->conn, $username, $password)) {
            ftp_close($this->conn);
            throw new Exception("FTP登录失败");
        }
    }

    /**
     * 获取所有选项信息
     */
    public function getOptionsInfo() {
        $options = [
            'FTP_TIMEOUT_SEC' => FTP_TIMEOUT_SEC,
            'FTP_AUTOSEEK' => FTP_AUTOSEEK,
            'FTP_USEPASVADDRESS' => FTP_USEPASVADDRESS,
        ];

        $results = [];

        foreach ($options as $name => $value) {
            $result = ftp_get_option($this->conn, $value);
            if ($result !== false) {
                if ($name === 'FTP_TIMEOUT_SEC') {
                    $results[$name] = $result . ' 秒';
                } else {
                    $results[$name] = $result ? 'true' : 'false';
                }
            } else {
                $results[$name] = '不支持';
            }
        }

        return $results;
    }

    /**
     * 检查连接性能
     */
    public function checkPerformance() {
        $start_time = microtime(true);

        // 获取当前目录来测试响应时间
        $pwd = ftp_pwd($this->conn);

        $end_time = microtime(true);
        $response_time = round(($end_time - $start_time) * 1000, 2); // 毫秒

        return [
            'response_time' => $response_time . ' 毫秒',
            'current_dir' => $pwd
        ];
    }

    /**
     * 测试被动模式
     */
    public function testPassiveMode() {
        // 获取当前PASV地址设置
        $use_pasv_address = ftp_get_option($this->conn, FTP_USEPASVADDRESS);

        // 开启被动模式
        $pasv_result = ftp_pasv($this->conn, true);

        // 尝试列出文件来测试被动模式
        $list_result = ftp_nlist($this->conn, ".");

        return [
            'pasv_address_setting' => $use_pasv_address ? '使用服务器返回地址' : '使用连接地址',
            'pasv_mode_enabled' => $pasv_result,
            'directory_listing' => $list_result !== false ? '成功' : '失败',
            'file_count' => $list_result !== false ? count($list_result) : 0
        ];
    }

    /**
     * 生成诊断报告
     */
    public function generateReport() {
        $report = [];

        $report['连接选项'] = $this->getOptionsInfo();
        $report['性能测试'] = $this->checkPerformance();
        $report['被动模式测试'] = $this->testPassiveMode();
        $report['服务器信息'] = $this->getServerInfo();

        return $report;
    }

    /**
     * 获取服务器信息
     */
    private function getServerInfo() {
        $info = [];

        // 尝试获取系统类型
        if (function_exists('ftp_systype')) {
            $info['system_type'] = ftp_systype($this->conn) ?: '未知';
        }

        // 获取连接状态
        $info['connection_status'] = is_resource($this->conn) ? '活动' : '断开';

        return $info;
    }

    public function close() {
        if (is_resource($this->conn)) {
            ftp_close($this->conn);
        }
    }

    public function __destruct() {
        $this->close();
    }
}

// 使用示例
try {
    $diagnostic = new FTPDiagnosticTool("ftp.example.com", "username", "password");

    echo "FTP连接诊断报告\n";
    echo str_repeat("=", 50) . "\n\n";

    $report = $diagnostic->generateReport();

    foreach ($report as $section => $data) {
        echo "【{$section}】\n";
        echo str_repeat("-", 30) . "\n";

        if (is_array($data)) {
            foreach ($data as $key => $value) {
                if (is_array($value)) {
                    echo "  {$key}:\n";
                    foreach ($value as $subkey => $subvalue) {
                        echo "    {$subkey}: {$subvalue}\n";
                    }
                } else {
                    echo "  {$key}: {$value}\n";
                }
            }
        }
        echo "\n";
    }

    $diagnostic->close();

} catch (Exception $e) {
    echo "诊断错误: " . $e->getMessage() . "\n";
}
?>

示例3:动态调整连接设置

<?php
/**
 * 智能FTP连接管理器
 */
class SmartFTPManager {
    private $conn;
    private $options = [];
    private $performance_log = [];

    public function __construct($host, $username, $password) {
        $this->conn = ftp_connect($host);
        if (!$this->conn) {
            throw new Exception("无法连接到FTP服务器");
        }

        if (!ftp_login($this->conn, $username, $password)) {
            ftp_close($this->conn);
            throw new Exception("FTP登录失败");
        }

        // 保存初始选项
        $this->saveCurrentOptions();
    }

    /**
     * 保存当前选项
     */
    private function saveCurrentOptions() {
        $this->options['timeout'] = ftp_get_option($this->conn, FTP_TIMEOUT_SEC);
        $this->options['autoseek'] = ftp_get_option($this->conn, FTP_AUTOSEEK);
        $this->options['use_pasv_address'] = ftp_get_option($this->conn, FTP_USEPASVADDRESS);
    }

    /**
     * 根据操作类型优化设置
     */
    public function optimizeForOperation($operation_type) {
        $original_options = $this->getCurrentOptions();

        switch ($operation_type) {
            case 'large_file_transfer':
                // 大文件传输:增加超时时间,启用断点续传
                ftp_set_option($this->conn, FTP_TIMEOUT_SEC, 300);
                ftp_set_option($this->conn, FTP_AUTOSEEK, true);
                echo "优化为大型文件传输模式\n";
                break;

            case 'multiple_small_files':
                // 多个小文件:减小超时时间,禁用自动寻找(因为不需要)
                ftp_set_option($this->conn, FTP_TIMEOUT_SEC, 30);
                ftp_set_option($this->conn, FTP_AUTOSEEK, false);
                echo "优化为多个小文件传输模式\n";
                break;

            case 'unstable_network':
                // 不稳定网络:增加超时时间,禁用PASV地址使用
                ftp_set_option($this->conn, FTP_TIMEOUT_SEC, 180);
                ftp_set_option($this->conn, FTP_USEPASVADDRESS, false);
                echo "优化为不稳定网络模式\n";
                break;

            default:
                // 恢复默认设置
                $this->restoreDefaultOptions();
                echo "使用默认设置\n";
        }

        return $original_options;
    }

    /**
     * 获取当前所有选项
     */
    public function getCurrentOptions() {
        return [
            'FTP_TIMEOUT_SEC' => ftp_get_option($this->conn, FTP_TIMEOUT_SEC),
            'FTP_AUTOSEEK' => ftp_get_option($this->conn, FTP_AUTOSEEK),
            'FTP_USEPASVADDRESS' => ftp_get_option($this->conn, FTP_USEPASVADDRESS)
        ];
    }

    /**
     * 恢复默认选项
     */
    public function restoreDefaultOptions() {
        if (isset($this->options['timeout'])) {
            ftp_set_option($this->conn, FTP_TIMEOUT_SEC, $this->options['timeout']);
        }
        if (isset($this->options['autoseek'])) {
            ftp_set_option($this->conn, FTP_AUTOSEEK, $this->options['autoseek']);
        }
        if (isset($this->options['use_pasv_address'])) {
            ftp_set_option($this->conn, FTP_USEPASVADDRESS, $this->options['use_pasv_address']);
        }
    }

    /**
     * 监控操作性能
     */
    public function monitorOperation($operation_name, callable $operation) {
        $start_time = microtime(true);

        try {
            $result = $operation();
            $success = true;
        } catch (Exception $e) {
            $result = $e->getMessage();
            $success = false;
        }

        $end_time = microtime(true);
        $duration = round(($end_time - $start_time) * 1000, 2);

        // 记录性能日志
        $log_entry = [
            'operation' => $operation_name,
            'duration_ms' => $duration,
            'success' => $success,
            'timestamp' => date('Y-m-d H:i:s'),
            'options' => $this->getCurrentOptions()
        ];

        $this->performance_log[] = $log_entry;

        // 如果操作时间过长,建议调整设置
        if ($duration > 5000) { // 超过5秒
            echo "警告: {$operation_name} 操作耗时 {$duration} 毫秒,建议增加超时时间\n";
        }

        return [
            'result' => $result,
            'duration' => $duration,
            'success' => $success
        ];
    }

    /**
     * 获取性能报告
     */
    public function getPerformanceReport() {
        if (empty($this->performance_log)) {
            return "没有性能数据";
        }

        $total_operations = count($this->performance_log);
        $successful_operations = 0;
        $total_duration = 0;

        foreach ($this->performance_log as $log) {
            if ($log['success']) {
                $successful_operations++;
            }
            $total_duration += $log['duration_ms'];
        }

        $success_rate = ($successful_operations / $total_operations) * 100;
        $avg_duration = $total_duration / $total_operations;

        return [
            'total_operations' => $total_operations,
            'successful_operations' => $successful_operations,
            'success_rate' => round($success_rate, 2) . '%',
            'average_duration' => round($avg_duration, 2) . ' 毫秒',
            'logs' => $this->performance_log
        ];
    }

    public function getConnection() {
        return $this->conn;
    }

    public function close() {
        if (is_resource($this->conn)) {
            ftp_close($this->conn);
        }
    }
}

// 使用示例
try {
    $manager = new SmartFTPManager("ftp.example.com", "username", "password");

    echo "初始连接选项:\n";
    $initial_options = $manager->getCurrentOptions();
    foreach ($initial_options as $key => $value) {
        echo "  {$key}: " . (is_bool($value) ? ($value ? 'true' : 'false') : $value) . "\n";
    }

    echo "\n优化为大文件传输模式:\n";
    $manager->optimizeForOperation('large_file_transfer');

    // 模拟大文件上传
    $result = $manager->monitorOperation('大文件上传', function() use ($manager) {
        $conn = $manager->getConnection();
        // 这里应该是实际的上传操作
        // ftp_fput($conn, "remote_large_file.zip", $local_handle, FTP_BINARY);
        sleep(2); // 模拟耗时操作
        return "上传成功";
    });

    echo "操作结果: {$result['result']} (耗时: {$result['duration']} 毫秒)\n";

    // 获取性能报告
    echo "\n性能报告:\n";
    $report = $manager->getPerformanceReport();
    if (is_array($report)) {
        foreach ($report as $key => $value) {
            if ($key !== 'logs') {
                echo "  {$key}: {$value}\n";
            }
        }
    }

    $manager->close();

} catch (Exception $e) {
    echo "错误: " . $e->getMessage() . "\n";
}
?>

示例4:设置和获取选项的最佳实践

<?php
/**
 * FTP选项管理器
 */
class FTPOptionsManager {
    const OPTION_TIMEOUT = FTP_TIMEOUT_SEC;
    const OPTION_AUTOSEEK = FTP_AUTOSEEK;
    const OPTION_USEPASVADDRESS = FTP_USEPASVADDRESS;

    private $conn;
    private $option_history = [];

    public function __construct($host, $username, $password) {
        $this->conn = ftp_connect($host);
        if (!$this->conn) {
            throw new Exception("无法连接到FTP服务器");
        }

        if (!ftp_login($this->conn, $username, $password)) {
            ftp_close($this->conn);
            throw new Exception("FTP登录失败");
        }

        $this->recordOptionState("连接建立");
    }

    /**
     * 安全获取选项值
     */
    public function safeGetOption($option) {
        $value = ftp_get_option($this->conn, $option);

        if ($value === false) {
            // 检查是否是无效选项
            $last_error = error_get_last();
            if ($last_error && strpos($last_error['message'], 'Unrecognized option') !== false) {
                throw new Exception("不支持的FTP选项: " . $this->getOptionName($option));
            }
        }

        return $value;
    }

    /**
     * 安全设置选项值
     */
    public function safeSetOption($option, $value) {
        $old_value = $this->safeGetOption($option);

        if (!ftp_set_option($this->conn, $option, $value)) {
            throw new Exception("设置选项失败: " . $this->getOptionName($option));
        }

        $this->recordOptionChange($option, $old_value, $value);

        return $old_value;
    }

    /**
     * 记录选项状态
     */
    private function recordOptionState($context) {
        $state = [
            'timestamp' => microtime(true),
            'context' => $context,
            'options' => []
        ];

        $options = [
            self::OPTION_TIMEOUT,
            self::OPTION_AUTOSEEK,
            self::OPTION_USEPASVADDRESS
        ];

        foreach ($options as $option) {
            try {
                $value = $this->safeGetOption($option);
                $state['options'][$this->getOptionName($option)] = $value;
            } catch (Exception $e) {
                $state['options'][$this->getOptionName($option)] = '错误: ' . $e->getMessage();
            }
        }

        $this->option_history[] = $state;
    }

    /**
     * 记录选项变化
     */
    private function recordOptionChange($option, $old_value, $new_value) {
        $change = [
            'timestamp' => microtime(true),
            'option' => $this->getOptionName($option),
            'old_value' => $this->formatValue($old_value),
            'new_value' => $this->formatValue($new_value),
            'caller' => $this->getCallerInfo()
        ];

        $this->option_history[] = [
            'timestamp' => microtime(true),
            'context' => '选项变更',
            'change' => $change
        ];
    }

    /**
     * 获取选项名称
     */
    private function getOptionName($option) {
        $names = [
            self::OPTION_TIMEOUT => 'FTP_TIMEOUT_SEC',
            self::OPTION_AUTOSEEK => 'FTP_AUTOSEEK',
            self::OPTION_USEPASVADDRESS => 'FTP_USEPASVADDRESS'
        ];

        return isset($names[$option]) ? $names[$option] : "未知选项({$option})";
    }

    /**
     * 格式化值
     */
    private function formatValue($value) {
        if (is_bool($value)) {
            return $value ? 'true' : 'false';
        } elseif (is_int($value)) {
            return (string)$value;
        } else {
            return var_export($value, true);
        }
    }

    /**
     * 获取调用者信息
     */
    private function getCallerInfo() {
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
        if (isset($trace[2])) {
            return $trace[2]['file'] . ':' . $trace[2]['line'];
        }
        return '未知';
    }

    /**
     * 获取选项历史
     */
    public function getOptionHistory() {
        return $this->option_history;
    }

    /**
     * 生成选项报告
     */
    public function generateOptionReport() {
        $report = "FTP选项监控报告\n";
        $report .= str_repeat("=", 50) . "\n";

        foreach ($this->option_history as $index => $entry) {
            $time = date('Y-m-d H:i:s', (int)$entry['timestamp']);
            $report .= "[{$time}] {$entry['context']}\n";

            if (isset($entry['options'])) {
                foreach ($entry['options'] as $name => $value) {
                    $report .= "  {$name}: " . $this->formatValue($value) . "\n";
                }
            }

            if (isset($entry['change'])) {
                $change = $entry['change'];
                $report .= "  变更: {$change['option']} = {$change['old_value']} -> {$change['new_value']}\n";
                $report .= "  调用: {$change['caller']}\n";
            }

            $report .= "\n";
        }

        return $report;
    }

    /**
     * 获取推荐配置
     */
    public function getRecommendedConfiguration($use_case) {
        $recommendations = [
            'web_hosting' => [
                'timeout' => 30,
                'autoseek' => true,
                'use_pasv_address' => true,
                'notes' => '适合常规网站托管,中等超时时间'
            ],
            'backup_server' => [
                'timeout' => 300,
                'autoseek' => true,
                'use_pasv_address' => false,
                'notes' => '适合备份服务器,长超时时间,禁用PASV地址'
            ],
            'file_sharing' => [
                'timeout' => 60,
                'autoseek' => false,
                'use_pasv_address' => true,
                'notes' => '适合文件共享,短超时时间,禁用自动寻找'
            ],
            'cdn_storage' => [
                'timeout' => 120,
                'autoseek' => true,
                'use_pasv_address' => true,
                'notes' => '适合CDN存储,中等超时时间'
            ]
        ];

        return isset($recommendations[$use_case]) ? $recommendations[$use_case] : null;
    }

    /**
     * 应用推荐配置
     */
    public function applyRecommendedConfiguration($use_case) {
        $config = $this->getRecommendedConfiguration($use_case);

        if (!$config) {
            throw new Exception("未知的用例: {$use_case}");
        }

        $this->safeSetOption(self::OPTION_TIMEOUT, $config['timeout']);
        $this->safeSetOption(self::OPTION_AUTOSEEK, $config['autoseek']);
        $this->safeSetOption(self::OPTION_USEPASVADDRESS, $config['use_pasv_address']);

        return $config;
    }

    public function getConnection() {
        return $this->conn;
    }

    public function close() {
        if (is_resource($this->conn)) {
            ftp_close($this->conn);
        }
    }
}

// 使用示例
try {
    $manager = new FTPOptionsManager("ftp.example.com", "username", "password");

    echo "当前选项状态:\n";
    $current_state = $manager->safeGetOption(FTPOptionsManager::OPTION_TIMEOUT);
    echo "超时时间: " . ($current_state !== false ? $current_state . ' 秒' : '未知') . "\n";

    echo "\n应用Web托管推荐配置:\n";
    $config = $manager->applyRecommendedConfiguration('web_hosting');
    echo "已应用配置: {$config['notes']}\n";

    echo "\n监控报告:\n";
    echo $manager->generateOptionReport();

    // 进行一些操作
    $conn = $manager->getConnection();
    $manager->safeSetOption(FTPOptionsManager::OPTION_TIMEOUT, 60);

    echo "\n最终选项状态:\n";
    echo "超时时间: " . $manager->safeGetOption(FTPOptionsManager::OPTION_TIMEOUT) . " 秒\n";

    $manager->close();

} catch (Exception $e) {
    echo "错误: " . $e->getMessage() . "\n";
}
?>

示例5:错误处理和选项验证

<?php
/**
 * 带有完整错误处理的FTP选项工具
 */
class SafeFTPOptions {
    private $conn;
    private $last_error = null;

    public function __construct($host, $username, $password, $timeout = 90) {
        // 设置错误处理器
        set_error_handler([$this, 'errorHandler']);

        $this->conn = ftp_connect($host, 21, $timeout);
        if (!$this->conn) {
            $this->throwException("无法连接到FTP服务器");
        }

        if (!ftp_login($this->conn, $username, $password)) {
            ftp_close($this->conn);
            $this->throwException("FTP登录失败");
        }

        // 恢复错误处理器
        restore_error_handler();
    }

    /**
     * 自定义错误处理器
     */
    public function errorHandler($errno, $errstr, $errfile, $errline) {
        $this->last_error = [
            'level' => $errno,
            'message' => $errstr,
            'file' => $errfile,
            'line' => $errline
        ];
        return true; // 阻止PHP默认错误处理器
    }

    /**
     * 安全获取选项(带验证)
     */
    public function getOption($option, $default = null) {
        $this->last_error = null;
        set_error_handler([$this, 'errorHandler']);

        $value = ftp_get_option($this->conn, $option);

        restore_error_handler();

        if ($value === false) {
            if ($this->last_error) {
                // 检查是否是"Unrecognized option"错误
                if (strpos($this->last_error['message'], 'Unrecognized option') !== false) {
                    $this->logWarning("尝试获取不支持的选项: " . $this->optionToString($option));
                    return $default;
                } else {
                    $this->logError("获取选项失败", $this->last_error);
                }
            }
            return $default;
        }

        return $value;
    }

    /**
     * 验证选项值是否有效
     */
    public function validateOptionValue($option, $value) {
        switch ($option) {
            case FTP_TIMEOUT_SEC:
                return is_int($value) && $value > 0;

            case FTP_AUTOSEEK:
            case FTP_USEPASVADDRESS:
                return is_bool($value);

            default:
                $this->logWarning("验证未知选项: " . $this->optionToString($option));
                return true; // 对于未知选项,不进行验证
        }
    }

    /**
     * 设置选项(带验证和回滚)
     */
    public function setOption($option, $value, $validate = true) {
        if ($validate && !$this->validateOptionValue($option, $value)) {
            throw new Exception("无效的选项值: " . var_export($value, true) .
                               " 对于选项: " . $this->optionToString($option));
        }

        // 获取旧值以便回滚
        $old_value = $this->getOption($option);

        set_error_handler([$this, 'errorHandler']);
        $result = ftp_set_option($this->conn, $option, $value);
        restore_error_handler();

        if (!$result) {
            if ($this->last_error) {
                $this->logError("设置选项失败", $this->last_error);
            }
            throw new Exception("无法设置选项: " . $this->optionToString($option));
        }

        // 验证设置是否成功
        $actual_value = $this->getOption($option);
        if ($actual_value !== $value) {
            // 回滚到旧值
            if ($old_value !== false) {
                ftp_set_option($this->conn, $option, $old_value);
            }
            throw new Exception("选项设置验证失败: 期望 " . var_export($value, true) .
                               " 但得到 " . var_export($actual_value, true));
        }

        $this->logInfo("选项已设置", [
            'option' => $this->optionToString($option),
            'old_value' => $old_value,
            'new_value' => $value
        ]);

        return $old_value;
    }

    /**
     * 获取所有支持的选项
     */
    public function getAllSupportedOptions() {
        $options = [];
        $test_values = [
            FTP_TIMEOUT_SEC => 90,
            FTP_AUTOSEEK => true,
            FTP_USEPASVADDRESS => true
        ];

        foreach ($test_values as $option => $test_value) {
            $old_value = $this->getOption($option);
            if ($old_value !== false) {
                // 尝试设置并恢复来测试是否支持
                try {
                    $this->setOption($option, $test_value, false);
                    // 恢复原值
                    if ($old_value !== false) {
                        $this->setOption($option, $old_value, false);
                    }
                    $options[$this->optionToString($option)] = true;
                } catch (Exception $e) {
                    $options[$this->optionToString($option)] = false;
                }
            } else {
                $options[$this->optionToString($option)] = false;
            }
        }

        return $options;
    }

    /**
     * 选项常量转字符串
     */
    private function optionToString($option) {
        $map = [
            FTP_TIMEOUT_SEC => 'FTP_TIMEOUT_SEC',
            FTP_AUTOSEEK => 'FTP_AUTOSEEK',
            FTP_USEPASVADDRESS => 'FTP_USEPASVADDRESS'
        ];

        return isset($map[$option]) ? $map[$option] : "未知选项({$option})";
    }

    /**
     * 抛出异常
     */
    private function throwException($message) {
        restore_error_handler();
        throw new Exception($message);
    }

    /**
     * 日志方法
     */
    private function logError($message, $context = []) {
        error_log("[ERROR] " . $message . " " . json_encode($context));
    }

    private function logWarning($message, $context = []) {
        error_log("[WARNING] " . $message . " " . json_encode($context));
    }

    private function logInfo($message, $context = []) {
        error_log("[INFO] " . $message . " " . json_encode($context));
    }

    public function getLastError() {
        return $this->last_error;
    }

    public function getConnection() {
        return $this->conn;
    }

    public function close() {
        if (is_resource($this->conn)) {
            ftp_close($this->conn);
        }
    }
}

// 使用示例
try {
    $ftp = new SafeFTPOptions("ftp.example.com", "username", "password");

    echo "测试支持的选项:\n";
    $supported_options = $ftp->getAllSupportedOptions();
    foreach ($supported_options as $name => $supported) {
        echo "  {$name}: " . ($supported ? "支持" : "不支持") . "\n";
    }

    echo "\n获取超时设置:\n";
    $timeout = $ftp->getOption(FTP_TIMEOUT_SEC, 90);
    echo "  当前超时: {$timeout} 秒\n";

    echo "\n设置新超时时间:\n";
    $old_timeout = $ftp->setOption(FTP_TIMEOUT_SEC, 120);
    echo "  旧超时: {$old_timeout} 秒\n";
    echo "  新超时: " . $ftp->getOption(FTP_TIMEOUT_SEC) . " 秒\n";

    echo "\n尝试获取不支持的选项:\n";
    $invalid_option = $ftp->getOption(999, 'default_value');
    echo "  结果: {$invalid_option}\n";

    $ftp->close();

} catch (Exception $e) {
    echo "错误: " . $e->getMessage() . "\n";
    if ($ftp->getLastError()) {
        echo "详细错误: " . print_r($ftp->getLastError(), true) . "\n";
    }
}
?>

选项详解

描述

设置FTP操作的超时时间(秒)。这个选项影响所有网络操作,包括连接、登录、数据传输等。

建议值
  • 常规操作:30-90秒
  • 大文件传输:300-600秒
  • 不稳定网络:120-180秒
  • 局域网:10-30秒
使用示例
// 获取当前超时设置
$current_timeout = ftp_get_option($conn, FTP_TIMEOUT_SEC);

// 设置新的超时时间
ftp_set_option($conn, FTP_TIMEOUT_SEC, 300); // 5分钟

// 验证设置
$new_timeout = ftp_get_option($conn, FTP_TIMEOUT_SEC);
echo "超时时间已设置为: {$new_timeout} 秒";

描述

控制在恢复上传或下载时是否自动寻找文件中的正确位置。启用此选项后,当指定startpos参数时,会自动寻找文件位置。

适用场景
  • 启用(true):断点续传、大文件分块传输
  • 禁用(false):小文件传输、不需要恢复位置的场景
注意事项

在某些FTP服务器上,自动寻找可能导致性能问题。如果遇到问题,可以尝试禁用它。

描述

控制是否使用PASV命令返回的IP地址。有些FTP服务器在被动模式下返回错误的IP地址,禁用此选项可以解决连接问题。

常见问题
  • 连接超时:如果服务器返回错误的IP地址,可能导致连接超时
  • 被动模式失败:某些防火墙配置可能导致被动模式失败
  • NAT网络:在NAT网络环境中可能需要禁用此选项
解决方案

如果遇到被动模式连接问题,可以尝试:

// 禁用PASV地址使用
ftp_set_option($conn, FTP_USEPASVADDRESS, false);

// 然后开启被动模式
ftp_pasv($conn, true);

常见问题

ftp_get_option()返回false可能有以下原因:

  1. 不支持的选项:尝试获取PHP版本不支持的选项
  2. 无效的连接:FTP连接已关闭或无效
  3. 参数错误:选项常量值不正确
  4. PHP配置:FTP扩展未正确安装或配置

可以使用以下代码进行调试:

$value = ftp_get_option($conn, FTP_TIMEOUT_SEC);
if ($value === false) {
    $error = error_get_last();
    if ($error) {
        echo "错误: " . $error['message'] . "\n";
    }
}

<?php
function getSupportedFTPOptions() {
    $supported = [];

    // 测试标准选项
    $test_options = [
        'FTP_TIMEOUT_SEC' => FTP_TIMEOUT_SEC,
        'FTP_AUTOSEEK' => FTP_AUTOSEEK,
        'FTP_USEPASVADDRESS' => FTP_USEPASVADDRESS,
    ];

    // 创建测试连接
    $conn = ftp_connect('localhost', 21, 1); // 1秒超时快速失败

    if ($conn) {
        foreach ($test_options as $name => $constant) {
            $value = @ftp_get_option($conn, $constant);
            $supported[$name] = ($value !== false);
        }
        ftp_close($conn);
    }

    return $supported;
}

// 检查PHP版本
echo "PHP版本: " . PHP_VERSION . "\n";
echo "FTP扩展版本: " . (extension_loaded('ftp') ? phpversion('ftp') : '未加载') . "\n\n";

$options = getSupportedFTPOptions();
echo "支持的选项:\n";
foreach ($options as $name => $supported) {
    echo "  {$name}: " . ($supported ? "✓" : "✗") . "\n";
}
?>

选项 对性能的影响 优化建议
FTP_TIMEOUT_SEC 设置过短会导致频繁超时,设置过长会占用连接资源 根据网络状况调整,一般为30-120秒
FTP_AUTOSEEK 启用会增加少量CPU开销,但支持断点续传 大文件传输时启用,小文件传输时禁用
FTP_USEPASVADDRESS 禁用可能导致额外的网络延迟 仅在遇到PASV模式问题时禁用

相关函数

函数 描述
ftp_set_option() 设置FTP连接的运行时选项
ftp_connect() 建立FTP连接
ftp_pasv() 开启或关闭被动模式
error_get_last() 获取最后发生的错误
phpversion() 获取PHP扩展版本
最佳实践:
  • 在修改选项前,先使用ftp_get_option()获取当前值
  • 修改选项后,验证设置是否成功
  • 记录选项变更历史,便于调试和回滚
  • 根据具体应用场景优化选项设置
  • 处理不支持的选项情况,提供合理的默认值
  • 定期检查FTP连接的配置是否符合预期