PHP 7.3.25 的 FTP 扩展是内置 C 扩展,位于 ext/ftp/,基于 BSD socket 原生实现 RFC 959 双通道(控制+数据连接),不依赖 OpenSSL 或 SSH,故不支持 FTPS 或 SFTP;加密传输需单独启用 openssl 或 ssh2 扩展。

PHP 7.3.25 中的 FTP 扩展 是一个内置(built-in)的 C 扩展,其源码位于 PHP 源码树的 ext/ftp/ 目录下。它不依赖外部 PECL 包,也不通过 `ssh2` 或其他协议实现,而是直接封装 POSIX 级别的 BSD socket API,以原生方式实现 FTP 协议的控制连接与数据连接逻辑。
FTP 扩展如何与 PHP 引擎交互
该扩展遵循标准 Zend 模块生命周期,在 PHP 启动时注册函数、初始化全局资源池(如连接句柄缓存),并在请求结束时自动清理。核心机制包括:
- 所有 FTP 函数(如
ftp_connect()、ftp_login()、ftp_put())均由PHP_FUNCTION(xxx)宏定义,参数通过zend_parse_parameters()解析; - 每个活动 FTP 连接被抽象为一个
php_ftpbuf结构体,内部持有一个int control_fd(控制套接字)和可选的int data_fd(数据套接字); - 连接状态(如是否启用 PASV、当前工作目录、传输模式)保存在结构体内存中,而非依赖服务器响应缓存;
- 错误处理统一映射到 PHP 的
EG(last_error_message)和ftp_errno()/ftp_error()辅助函数。
控制连接与数据连接的双通道实现
FTP 扩展严格遵循 RFC 959 规范,将通信拆分为两个独立 TCP 流:
-
控制连接:由
ftp_connect()创建,使用socket(AF_INET, SOCK_STREAM, 0)建立,全程保持长连接;所有命令(USER/PASS/CWD/LIST)均以 CRLF 结尾,通过send()发送,并用recv()同步读取 3 位状态码响应; -
数据连接:按需建立——主动模式(PORT)由客户端监听临时端口并告知服务端;被动模式(PASV)则解析服务端返回的
227 Entering Passive Mode (a,b,c,d,p1,p2),拼出 IP 和端口号后发起新连接; - 文件上传下载实际走的是这个独立的数据连接,与控制连接完全解耦,因此
ftp_put()内部会先发STOR命令触发服务端准备接收,再切换到数据通道发送二进制流。
底层 socket 行为与平台适配
扩展代码中大量使用条件编译处理跨平台差异:
立即学习“PHP免费学习笔记(深入)”;
- Linux/BSD 上默认启用
SO_KEEPALIVE防连接空闲中断; - Windows 下调用
WSAStartup()初始化 Winsock,并用closesocket()替代close(); - 超时控制通过
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO / SO_SNDTIMEO, ...)设置,而非 PHP 层的stream_set_timeout(); - IPv6 支持通过
getaddrinfo()统一解析主机名,兼容ftp://[::1]/类地址写法。
为什么它不能直接支持 SFTP 或 FTPS
因为 FTP 扩展的设计目标是轻量、标准、无依赖:
- 不链接 OpenSSL 库,故无法实现 FTPS(显式 TLS)的握手与加密通道;
- 不集成 SSH 协议栈,因此与 SFTP(基于 SSH 的子系统)毫无关系——SFTP 不是“FTP over SSH”,而是完全不同的协议;
- 若需加密能力,必须启用独立扩展:如
openssl(用于 FTPS)或ssh2(用于 SFTP),二者与 FTP 扩展互不兼容、不可混用。



















