php の file_get_contents で true も false も返ってこない 2

特定のURLでfile_get_contents で true も false も返ってこない現象があって
困っていたのでその対策メモ

現象

単純に

$html = file_get_contents($url);

ってやるとタイムアウトすることがあって、
それは
ini_set(‘default_socket_timeout’,3);
ってやってもダメなパターン。

問題はタイムアウトじゃない?

file_get_contentsがだめでもこれ以上切り分けできないので、
処理を1個づつかくために fsockopen 使ってみたけどやっぱりだめ。

fsockopen がダメな時のソースはこんなかんじ。

$fp = fsockopen($server,80,$errno,$errstr,$timeout);
if($fp){
  while(!feof($fp)){
    $data .= fread($fp, 1024);
  }
}
echo $data;

で こっから1こづつ切り分けていくと
どうも

while(!feof($fp)){
  $data .= fread($fp,1024);
}

ここが無限ループになってるらしい。

file_get_contentsのCソースまでは追いきれないので
(Cソース探したけどどこにあるかわからなくて)
カンですが、file_get_contents も同じ感じの処理をしていて、
feof のとこで詰まってるんじゃないかと。
だとしたら、ソケットの問題じゃないので、default_socket_timeout を指定しても意味なかったってわけです。

こうやって解決

問題が無限ループだったので php feof で検索したら・・・

関連検索: php feof 無限ループ, php feof fgets

同じ問題で詰まった人がいっぱいいるw!

で見ていくと

http://jp.php.net/feof
無効なファイルポインタを渡した場合、無限ループに陥ることがあります。 なぜなら EOF が TRUE を返すことができないからです。

ということらしい。

つまり feof が true を返さないので、feof で判定するのはダメなわけだ。
おそらく何らかの形でEOFが受信できない場合も trueが返らないんだろう。。
なので feof じゃなくて fread が成功したかで判定するように変更

$fp = fsockopen($server,80,$errno,$errstr,$timeout);
if($fp){
while($eof_check= fread($fp,1024)){
  $data .= $eof_check;
}
}
echo $data;

したら うまくいった!

ってことで

結局こんな感じの関数作って対処しました。

function ex_file_get_contents($server=”,$timeout=10){
  $data = ”;
  if(isset($server)){
    $fp = @fsockopen($server, 80,$errno, $errstr, $timeout);
    if($fp){
      while($eof_check=@fread($fp, 1024)){
        $data .= $eof_check;
      }
      @fclose($fp);
    }else{
      return false;
    }
  }else{
    return false;
  }
  return $data;
}