PHP ftp_cdup() 函数

注意:ftp_cdup() 函数需要启用FTP扩展。在使用前请确保已安装并启用FTP扩展。

定义和用法

ftp_cdup() 函数用于在FTP服务器上切换到当前目录的父目录。

这个函数是FTP协议中的 CDUP 命令的PHP实现,它相当于执行 ftp_chdir("..") 命令。

  • 功能等效ftp_cdup() 等同于 ftp_chdir("..")
  • 目录导航:用于在FTP目录结构中向上移动一层
  • 标准FTP命令:这是标准的FTP协议命令,几乎所有FTP服务器都支持

语法

ftp_cdup(resource $ftp_stream): bool

参数值

参数 描述
$ftp_stream FTP连接资源。由 ftp_connect()ftp_ssl_connect() 函数返回的连接标识符。

返回值

  • 成功时返回 true
  • 失败时返回 false
注意:如果已经在根目录,尝试切换到父目录可能会失败。

与 ftp_chdir() 的比较

方面 ftp_cdup() ftp_chdir()
功能 切换到父目录 切换到指定目录
参数 只需要FTP连接资源 需要FTP连接资源和目录路径
等效操作 ftp_chdir("..") 无等效操作
使用场景 简单向上导航一层 导航到任意目录
灵活性 较低(只能向上) 较高(可到任意目录)

示例

示例 1:基本用法 - 切换到父目录

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

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

// 登录到FTP服务器
if (@ftp_login($conn_id, $ftp_user, $ftp_pass)) {
    echo "已连接到 $ftp_server,用户为 $ftp_user<br>";
} else {
    echo "无法以 $ftp_user 身份登录<br>";
    ftp_close($conn_id);
    exit;
}

// 启用被动模式
ftp_pasv($conn_id, true);

// 获取当前目录
$current_dir = ftp_pwd($conn_id);
echo "当前目录: " . htmlspecialchars($current_dir) . "<br>";

// 切换到子目录
if (ftp_chdir($conn_id, "public_html")) {
    echo "已切换到 public_html 目录<br>";
    $current_dir = ftp_pwd($conn_id);
    echo "当前目录: " . htmlspecialchars($current_dir) . "<br>";

    // 使用 ftp_cdup() 切换到父目录
    if (ftp_cdup($conn_id)) {
        echo "已切换到父目录<br>";
        $current_dir = ftp_pwd($conn_id);
        echo "当前目录: " . htmlspecialchars($current_dir) . "<br>";
    } else {
        echo "无法切换到父目录<br>";
    }
} else {
    echo "无法切换到 public_html 目录<br>";
}

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

示例 2:封装导航功能

<?php
/**
 * FTP目录导航类
 */
class FTPNavigator {
    private $conn_id;
    private $history = [];

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

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

