feof
Tests whether a file pointer has reached the end of the file or hit a read error.
feof(resource $stream): bool- Added in PHP 4.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$stream | resource | A valid file pointer opened by fopen() or fsockopen() and not yet closed by fclose(). |
Return value
booltrue if the file pointer is at EOF or an error occurred (including a socket timeout); false otherwise.
Errors
Throws a TypeError when the argument is not a valid open stream resource.
feof() reports a flag on the stream, not a position. The flag is set by a read that came up short — it is not set merely because the pointer happens to sit on the last byte. That single fact explains almost every bug involving this function.
The extra iteration
The familiar while (!feof($h)) loop reads every line, then runs one more time: at the top of that final pass the flag is still unset, fgets() is called, it returns false, and your body handles a non-string. If the body appends to a buffer or pushes to an array, you get a stray empty entry.
Test the read instead:
while (($line = fgets($h)) !== false) {
// ...
}This is shorter, has no off-by-one, and works identically on files, pipes and sockets.
Where feof still earns its place
Use it after the loop to distinguish "finished the file" from "stopped early because of an error", and in protocol code where you read a fixed number of bytes with fread() and want to know whether more may arrive.
On network streams
It returns true on a socket timeout too, so a slow peer is indistinguishable from a closed connection. Check stream_get_meta_data($h)['timed_out'] when that difference matters.
Example
When to use it
- Confirm after a read loop that you reached the real end of the file rather than aborting on an error
- Drive a fread() loop that pulls a socket response in fixed-size chunks
- Detect a truncated download by checking the flag before the expected byte count was reached
- Stop a streaming parser that consumes a file with fgetc() one byte at a time
Frequently asked questions
Why does my while(!feof()) loop run one extra time?
Because the EOF flag is only set after a read fails, not when the pointer reaches the last byte. Use while (($line = fgets($h)) !== false) so the loop condition is the read itself.
Is feof reliable on sockets?
Only partly — it also returns true when the socket times out, so you cannot tell a slow peer from a closed connection. Inspect stream_get_meta_data($stream)['timed_out'] to distinguish the two.
How do I read a file line by line without feof?
Assign inside the condition: while (($line = fgets($handle)) !== false). The strict comparison is required because a line containing only "0" is falsy.
More examples
The loop you should write instead
Three lines are printed and nothing else — the false return ends the loop instead of entering the body. The feof() call afterwards is the useful one: it confirms the loop exited at the end of the file rather than on a read error, which is exactly the check the naive loop cannot make.
<?php
file_put_contents("lines.txt", "alpha\nbeta\ngamma\n");
$h = fopen("lines.txt", "r");
$n = 0;
while (($line = fgets($h)) !== false) {
$n++;
echo $n, ": ", rtrim($line, "\r\n"), "\n";
}
var_dump(feof($h)); // true: we stopped because of EOF, not an error
fclose($h);
unlink("lines.txt");The flag is set by a failed read, not by position
Reading the final byte of a one-byte file does not set the EOF flag, because the stream got exactly what it asked for and never tried to read past the end. Only the second fread(), which returns an empty string, flips the flag. This is the mechanism behind the extra loop iteration.
<?php
file_put_contents("one.txt", "x");
$h = fopen("one.txt", "r");
var_dump(feof($h)); // nothing read yet
var_dump(fread($h, 1)); // the only byte
var_dump(feof($h)); // the read did not come up short
var_dump(fread($h, 1)); // now it does
var_dump(feof($h));
fclose($h);
unlink("one.txt");Chunked reading with feof
For fixed-size fread() the feof() loop is acceptable, because strlen('') is 0 and the extra pass adds nothing to the total. The output shows 20 bytes read across the chunks, including one final read that returns an empty string and sets the flag.
<?php
file_put_contents("blob.bin", str_repeat("AB", 10));
$h = fopen("blob.bin", "r");
$chunks = 0;
$bytes = 0;
while (!feof($h)) {
$chunk = fread($h, 8);
$bytes += strlen($chunk);
$chunks++;
}
fclose($h);
echo "$chunks reads, $bytes bytes\n";
unlink("blob.bin");
Discussion