元ネタ
コード
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
- よく似た関数の