        ftp_pasv($this->conn_id, true);
        $this->recordCurrentDirectory();
    }

    /**
     * 记录当前目录到历史
     */
    private function recordCurrentDirectory() {
        $current_dir = ftp_pwd($this->conn_id);
        $this->history[] = $current_dir;

        // 只保留最近20条历史记录
        if (count($this->history) > 20) {
            array_shift($this->history);
        }
    }

    /**
     * 获取当前目录
     */
    public function getCurrentDirectory() {
        return ftp_pwd($this->conn_id);
    }

    /**
     * 切换到父目录
     */
    public function goToParent() {
        if (ftp_cdup($this->conn_id)) {
            $this->recordCurrentDirectory();
            return true;
        }
        return false;
    }

    /**
     * 切换到指定目录
     */
    public function changeDirectory($directory) {
        if (ftp_chdir($this->conn_id, $directory)) {
            $this->recordCurrentDirectory();
            return true;
        }
        return false;
    }

    /**
     * 返回到上一级目录
     * 如果已经是最上级,返回false
     */
    public function goUp($levels = 1) {
        if ($levels < 1) {
            return true;
        }

        $success = true;
        for ($i = 0; $i < $levels; $i++) {
            if (!ftp_cdup($this->conn_id)) {
                $success = false;
                break;
            }
        }

        if ($success) {
            $this->recordCurrentDirectory();
        }

        return $success;
    }

    /**
     * 返回历史目录
     */
    public function goBack($steps = 1) {
        if ($steps <= 0 || $steps >= count($this->history)) {
            return false;
        }

        $target_index = count($this->history) - $steps - 1;
        $target_dir = $this->history[$target_index];

        return $this->changeDirectory($target_dir);
    }

    /**
     * 获取目录历史
     */
    public function getHistory() {
        return $this->history;
    }

    /**
     * 列出当前目录内容
     */
    public function listDirectory($directory = null) {
        $current_dir = $this->getCurrentDirectory();

        if ($directory !== null) {
            if (!$this->changeDirectory($directory)) {
                return false;
            }
        }

        $files = ftp_nlist($this->conn_id, ".");

        // 恢复原始目录
        if ($directory !== null) {
            $this->changeDirectory($current_dir);
        }

        return $files;
    }

    /**
     * 关闭连接
     */
    public function close() {
        if ($this->conn_id) {
            ftp_close($this->conn_id);
            $this->conn_id = null;
        }
    }

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

// 使用示例
echo "<h4>FTP目录导航示例:</h4>";

try {
    // 创建导航器实例
    $navigator = new FTPNavigator(
        "ftp.example.com",
        "username",
        "password"
    );

    echo "当前目录: " . htmlspecialchars($navigator->getCurrentDirectory()) . "<br>";

    // 切换到子目录
    if ($navigator->changeDirectory("public_html")) {
        echo "切换到: public_html<br>";
        echo "当前目录: " . htmlspecialchars($navigator->getCurrentDirectory()) . "<br>";

        // 使用 goToParent() 切换到父目录
        if ($navigator->goToParent()) {
            echo "返回到父目录<br>";
            echo "当前目录: " . htmlspecialchars($navigator->getCurrentDirectory()) . "<br>";
        }

        // 切换到更深目录
        $navigator->changeDirectory("public_html/images");
        echo "切换到: public_html/images<br>";

        // 向上移动2级
        if ($navigator->goUp(2)) {
            echo "向上移动2级<br>";
            echo "当前目录: " . htmlspecialchars($navigator->getCurrentDirectory()) . "<br>";
        }

        // 显示历史记录
        echo "<br><strong>目录历史:</strong><br>";
        $history = $navigator->getHistory();
        foreach ($history as $index => $dir) {
            echo ($index + 1) . ". " . htmlspecialchars($dir) . "<br>";
        }

        // 列出当前目录内容
        echo "<br><strong>目录列表:</strong><br>";
        $files = $navigator->listDirectory();
        if ($files !== false) {
            foreach ($files as $file) {
                echo htmlspecialchars(basename($file)) . "<br>";
            }
        }
    }

    // 自动关闭连接(析构函数会处理)

} catch (Exception $e) {
    echo "<div class='alert alert-danger'>错误: " . htmlspecialchars($e->getMessage()) . "</div>";
}
?>

示例 3:实现递归目录操作

<?php
/**
 * FTP递归目录操作类
 */
class FTPRecursiveOperations {
    private $conn_id;

    public function __construct($conn_id) {
        $this->conn_id = $conn_id;
    }

    /**
     * 递归创建目录
     */
    public function createDirectoryRecursive($remote_path) {
        $remote_path = rtrim($remote_path, '/');
        $parts = explode('/', $remote_path);
        $current_path = '';

        foreach ($parts as $part) {
            if (empty($part)) continue;

            $current_path .= '/' . $part;

            // 尝试切换到目录,如果失败则创建
            if (!@ftp_chdir($this->conn_id, $current_path)) {
                if (!ftp_mkdir($this->conn_id, $current_path)) {
                    return false;
                }
                ftp_chdir($this->conn_id, $current_path);
            }
        }

        return true;
    }

