解决PHP里fopen不能解析域名

背景

工作关系,需要将大部分服务器迁移到Amazon的云服务上,操作系统试用的是Amazon Linux,bash下使用wget和curl均正常。之后编译安装了PHP,步骤按照此文 进行

问题描述

安装成功后,发现fopen(),file_get_contents(),getimagesize() 等都不能正常获得网络上的内容,具体表现为凡参数是URL的,一律返回空值,error log没有任何warning或notice。而直接在PHP中使用curl系函数则正常。然而,如果是直接指定IP地址,如file_get_contents(‘http://127.0.0.1'),则工作正常。 试用strace来跟踪,发现带域名的URL在socket打开后马上关闭,而直接用IP地址的话socket后会使用connect来链接。

解决过程

首先排除了DNS的问题,因为除了这几个函数,其他一切工作正常。虽然是带域名的URL才有问题,但gethostbyname() 这个函数却可以得到正确返回。 然后想到的是php.ini 的配置问题——但发现allow_url_fopen 已经打开。 之后寻求Google帮忙,有人提及是SELINUX的问题。可我压根没有打开SELINUX。继续Google之,发现了StackOverflow的这篇

文章,里面提及的情况跟我完全一样。里面提及编译PHP的时候去除libcurl,但我傻傻的以为是将libcurl去除,还上网搜索怎样做,事后证明又失败了。 一轮搜索后无果,只能想活人求助,于是到V2EX发帖求助。最后,有人指出此是PHP的Bug。对于stackoverflow的这个帖子,给了我一个新思路——用不同的关键字搜索。他的问题描述与我一模一样,但标题却完全不同。最后,我以php curlwrapper搜到了最后的解决方案——『PHP –with-curlwrappers 导致的问题』。正如文章描述,做测试的时候wrapper-type是curl,而不是http

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
$file = fopen('http://www.google.com/', 'rb');
var_dump(stream_get_meta_data($file));
/*
输出结果:
array(10) {
['wrapper_data']=>
array(2) {
['headers']=>
array(0) {
}
['readbuf']=>
resource(38) of type (stream)
}
['wrapper_type']=>
string(4) 'cURL'
['stream_type']=>
string(4) 'cURL'
['mode']=>
string(2) 'rb'
['unread_bytes']=>
int(0)
['seekable']=>
bool(false)
['uri']=>
string(23) 'http://www.google.com/'
['timed_out']=>
bool(false)
['blocked']=>
bool(true)
['eof']=>
bool(false)
}*/

解决办法

重新编译PHP,去掉–with-curlwrapper 参数——编译前记得先执行 make clean。