feof() 用於判斷檔案指標是否到達 EOF(End Of File),是檔案讀取迴圈的核心條件。但其行為在 Socket、管道、無效指標等場景下極易踩坑。本文系統解析 feof() 機制、常見陷阱與最佳實踐。
feof(resource $stream): bool
// TRUE : 已到達 EOF 或發生錯誤(含 Socket 超時)
// FALSE : 未到達 EOF
<?php
$file = fopen($_SERVER['DOCUMENT_ROOT'] . '/me/test.txt', 'r');
if (!$file) {
die('無法開啟檔案');
}
while (!feof($file)) {
$line = fgets($file);
if ($line !== false) {
echo $line . '<br />';
}
}
fclose($file);
輸出:
Hello, this is a test file.
There are three lines here.
This is the last line.
$file = @fopen('no_such_file', 'r'); // 返回 false
while (!feof($file)) { ... } // feof(false) 總返回 false → 無限迴圈
修正:迴圈前必須檢查指標有效性。
$file = @fopen('no_such_file', 'r');
if (!$file) { die('開啟失敗'); }
while (!feof($file)) { ... }
fsockopen()、popen() 開啟的流,若遠端不關閉連線,feof() 會持續阻塞直到超時(預設 60 秒,可用 stream_set_timeout() 調整)。
$fp = fsockopen('example.com', 80);
stream_set_timeout($fp, 10); // 10 秒超時
fwrite($fp, "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n");
while (!feof($fp)) {
echo fgets($fp, 1024);
}
fclose($fp);
讀取錯誤時 fgets() 也返回 false,若僅判斷 feof() 會漏掉錯誤。標準模式:
while (($line = fgets($file)) !== false) {
// 處理 $line
}
// 迴圈結束後可檢查 feof() 區分正常結束 vs 錯誤
if (!feof($file)) {
error_log('讀取發生錯誤');
}
$meta = stream_get_meta_data($fp);
$meta['eof'] // 等同 feof($fp)
$meta['timed_out'] // Socket 超時標記(feof 無法區分)
$meta['blocked'] // 是否阻塞模式
$meta['unread_bytes'] // 緩衝區未讀位元組數
Socket 编程建議使用 stream_get_meta_data() 綜合判斷。
while (($line = fgets($fp)) !== false),無需額外 feof()。stream_set_timeout),並檢查 stream_get_meta_data()['timed_out']。fread($fp, 8192) 配合 feof(),避免一次性載入記憶體。fclose() 或 try-finally 確保關閉。feof() 看似簡單,實則隱藏 Socket 阻塞、無效指標無限迴圈、錯誤與 EOF 混淆等深坑。掌握「先驗證指標、優先用 fgets 返回值判斷、Socket 必設超時」三大原則,才能在檔案與網路流讀取中穩健落地。