    /**
     * 递归删除目录
     */
    public function deleteDirectoryRecursive($directory) {
        // 保存当前目录
        $original_dir = ftp_pwd($this->conn_id);

        // 切换到目标目录
        if (!ftp_chdir($this->conn_id, $directory)) {
            return false;
        }

        // 获取目录列表
        $files = ftp_nlist($this->conn_id, ".");

        foreach ($files as $file) {
            // 跳过 . 和 ..
            if ($file == '.' || $file == '..') {
                continue;
            }

            // 检查是否是目录
            if ($this->isDirectory($file)) {
                // 递归删除子目录
                if (!$this->deleteDirectoryRecursive($file)) {
                    ftp_chdir($this->conn_id, $original_dir);
                    return false;
                }
            } else {
                // 删除文件
                if (!ftp_delete($this->conn_id, $file)) {
                    ftp_chdir($this->conn_id, $original_dir);
                    return false;
                }
            }
        }

        // 回到父目录
        ftp_cdup($this->conn_id);

        // 删除空目录
        if (!ftp_rmdir($this->conn_id, $directory)) {
            ftp_chdir($this->conn_id, $original_dir);
            return false;
        }

        // 恢复原始目录
        ftp_chdir($this->conn_id, $original_dir);
        return true;
    }

    /**
     * 递归上传目录
     */
    public function uploadDirectoryRecursive($local_dir, $remote_dir) {
        if (!is_dir($local_dir)) {
            return false;
        }

        // 在远程创建目录
        if (!$this->createDirectoryRecursive($remote_dir)) {
            return false;
        }

        // 保存当前目录
        $original_remote_dir = ftp_pwd($this->conn_id);

        // 切换到远程目录
        if (!ftp_chdir($this->conn_id, $remote_dir)) {
            return false;
        }

        // 处理本地目录内容
        $items = scandir($local_dir);
        $success = true;

        foreach ($items as $item) {
            if ($item == '.' || $item == '..') {
                continue;
            }

            $local_path = $local_dir . '/' . $item;
            $remote_path = $item;

            if (is_dir($local_path)) {
                // 递归上传子目录
                if (!$this->uploadDirectoryRecursive($local_path, $remote_path)) {
                    $success = false;
                    break;
                }
            } else {
                // 上传文件
                if (!ftp_put($this->conn_id, $remote_path, $local_path, FTP_BINARY)) {
                    $success = false;
                    break;
                }
            }
        }

        // 回到原始目录
        ftp_chdir($this->conn_id, $original_remote_dir);
        return $success;
    }

    /**
     * 检查是否为目录
     */
    private function isDirectory($path) {
        $original_dir = ftp_pwd($this->conn_id);

        // 尝试切换到路径
        if (@ftp_chdir($this->conn_id, $path)) {
            // 如果是目录,切换回父目录
            ftp_cdup($this->conn_id);
            ftp_chdir($this->conn_id, $original_dir);
            return true;
        }

        ftp_chdir($this->conn_id, $original_dir);
        return false;
    }

    /**
     * 递归列出目录结构
     */
    public function listDirectoryRecursive($directory = ".", $indent = "") {
        $files = ftp_nlist($this->conn_id, $directory);
        $result = [];

        foreach ($files as $file) {
            if ($file == '.' || $file == '..') {
                continue;
            }

            $full_path = ($directory == ".") ? $file : $directory . '/' . $file;
            $relative_path = basename($full_path);

            $result[] = $indent . $relative_path;

            if ($this->isDirectory($full_path)) {
                // 递归列出子目录
                $sub_result = $this->listDirectoryRecursive($full_path, $indent . "  ");
                $result = array_merge($result, $sub_result);
            }
        }

        return $result;
    }
}

// 使用示例
echo "<h4>FTP递归目录操作示例:</h4>";

// 假设已经建立了FTP连接
$ftp_server = "ftp.example.com";
$ftp_user = "username";
$ftp_pass = "password";

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

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

