fgets

Reads one line from an open file pointer, including the trailing newline, or false at end of file.

Signature
fgets(resource $stream, ?int $length = null): string|false
  • Added in PHP 4.0

Parameters

ParameterTypeDescription
$streamresourceA valid file pointer opened by fopen() or fsockopen() and not yet closed by fclose().
$lengthoptional?int= nullReading ends after length minus one bytes, or at a newline (which is included in the result), or at EOF — whichever comes first. Omit it to read a whole line.

Return value

string|falseA string of up to length minus one bytes read from the stream, or false when there is no more data or an error occurs.

Errors

Throws a TypeError when the argument is not a valid open stream resource, and a ValueError when length is less than 1.

fgets() reads up to and including the next newline. Memory use is one line at a time, which is why it — not file() and not file_get_contents() — is how you process a log file bigger than your memory_limit.

The loop to write

$h = fopen("big.log", "r");
while (($line = fgets($h)) !== false) {
    // ...
}
fclose($h);

The strict !== false matters: when the last line of a file has no trailing newline, fgets() returns it as-is, so a final line of just 0 comes back as the falsy string "0". Testing while ($line = fgets($h)) stops there and truncates such files silently.

What trips people up

The newline is part of the returned string. Almost every comparison you write needs rtrim($line, "\r\n") first. Use that two-character mask rather than a bare trim() when leading or trailing spaces are significant data.

fgets() splits on \n only. A file written on Windows leaves a \r at the end of every line, which is why $line === "END" mysteriously fails on one machine and works on another.

The length argument counts bytes and gives you at most length - 1 of them; if a line is longer, the rest arrives on the next call. There is no way to detect that truncation from the return value alone.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Scan a multi-gigabyte access log for error lines without loading it into memory
  • Read a protocol response header by header from a stream until a blank line
  • Process a newline-delimited JSON export one record at a time
  • Read the first line of a file to sniff its header without opening the whole thing

Frequently asked questions

Does fgets include the newline character?

Yes — the trailing \n is part of the returned string, and on a Windows-authored file the \r before it is too. Strip both with rtrim($line, "\r\n").

What is the difference between fgets and fgetss?

fgetss() stripped HTML tags while reading; it was deprecated in PHP 7.3 and removed in PHP 8.0. Use fgets() and pass the result to strip_tags() if you need that behaviour.

How do I read a large file line by line without running out of memory?

Open it with fopen() and loop with while (($line = fgets($handle)) !== false), which holds only one line at a time. file() and file_get_contents() both load the entire file into memory and will hit memory_limit.

More examples

The newline is included

The returned string is five bytes, not three: fgets() stops after the \n and keeps both line-ending characters. The direct comparison fails, which is the classic bug when a config file was edited on Windows. rtrim() with the "\r\n" mask fixes it without eating meaningful spaces.

Example · php
<?php
file_put_contents("crlf.txt", "END\r\nnext\r\n");

$h = fopen("crlf.txt", "r");
$line = fgets($h);
var_dump($line);                        // string(5) "END\r\n"
var_dump($line === "END");              // false
var_dump(rtrim($line, "\r\n") === "END");  // true
fclose($h);

unlink("crlf.txt");

Limiting the read length

A length of 5 returns 4 bytes, "abcd" — the argument counts the terminating byte the C function would need. The rest of the line is not discarded; the next call picks it up mid-line. That is why an under-sized length quietly splits records instead of erroring.

Example · php
<?php
file_put_contents("long.txt", "abcdefghij\nklm\n");

$h = fopen("long.txt", "r");
var_dump(fgets($h, 5));   // at most 4 bytes
var_dump(fgets($h));      // remainder of the same line
var_dump(fgets($h));      // the next line
var_dump(fgets($h));      // false at EOF
fclose($h);

unlink("long.txt");

Read a header, then the body

One fgets() consumes the header line and leaves the pointer exactly at the start of the body, so the following loop reads only the remaining lines. This partial-consumption pattern is what makes fgets() suitable for line-oriented protocols where the first few lines have a different shape from the rest.

Example · php
<?php
file_put_contents("msg.txt", "SUBJECT: hello\r\nbody line 1\r\nbody line 2\r\n");

$h = fopen("msg.txt", "r");
$subject = rtrim(fgets($h), "\r\n");
echo "[", $subject, "]\n";

$body = [];
while (($line = fgets($h)) !== false) {
    $body[] = rtrim($line, "\r\n");
}
fclose($h);

print_r($body);
unlink("msg.txt");

Discussion

  • Be the first to comment on this lesson.