在x3.4当中,dfsockopen就存在一系列影响使用的问题,而这些问题在x3.5当中变得更加明显。
受条件限制无法在x3.4上搞大刀阔斧的改造,而小修小补解决不了根本问题。
作为Discuz内唯一的,也是插件等应用非常依赖的网络请求组件,它需要拥有更多的能力。
x3.5让这一切变得可能,同时也能扩展更多的可能性。
(本PR未包含UCenter等其余位置的改造,相关改造参见!675:修复 dfopen的socket相关问题(UCenter及安装程序部分) !677:修复 目前系统内存在的部分小Bug以及小需求

曾经的问题:

  1. dfsockopen无法正常连接依赖SNI的https地址(最严重的问题,直接影响可用性)
  2. dfsockopen无法支持ipv6,且在其原有实现机制下强行加入ipv6,会严重影响不支持ipv6的服务器的正常使用。
  3. dfsockopen很难调试查错,遇到问题难以解决
  4. dfsockopen的参数太多了
  5. dfsockopen不支持https证书校验,且无法修改
  6. dfsockopen在非curl模式下的优先级不够合理,最优先使用的模式居然功能最弱
  7. dfsockopen在非curl模式下直接禁用了安全校验,存在SSRF风险
  8. dfsockopen在非curl模式下,multipart格式有bug无法使用
  9. dfsockopen在curl模式下,由于x3.5所依赖的php版本原因,不支持单域名多ip功能
  10. dfsockopen不支持GET和POST以外的方法,也缺少自定义header等重要功能
  11. dfsockopen中存在的各类bug,等等

解决方案:

  • 另起炉灶,新建filesock类
  • filesock类同时兼容curl,socket(fopen,stream_socket_client,fsocketopen),哪怕服务器curl没开,allow_url_fopen也没开,一样能用。Guzzle都不可以这样。
  • 新增SNI兼容相关代码,可以正确连接到依赖SNI的站点了。
  • 重写dns查询和校验部分,针对单服务器多ip情况进行了处理,以支持ipv6。
  • 由于同时支持ipv6和ipv4的站点的存在,增加ip校验功能,让服务器连接校验通过的ip。
  • 为curl和fopen场景添加错误处理功能,解决难以调试的问题。
  • 实现了对get和post以外的协议的支持,新增rawdata功能,可传入其他类型的body数据。
  • 修复了fopen协议会重复发送数据的问题。
  • 修复了socket模式下安全校验无法正常工作的问题。
  • 修复了连接超时和请求超时无法区分的问题。
  • 新增允许用户修改header和ua的功能。
  • 修复了php5.6等低版本下curl表现与文档预期不符的问题。
  • 修复了php新版本不支持@符号上传文件的问题。
  • 修复了curl模式下不兼容socket模式上传表单形式文件的问题。
  • 修复了curl模式下不兼容socket模式limit参数的问题。
  • 修复了socket模式使用formdata上传文件或提交表单时无法被服务器正确识别的问题。

示例(以下写法均可使用):

//和从前完全一样的用法
echo dfsockopen('http://discuz.net');

$fs = filesock::open(['url' => 'http://discuz.net']);
echo $fs->request();

$fs = filesock::open();
echo $fs->get('http://discuz.net');
echo $fs->post('http://discuz.net');
echo $fs->put('http://discuz.net');
echo $fs->delete('http://discuz.net');

//使用post提交表单
$fs = filesock::open();
echo $fs->request(['url' => 'http://discuz.net', 'post' => ['a' => 3]]);
//这么写也可以
$fs = filesock::open();
$fs->url = 'http://discuz.net';
$fs->method = 'POST';
$fs->post = ['a' => 3];
echo $fs->request();

相关讨论

!563:【轻量级 PR】:update upload/source/function/function_filesock.php.


文档

您可以继续使用dfsockopen,它的行为应当与x3.4时期没有明显区别,但底层已经被更换为全新的filesock类。
dfsockopen的功能与以往一样,并不包含filesock的新增功能。
filesock类完整实现了dfsockopen的全部已有功能,并新增了许多功能以满足当今开发的需求。推荐在必要的位置直接使用filesock类以实现更多的功能。
filesock类不依赖curl(但推荐使用它),甚至可以在curl和allow_url_fopen都不支持的服务器上正常工作。

使用方法

配置

config_global.php文件当中与filesock相关的配置项:
$_config['security']['fsockopensafe']['port']
安全请求下允许的端口,启用安全检查的请求只能访问这些端口。
$_config['security']['fsockopensafe']['ipversion']
允许使用的ip协议(数组形式),服务器可以仅配置自己支持的协议,跳过不支持的协议以免请求过程中在上面浪费时间。
$_config['security']['fsockopensafe']['verifypeer']
是否验证证书有效性,开启可提升安全性,但需自行解决证书配置问题。
默认为关闭,开启前请务必确认php内已正确配置根证书列表

需要在php.ini内配置curl.cainfoopenssl.cafile

true为开启(调用php内置的证书),false为关闭。也可以设置为一个路径,若设置为字符串路径,系统会启用证书校验,访问路径对应的证书文件并以此为准。

初始化

使用$fs = filesock::open()即可初始化filesock类。
filesock::open($param)中的$param位置可以传入请求相关的参数。
注意:参数仅需要在任意一处位置传入即可,请勿多次传入。

传入参数

用户可以在初始化和发送请求的过程当中以数组形式传入参数,但也可以单独执行:
$fs->url = 'http://www.discuz.net'即可

发送请求

使用$fs->request()即可发出请求
也可以使用$fs->get()$fs->post()等方式发送请求,可以明确指定请求时所使用的method。
$fs->request($param)中的$param位置可以传入请求相关的参数。

获取内容

$fs->request()运行以后会直接返回返回内容的body部分。
如需获取header部分,请执行$fs->filesockheader。x3.4的$GLOBALS['filesockheader']写法也可以正常使用。

错误处理

如果请求出错,$fs->request()不会返回任何内容。
访问$fs->errno可以获得错误代码,没有错误的话通常是0。
访问$fs->errstr可以获得错误信息描述。
注意:curl和socket模式下的错误代码含义可能存在差异,表现也可能不同。

可用参数列表

如果在初始化和获取阶段传入参数,参数应当为数组形式。
只传入字符串的话,字符串会被当做url处理。
参数也可以直接在初始化后,请求之前直接传入。

  • url 请求的URL地址,必须传入。
  • limit 返回内容的最大长度,默认是0(不限制)
  • method http请求的method,默认自动匹配(有post表单为POST,否则为GET),必须使用大写(如GET)
  • post 传入的post表单,key value格式的数组,默认没有。
  • cookie 传入的cookie,需要自行转换为字符串后再传入。默认为空。
  • ip 指定请求所要连接的服务器ip。
  • conntimeout 连接过程的超时时长(秒),默认为5
  • timeout 连接成功以后的超时时长(秒),默认为15
  • block 和dfsockopen里的一样
  • encodetype 指定一个content type,通常在post/put等使用。可选项有URLENCODE(默认),FORMDATA,JSON,也可以填入任意有效的content type,如multipart/form-dataapplication/json等。
  • position 返回的body的起点位置,默认为0,设置以后会去除开头对应数量的字节。
  • files 欲上传的文件,key value格式的数组,其中value为文件名。注意:若存在与post当中相同的key,则不会尝试按照文件名去查找实际文件,而是将post里对应的value作为文件的实际值进行上传,并继续使用files里的value做上传的文件名。
  • unsafe 是否禁用安全检查。启用安全检查的情况下无法连接非标端口和服务器所在内网,增加了安全性的同时却也会引发某些情况下的不便。强烈建议不要在URL可能是用户输入的情况下禁用这个检查。
  • useragent 允许用户指定一个user agent用于请求。默认使用浏览器useragent。
  • header 数组形式,key value方式传入。请务必保证名称以首字母大写的方式传入,如User-Agent。
  • rawdata post或put需要写入原始数据的情况下使用此值,通常配合JSON使用。
  • verifypeer 是否检查https证书的有效性。通常建议在config里设置而非在此传入,修改前建议先读取此值避免不必要的覆盖。true为启用,false为禁用。也可传入一个路径,指向有效的证书文件,当某些特定业务涉及特定证书的情况下可能会有用。
  • allowcurl 是否允许使用curl发送请求,默认为true。此项参数必须在初始化阶段就传入,初始化以后已经完成了类的加载,再传入就没用了。