ftp_pasv($conn_id, true);

// 创建递归操作实例
$ftp_ops = new FTPRecursiveOperations($conn_id);

echo "当前目录: " . htmlspecialchars(ftp_pwd($conn_id)) . "<br><br>";

// 示例1:递归列出目录
echo "<strong>递归目录列表:</strong><br>";
$dir_list = $ftp_ops->listDirectoryRecursive();
foreach ($dir_list as $item) {
    echo htmlspecialchars($item) . "<br>";
}

echo "<br>";

// 示例2:创建嵌套目录
echo "<strong>创建嵌套目录:</strong><br>";
if ($ftp_ops->createDirectoryRecursive("/test/nested/directory")) {
    echo "成功创建目录结构: /test/nested/directory<br>";
} else {
    echo "创建目录失败<br>";
}

// 示例3:使用 ftp_cdup() 在递归操作中
echo "<br><strong>演示 ftp_cdup() 在递归中的使用:</strong><br>";

// 先进入一个深层次目录
ftp_chdir($conn_id, "/test/nested/directory");
echo "当前目录: " . htmlspecialchars(ftp_pwd($conn_id)) . "<br>";

// 使用 ftp_cdup() 返回上级
ftp_cdup($conn_id);
echo "切换到父目录后: " . htmlspecialchars(ftp_pwd($conn_id)) . "<br>";

// 再次使用 ftp_cdup()
ftp_cdup($conn_id);
echo "再次切换到父目录: " . htmlspecialchars(ftp_pwd($conn_id)) . "<br>";

// 清理:删除测试目录
echo "<br><strong>清理测试目录:</strong><br>";
if ($ftp_ops->deleteDirectoryRecursive("/test")) {
    echo "成功删除 /test 目录及其所有内容<br>";
} else {
    echo "删除目录失败<br>";
}

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

示例 4:实现FTP文件浏览器

<?php
/**
 * 简单的FTP文件浏览器
 */
class FTPFileBrowser {
    private $conn_id;
    private $current_path;

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

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

