元ネタ
コード
Before
<?php
$fh = fsockopen("koukoku.shadan.open.ad.jp", 23, $ec, $em);
while (!feof($fh)) {
$chunk = fread($fh, 2);
echo mb_convert_encoding($chunk, 'utf-8', 'sjis');
}
After (fread を使う一般的な解答)
<?php
if (!$sock = stream_socket_client('tcp://koukoku.shadan.open.ad.jp:23')) {
exit(1);
}
if (!stream_filter_append($sock, 'convert.iconv.cp932/utf-8')) {
exit(1);
}
while ('' !== $byte = (string)fread($sock, 1)) {
echo $byte;
}
fread で書いたあとに fgetc でいいじゃんってことに気づいたので↓を追記
After (fgetc を使う別解)
<?php
if (!$sock = stream_socket_client('tcp://koukoku.shadan.open.ad.jp:23')) {
exit(1);
}
if (!stream_filter_append($sock, 'convert.iconv.cp932/utf-8')) {
exit(1);
}
while (false !== $char = fgetc($sock)) {
echo $char;
}
何を変えたか
重要なところ
fsockopen は古い
PHP 5 以降では stream_socket_client だけで全て要件は満たせるはず!
なお私の PHP デビューは 5.2 ですが普通に昔 fsockopen 使ってました(小声)
feof の判定は無限ループになる可能性がある
-
feofがtrueを返す条件- EOF への到達
-
feofがfalseを返す条件- まだ有効なデータがある
- エラー
というわけで,何かあったときにこのスクリプトは無限ループになってしまう可能性があります。 fread の返り値を見るほうが信頼性は高いでしょう。
ストリームフィルタを使おう
ストリームフィルタを使うと, mb_convert_encoding で中途半端なバイト列に変換処理を適用してしまって文字化けすることもないと思います。
その他こだわり
- (ストリームフィルタを使う前提であれば)
freadは 1 バイト単位でよくね? -
fpassthruとかstream_copy_to_streamで綺麗に書きたかったけど,内部でバッファリングされすぎて出力遅れちゃうのが微妙でした。でもファイルに書き出すならこっちのほうがいいと思う -
freadは異常時以外falseは返さず,終了判定は空文字列との比較になる点に注意。いっそのこと,falseがstringキャストされると空文字列になる 性質を生かして空文字列との比較に一本化すると綺麗に書けるでしょう。- よく似た関数の
fgetsは最後にfalseを正常系で返してくるのでハマりポイントの 1 つ。一方でfgetcはfgetsと同じ挙動なのでfalseとの比較で OK
- よく似た関数の

