PHP file_get_contents获取https内容失败
前言
PHP用file_get_contents获取http内容没问题,换成https就报错了?这是最常见的问题,一般就是openssl扩展没开,或者SSL证书验证没过。本文讲怎么解决。
一、问题现象
<?php
$url = 'https://api.example.com/data';
$result = file_get_contents($url);
echo $result;
结果报错:
Warning: file_get_contents(): failed to open stream: Unable to find the wrapper "https"
或者:
Warning: file_get_contents(): SSL operation failed with code 1
二、常见原因
原因1:openssl扩展没装
PHP默认不开openssl扩展,file_get_contents不支持https。
原因2:allow_url_open没开
php.ini里禁了远程打开。
原因3:SSL证书验证失败
对方网站证书有问题,PHP验证不通过。
原因4:网络问题
服务器出不去,或者DNS解析不了。
三、排查步骤
第一步:看openssl扩展装了没
php -m | grep openssl
有输出就是装了,没输出就是没装。
或者写个phpinfo:
<?php
phpinfo();
搜"openssl",看有没有。
第二步:看allow_url_open开了没
<?php
echo ini_get('allow_url_fopen');
输出1就是开了,空就是没开。
第三步:用curl测试
curl https://api.example.com/data
curl能通就是PHP的问题,curl也不通就是网络问题。
四、解决方法
方法1:装openssl扩展(推荐)
CentOS
yum install php-openssl
或者:
yum install php7x-openssl
(x是你的PHP版本)
Ubuntu
apt install php-openssl
装完重启
systemctl restart php-fpm
方法2:改php.ini配置
打开php.ini,找到:
;extension=openssl
去掉前面的分号:
extension=openssl
然后重启php-fpm。
方法3:关掉SSL验证(临时)
不想改配置,可以在代码里关验证:
<?php
$context = stream_context_create([
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false
]
]);
$result = file_get_contents('https://api.example.com/data', false, $context);
echo $result;
注意: 这是临时方案,生产环境建议把证书配好。
方法4:改用curl(推荐)
file_get_contents不好用,直接用curl:
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/data');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo '错误: ' . curl_error($ch);
}
curl_close($ch);
echo $result;
curl比file_get_contents好用多了,推荐用curl。
五、常见坑
坑1:CLI和FPM配置不一样
命令行里openssl装了,网页上还是不行。
原因: CLI和FPM用不同的php.ini。
解决: 看phpinfo里的配置路径,改对应的那个。
坑2:装了扩展没重启
装完忘了重启php-fpm。
解决:
systemctl restart php-fpm
坑3:对方网站证书过期
对方网站SSL证书过期了,PHP验证不通过。
解决: 关验证,或者让对方更新证书。
坑4:allow_url_fopen没开
php.ini里禁了远程打开。
解决:
allow_url_fopen = On
坑5:防火墙挡了出站
服务器防火墙挡了443端口出站。
解决:
# 看能不能出去
curl -I https://www.baidu.com
# 不行的话开防火墙
firewall-cmd --add-port=443/tcp --permanent
firewall-cmd --reload
六、POST请求示例
file_get_contents发POST:
<?php
$url = 'https://api.example.com/submit';
$data = http_build_query([
'name' => '张三',
'age' => 20
]);
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'content' => $data,
'timeout' => 30
],
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false
]
]);
$result = file_get_contents($url, false, $context);
echo $result;
还是建议用curl,更灵活。
七、封装函数
function http_get($url, $timeout = 30)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
$result = curl_exec($ch);
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
return ['code' => -1, 'msg' => $error];
}
curl_close($ch);
return ['code' => 0, 'data' => $result];
}
// 使用
$res = http_get('https://api.example.com/data');
if ($res['code'] == 0) {
echo $res['data'];
} else {
echo '请求失败: ' . $res['msg'];
}
总结
记住:
- file_get_contents不支持https,是openssl扩展没装
- 装openssl扩展,重启php-fpm
- 或者关SSL验证临时解决
- 最推荐:改用curl,比file_get_contents好用多了
推荐方案:
直接用curl,别用file_get_contents搞https。
遇到问题加QQ23979811 协助处理