        ftp_pasv($this->conn_id, true);
        $this->current_path = ftp_pwd($this->conn_id);
    }

    /**
     * 获取当前路径
     */
    public function getCurrentPath() {
        return $this->current_path;
    }

    /**
     * 导航到父目录
     */
    public function goToParent() {
        if (ftp_cdup($this->conn_id)) {
            $this->current_path = ftp_pwd($this->conn_id);
            return true;
        }
        return false;
    }

    /**
     * 导航到指定目录
     */
    public function goToDirectory($directory) {
        // 处理相对路径
        if ($directory == "..") {
            return $this->goToParent();
        }

        if (ftp_chdir($this->conn_id, $directory)) {
            $this->current_path = ftp_pwd($this->conn_id);
            return true;
        }
        return false;
    }

    /**
     * 获取当前目录内容
     */
    public function getDirectoryContents() {
        $contents = [];
        $raw_list = ftp_rawlist($this->conn_id, ".");

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

        foreach ($raw_list as $line) {
            $item = $this->parseRawListLine($line);
            if ($item !== null) {
                $contents[] = $item;
            }
        }

        // 按类型和名称排序
        usort($contents, function($a, $b) {
            if ($a['type'] !== $b['type']) {
                return ($a['type'] === 'dir') ? -1 : 1;
            }
            return strcasecmp($a['name'], $b['name']);
        });

        return $contents;
    }

    /**
     * 解析 rawlist 输出行
     */
    private function parseRawListLine($line) {
        // Unix/Linux 格式解析
        if (preg_match('/^([-d])([-rwxs]{9})\s+(\d+)\s+(\S+)\s+(\S+)\s+(\d+)\s+(\w{3}\s+\d+\s+[\d:]+)\s+(.+)$/', $line, $matches)) {
            $type = ($matches[1] === 'd') ? 'dir' : 'file';
            $permissions = $matches[2];
            $size = (int)$matches[6];
            $date = $matches[7];
            $name = $matches[8];

            return [
                'type' => $type,
                'name' => $name,
                'size' => $size,
                'permissions' => $permissions,
                'date' => $date,
                'is_dir' => ($type === 'dir')
            ];
        }

        // Windows 格式解析
        elseif (preg_match('/^(\d{2}-\d{2}-\d{2}\s+\d{2}:\d{2}[AP]M)\s+(<DIR>|\d+)\s+(.+)$/', $line, $matches)) {
            $date = $matches[1];
            $size_or_dir = $matches[2];
            $name = $matches[3];

            if ($size_or_dir === '<DIR>') {
                $type = 'dir';
                $size = 0;
            } else {
                $type = 'file';
                $size = (int)$size_or_dir;
            }

            return [
                'type' => $type,
                'name' => $name,
                'size' => $size,
                'permissions' => '',
                'date' => $date,
                'is_dir' => ($type === 'dir')
            ];
        }

        return null;
    }

    /**
     * 获取文件大小的人类可读格式
     */
    private 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];
    }

    /**
     * 渲染文件浏览器界面
     */
    public function renderBrowser() {
        $contents = $this->getDirectoryContents();

        echo "<div class='ftp-browser'>";
        echo "<div class='ftp-path mb-3'>";
        echo "<strong>当前路径:</strong> " . htmlspecialchars($this->current_path);
        echo "</div>";

        // 父目录链接(如果不是根目录)
        if ($this->current_path !== '/') {
            echo "<div class='ftp-item mb-2'>";
            echo "<a href='?action=parent' class='text-decoration-none'>";
            echo "<i class='fas fa-level-up-alt'></i> 返回上级目录";
            echo "</a>";
            echo "</div>";
        }

        // 目录内容
        if (empty($contents)) {
            echo "<div class='alert alert-info'>目录为空</div>";
        } else {
            echo "<table class='table table-sm table-hover'>";
            echo "<thead><tr><th>名称</th><th>类型</th><th>大小</th><th>修改时间</th></tr></thead>";
            echo "<tbody>";

            foreach ($contents as $item) {
                $icon = $item['is_dir'] ? '<i class="fas fa-folder text-warning"></i>' :
                                         '<i class="fas fa-file text-secondary"></i>';

                $type = $item['is_dir'] ? '目录' : '文件';
                $size = $item['is_dir'] ? '-' : $this->formatSize($item['size']);

                echo "<tr>";
                echo "<td>$icon ";

                if ($item['is_dir']) {
                    echo "<a href='?action=enter&dir=" . urlencode($item['name']) . "' class='text-decoration-none'>";
                    echo htmlspecialchars($item['name']);
                    echo "</a>";
                } else {
                    echo htmlspecialchars($item['name']);
                }

                echo "</td>";
                echo "<td>$type</td>";
                echo "<td>$size</td>";
                echo "<td>" . htmlspecialchars($item['date']) . "</td>";
                echo "</tr>";
            }

            echo "</tbody></table>";
        }

        echo "</div>";
    }

    /**
     * 处理用户操作
     */
    public function handleRequest() {
        if (isset($_GET['action'])) {
            switch ($_GET['action']) {
                case 'parent':
                    $this->goToParent();
                    break;
                case 'enter':
                    if (isset($_GET['dir'])) {
                        $this->goToDirectory($_GET['dir']);
                    }
                    break;
            }
        }
    }

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

// 使用示例
echo "<h4>FTP文件浏览器示例:</h4>";

// 在实际应用中,这里应该从表单或配置获取FTP信息
$ftp_config = [
    'server' => 'ftp.example.com',
    'username' => 'username',
    'password' => 'password'
];

