
本文详解如何使用 PHP 的 FTP 函数安全、可靠地编辑远程服务器上的文本文件,重点解决 file_exists() 误判、ftp_fput() 写入空文件、路径混淆等常见问题,并提供可直接复用的健壮代码方案。
本文详解如何使用 php 的 ftp 函数安全、可靠地编辑远程服务器上的文本文件,重点解决 `file_exists()` 误判、`ftp_fput()` 写入空文件、路径混淆等常见问题,并提供可直接复用的健壮代码方案。
在多站点架构中集中管理白名单(如邮箱域名列表)时,常需通过 PHP 脚本动态更新远程服务器上的共享文本文件(如 email_domain_whitelist.txt)。但许多开发者会陷入一个典型误区:混淆本地文件操作与 FTP 协议语义——例如错误地对 HTTP URL(如 https://example.com/...)调用 file_exists(),或试图用 ftp_fput() 向未正确初始化的文件指针写入数据,最终导致“文件找不到”或“上传空文件”等静默失败。
根本原因在于:
- file_exists() 只能检测本地文件系统路径,无法识别 FTP 或 HTTP URL;
- ftp_fput() 需要一个已打开且可读的本地文件资源(resource),而 fopen($filename, 'r+') 在文件不存在时会失败;若误用未写入内容的句柄,将上传零字节文件;
- ftp_delete() 的参数必须是远程服务器上的相对或绝对路径(不含协议和域名),而非 URL;
- ftp_put() 更适合“上传本地生成的临时文件”,且天然支持覆盖同名文件,无需手动删除旧文件。
✅ 正确做法是:先在本地生成临时文件 → 写入处理后的内容 → 使用 ftp_put() 上传覆盖远程目标文件。以下是优化后的完整实现:
function eri_update_global_whitelist($string, $add_or_remove = 'add') {
// 域名预处理(去空格、转小写、分割)
$domains = array_filter(
array_map('trim', explode(',', strtolower(str_replace(' ', '', $string)))),
function($d) { return strpos($d, '.') !== false; }
);
if (empty($domains)) {
echo '<br>No valid domains to process.';
return;
}
$whitelist_url = 'https://example.com/eri-webtools-plugin/data/email_domain_whitelist.txt';
$remote_path = '/eri-webtools-plugin/data/email_domain_whitelist.txt'; // FTP 路径(无协议!)
$local_file = 'email_domain_whitelist.txt'; // 本地临时文件名(自动存于脚本工作目录)
// 1. 获取并解析现有白名单
$current_content = @file_get_contents($whitelist_url);
$existing_domains = $current_content
? array_filter(array_map('trim', explode(',', strtolower(str_replace(' ', '', $current_content))))),
function($d) { return strpos($d, '.') !== false; }
: [];
// 2. 执行增删逻辑
if ($add_or_remove === 'add') {
$new_domains = array_values(array_unique(array_merge($existing_domains, $domains)));
} else {
$new_domains = array_values(array_diff($existing_domains, $domains));
}
// 3. 生成本地临时文件
$content = implode(', ', $new_domains);
$fp = fopen($local_file, 'w');
if (!$fp) {
echo '<br>Failed to create local temp file.';
return;
}
fwrite($fp, $content);
fclose($fp);
// 4. FTP 上传(覆盖模式)
require_once $_SERVER['DOCUMENT_ROOT'] . '/wp-content/plugins/eri-webtools-plugin/ftp_config.php';
$conn = ftp_connect($ftp_server);
if (!$conn) {
echo '<br>FTP connection failed.';
return;
}
$login = ftp_login($conn, $ftp_username, $ftp_userpass);
if (!$login) {
echo '<br>FTP login failed.';
ftp_close($conn);
return;
}
ftp_pasv($conn, true); // 启用被动模式(推荐)
// ✅ 关键:使用 ftp_put() + 本地文件路径(字符串),非文件指针
if (ftp_put($conn, $remote_path, $local_file, FTP_ASCII)) {
echo '<br>Whitelist updated successfully on remote server.';
} else {
echo '<br>FTP upload failed: ' . ftp_strerror($conn);
}
ftp_close($conn);
// 清理本地临时文件(可选但推荐)
@unlink($local_file);
}⚠️ 重要注意事项:
立即学习“PHP免费学习笔记(深入)”;
- 路径规范:ftp_put() 第二个参数(远程路径)必须是服务器上的路径(如 /path/to/file.txt),不能带 http:// 或 https://;第一个参数(本地路径)是 PHP 进程可访问的绝对或相对路径。
- 权限与安全:确保 FTP 用户对目标目录具有写权限;生产环境建议使用 SFTP(ssh2_sftp())替代明文 FTP;避免在 Web 可访问目录中生成临时文件。
- 错误抑制:@file_get_contents() 和 @unlink() 前加 @ 是为防止警告干扰,实际项目中应结合 error_get_last() 或日志记录做精细化错误处理。
- 原子性保障:当前方案通过覆盖实现“准原子更新”。如需强一致性(如防止中间状态被读取),可先上传为 .tmp 文件,再 ftp_rename() 替换原文件。
总结:远程文件编辑的核心在于严格区分本地资源与远程资源。放弃对 URL 使用 file_exists(),避免 ftp_fput() 与未初始化文件指针的组合,坚定采用 ftp_put() + 本地临时文件的工作流,即可稳定、高效地完成跨站点配置同步任务。










