LoginSignup
14
8

@uzulla さんの「一般社団法人サイバー技術・インターネット自由研究会の電子公告をPHPで読む」にダメ出しする

Last updated at Posted at 2023-09-05

元ネタ

コード

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 の判定は無限ループになる可能性がある

  • feoftrue を返す条件
    • EOF への到達
  • feoffalse を返す条件
    • まだ有効なデータがある
    • エラー

というわけで,何かあったときにこのスクリプトは無限ループになってしまう可能性がありますfread の返り値を見るほうが信頼性は高いでしょう。

ストリームフィルタを使おう

image.png

image.png

ストリームフィルタを使うと, mb_convert_encoding で中途半端なバイト列に変換処理を適用してしまって文字化けすることもないと思います。

その他こだわり

  • (ストリームフィルタを使う前提であれば) fread は 1 バイト単位でよくね?
  • fpassthru とか stream_copy_to_stream で綺麗に書きたかったけど,内部でバッファリングされすぎて出力遅れちゃうのが微妙でした。でもファイルに書き出すならこっちのほうがいいと思う
  • fread は異常時以外 false は返さず,終了判定は空文字列との比較になる点に注意。いっそのこと, falsestring キャストされると空文字列になる 性質を生かして空文字列との比較に一本化すると綺麗に書けるでしょう。
    • よく似た関数の fgets は最後に false を正常系で返してくるのでハマりポイントの 1 つ。一方で fgetcfgets と同じ挙動なので false との比較で OK
14
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
8