try {
    // 创建浏览器实例
    $browser = new FTPFileBrowser(
        $ftp_config['server'],
        $ftp_config['username'],
        $ftp_config['password']
    );

    // 处理用户操作
    $browser->handleRequest();

    // 渲染浏览器界面
    $browser->renderBrowser();

    // 显示一些统计信息
    echo "<div class='mt-3 alert alert-secondary'>";
    echo "<strong>FTP服务器信息:</strong><br>";
    echo "服务器: " . htmlspecialchars($ftp_config['server']) . "<br>";
    echo "当前路径: " . htmlspecialchars($browser->getCurrentPath());
    echo "</div>";

    // 关闭连接
    $browser->close();

} catch (Exception $e) {
    echo "<div class='alert alert-danger'>错误: " . htmlspecialchars($e->getMessage()) . "</div>";
}
?>

示例 5:错误处理和边界情况

<?php
/**
 * 安全的FTP目录操作函数
 */
class SafeFTPOperations {
    private $conn_id;

    public function __construct($conn_id) {
        $this->conn_id = $conn_id;
    }

    /**
     * 安全的切换到父目录
     */
    public function safeCdup() {
        // 获取当前目录
        $current_dir = @ftp_pwd($this->conn_id);
        if ($current_dir === false) {
            return [
                'success' => false,
                'error' => '无法获取当前目录',
                'code' => 'PWD_FAILED'
            ];
        }

        // 检查是否已经在根目录
        if ($current_dir === '/' || $current_dir === '\\') {
            return [
                'success' => false,
                'error' => '已经在根目录,无法切换到父目录',
                'code' => 'ALREADY_AT_ROOT',
                'current_dir' => $current_dir
            ];
        }

        // 尝试切换到父目录
        if (!@ftp_cdup($this->conn_id)) {
            return [
                'success' => false,
                'error' => '切换到父目录失败',
                'code' => 'CDUP_FAILED',
                'current_dir' => $current_dir
            ];
        }

        // 验证切换是否成功
        $new_dir = @ftp_pwd($this->conn_id);
        if ($new_dir === false) {
            return [
                'success' => false,
                'error' => '切换后无法验证目录',
                'code' => 'VERIFICATION_FAILED'
            ];
        }

        // 确保确实切换到了父目录
        $parent_of_current = dirname($current_dir);
        if ($parent_of_current === '.') {
            $parent_of_current = '/';
        }

        if ($new_dir !== $parent_of_current) {
            return [
                'success' => false,
                'error' => '目录切换验证失败',
                'code' => 'VERIFICATION_MISMATCH',
                'expected' => $parent_of_current,
                'actual' => $new_dir
            ];
        }

        return [
            'success' => true,
            'previous_dir' => $current_dir,
            'current_dir' => $new_dir,
            'message' => '成功切换到父目录'
        ];
    }

    /**
     * 安全地向上导航多级
     */
    public function safeNavigateUp($levels = 1) {
        if ($levels < 1) {
            return [
                'success' => true,
                'message' => '无需导航',
                'levels' => 0
            ];
        }

        $results = [];
        $success_count = 0;

        for ($i = 1; $i <= $levels; $i++) {
            $result = $this->safeCdup();
            $results[$i] = $result;

            if ($result['success']) {
                $success_count++;
            } else {
                // 如果某个级别失败,停止继续尝试
                break;
            }
        }

        return [
            'success' => ($success_count == $levels),
            'levels_attempted' => $levels,
            'levels_succeeded' => $success_count,
            'results' => $results,
            'final_dir' => ftp_pwd($this->conn_id)
        ];
    }

    /**
     * 测试FTP连接和功能
     */
    public function testConnection() {
        $tests = [];
        $all_passed = true;

        // 测试1:获取当前目录
        $current_dir = @ftp_pwd($this->conn_id);
        if ($current_dir === false) {
            $tests[] = ['test' => 'PWD命令', 'result' => '失败', 'error' => '无法获取当前目录'];
            $all_passed = false;
        } else {
            $tests[] = ['test' => 'PWD命令', 'result' => '通过', 'value' => $current_dir];
        }

        // 测试2:切换到存在的子目录(如果有)
        $test_dir = "test_directory_" . time();
        if (@ftp_mkdir($this->conn_id, $test_dir)) {
            if (@ftp_chdir($this->conn_id, $test_dir)) {
                $tests[] = ['test' => '切换到子目录', 'result' => '通过'];

                // 测试3:使用 ftp_cdup() 返回
                if (@ftp_cdup($this->conn_id)) {
                    $tests[] = ['test' => '使用CDUP返回父目录', 'result' => '通过'];
                } else {
                    $tests[] = ['test' => '使用CDUP返回父目录', 'result' => '失败'];
                    $all_passed = false;
                }

                // 清理测试目录
                @ftp_chdir($this->conn_id, "..");
                @ftp_rmdir($this->conn_id, $test_dir);
            } else {
                $tests[] = ['test' => '切换到子目录', 'result' => '失败'];
                $all_passed = false;
            }
        } else {
            $tests[] = ['test' => '创建测试目录', 'result' => '跳过', 'note' => '可能没有创建权限'];
        }

        // 测试4:在根目录测试 ftp_cdup()
        // 先尝试回到根目录
        while (true) {
            $dir = ftp_pwd($this->conn_id);
            if ($dir === '/' || $dir === false) {
                break;
            }
            if (!@ftp_cdup($this->conn_id)) {
                break;
            }
        }

        // 现在在根目录测试 ftp_cdup()
        $result = @ftp_cdup($this->conn_id);
        if ($result === false) {
            $tests[] = ['test' => '根目录CDUP', 'result' => '通过', 'note' => '在根目录调用CDUP返回false,符合预期'];
        } else {
            $tests[] = ['test' => '根目录CDUP', 'result' => '警告', 'note' => '在根目录调用CDUP没有返回false'];
        }

        return [
            'all_passed' => $all_passed,
            'tests' => $tests,
            'server_type' => @ftp_systype($this->conn_id)
        ];
    }
}

// 使用示例
echo "<h4>FTP错误处理和边界情况测试:</h4>";

// 模拟FTP连接
$ftp_server = "ftp.example.com";
$ftp_user = "username";
$ftp_pass = "password";

$conn_id = @ftp_connect($ftp_server);
if (!$conn_id) {
    echo "<div class='alert alert-danger'>无法连接到FTP服务器</div>";
} else {
    if (!@ftp_login($conn_id, $ftp_user, $ftp_pass)) {
        echo "<div class='alert alert-danger'>FTP登录失败</div>";
        ftp_close($conn_id);
    } else {
        ftp_pasv($conn_id, true);

        // 创建安全操作实例
        $safe_ops = new SafeFTPOperations($conn_id);

        echo "<h5>1. 连接功能测试:</h5>";
        $test_results = $safe_ops->testConnection();

        echo "<table class='table table-sm'>";
        echo "<thead><tr><th>测试项目</th><th>结果</th><th>详细信息</th></tr></thead>";
        echo "<tbody>";

        foreach ($test_results['tests'] as $test) {
            $result_class = '';
            switch ($test['result']) {
                case '通过': $result_class = 'table-success'; break;
                case '失败': $result_class = 'table-danger'; break;
                case '警告': $result_class = 'table-warning'; break;
                default: $result_class = 'table-info';
            }

            echo "<tr class='$result_class'>";
            echo "<td>" . htmlspecialchars($test['test']) . "</td>";
            echo "<td>" . htmlspecialchars($test['result']) . "</td>";
            echo "<td>";
            if (isset($test['error'])) {
                echo htmlspecialchars($test['error']);
            } elseif (isset($test['value'])) {
                echo htmlspecialchars($test['value']);
            } elseif (isset($test['note'])) {
                echo htmlspecialchars($test['note']);
            }
            echo "</td>";
            echo "</tr>";
        }

        echo "</tbody></table>";

        echo "<div class='alert " . ($test_results['all_passed'] ? 'alert-success' : 'alert-warning') . "'>";
        echo "服务器类型: " . htmlspecialchars($test_results['server_type'] ?? '未知') . "<br>";
        echo "所有测试: " . ($test_results['all_passed'] ? '通过' : '部分失败');
        echo "</div>";

        echo "<h5>2. 安全的父目录切换测试:</h5>";

        // 测试安全切换到父目录
        $result = $safe_ops->safeCdup();

        echo "<div class='alert " . ($result['success'] ? 'alert-success' : 'alert-danger') . "'>";
        echo "<strong>" . ($result['success'] ? '成功' : '失败') . "</strong><br>";
        echo "信息: " . htmlspecialchars($result['message'] ?? $result['error']) . "<br>";

        if (isset($result['previous_dir'])) {
            echo "原目录: " . htmlspecialchars($result['previous_dir']) . "<br>";
        }
        if (isset($result['current_dir'])) {
            echo "现目录: " . htmlspecialchars($result['current_dir']) . "<br>";
        }

        if (isset($result['code'])) {
            echo "错误代码: " . htmlspecialchars($result['code']) . "<br>";
        }

        echo "</div>";

        echo "<h5>3. 多级向上导航测试:</h5>";

        // 先进入一个深层次目录以便测试
        $test_path = "/level1/level2/level3";
        @ftp_mkdir($conn_id, "level1");
        @ftp_chdir($conn_id, "level1");
        @ftp_mkdir($conn_id, "level2");
        @ftp_chdir($conn_id, "level2");
        @ftp_mkdir($conn_id, "level3");
        @ftp_chdir($conn_id, "level3");

        echo "当前目录: " . htmlspecialchars(ftp_pwd($conn_id)) . "<br>";

        // 向上导航2级
        $nav_result = $safe_ops->safeNavigateUp(2);

        echo "<div class='alert " . ($nav_result['success'] ? 'alert-success' : 'alert-warning') . "'>";
        echo "尝试导航: " . $nav_result['levels_attempted'] . " 级<br>";
        echo "成功导航: " . $nav_result['levels_succeeded'] . " 级<br>";
        echo "最终目录: " . htmlspecialchars($nav_result['final_dir']) . "<br>";
        echo "</div>";

        // 清理测试目录
        @ftp_chdir($conn_id, "/");
        @ftp_rmdir($conn_id, "level1/level2/level3");
        @ftp_rmdir($conn_id, "level1/level2");
        @ftp_rmdir($conn_id, "level1");

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

注意事项

  • 根目录限制:如果在根目录调用 ftp_cdup(),大多数FTP服务器会返回错误
  • 权限问题:需要对该目录有读取权限才能成功切换
  • 连接状态:必须在有效的FTP连接上调用此函数
  • 错误处理:应检查函数返回值,处理可能的失败情况
  • 跨平台差异:不同操作系统的FTP服务器可能有不同的路径分隔符
  • 替代方案ftp_cdup() 等价于 ftp_chdir("..")
  • 性能:频繁的目录切换可能影响性能,应考虑批量操作

相关FTP函数

函数 描述
ftp_chdir() 改变FTP服务器上的当前目录
ftp_pwd() 返回当前目录名
ftp_mkdir() 在FTP服务器上建立新目录
ftp_rmdir() 删除FTP服务器上的目录
ftp_nlist() 返回指定目录的文件列表
ftp_rawlist() 返回指定目录的详细列表
ftp_size() 返回指定文件的大小

最佳实践

  1. 错误处理:始终检查 ftp_cdup() 的返回值
  2. 状态验证:切换目录后,使用 ftp_pwd() 验证当前目录
  3. 路径处理:使用适当的方法处理相对路径和绝对路径
  4. 连接管理:确保在有效的FTP连接上操作
  5. 用户反馈:为用户提供清晰的目录导航反馈
  6. 性能优化:避免不必要的目录切换操作
  7. 日志记录:记录重要的目录操作,便于调试
  8. 权限检查:在切换目录前检查用户权限

浏览器支持

该函数是 PHP 后端函数,与浏览器无关。需要 PHP 4.0 或更高版本,且启用 FTP 扩展。