fseek
Moves the read/write pointer of an open stream to a given byte offset.
fseek(resource $stream, int $offset, int $whence = SEEK_SET): int- Added in PHP 4
Parameters
| Parameter | Type | Description |
|---|---|---|
$stream | resource | An open, seekable stream handle, typically returned by fopen(). |
$offset | int | The byte offset to move to. May be negative when used with SEEK_END or SEEK_CUR. |
$whenceoptional | int= SEEK_SET | Where the offset is measured from: SEEK_SET (start of file), SEEK_CUR (current position) or SEEK_END (end of file). |
Return value
int0 on success, -1 on failure.
Errors
Throws a TypeError if $stream is not an open stream resource. An unrecognised $whence is not an exception: fseek simply fails and returns -1.
fseek repositions the pointer of an open stream so the next fread or fwrite happens somewhere else in the file. It is what turns a file handle from a one-way tape into random-access storage.
When to reach for it
Use it to read a fixed-width record by index (fseek($fp, $n * $recordSize)), to tail the last few kilobytes of a log with SEEK_END and a negative offset, to skip a header before parsing a body, or to patch a few bytes in place without rewriting a whole file.
What trips people up
The return value is inverted compared with almost everything else in PHP: 0 means success and -1 means failure. Writing if (fseek($fp, 100)) { /* worked */ } gets it exactly backwards; test === -1 for failure.
Seeking past the end of the file is allowed and does not fail. Nothing is written until you write, at which point the gap is filled with null bytes — a common source of mysteriously padded files.
Not every stream is seekable. Pipes, sockets and many wrapper streams are forward-only; check stream_get_meta_data($fp)['seekable'] first. Finally, handles opened in append mode (a, a+) accept the seek but ignore it for writes, which always land at the end.
Example
When to use it
- Read record number N out of a fixed-width binary index without scanning the file.
- Show the last 4 KB of a log file by seeking to -4096 from SEEK_END.
- Skip a known header block, then hand the rest of the stream to a parser.
Frequently asked questions
Why does fseek return 0 when it works?
It mirrors the C library's fseek(), which uses 0 for success and -1 for failure. Test the result with === -1 rather than a truthiness check, because 0 is falsy in PHP.
What is the difference between fseek and rewind?
rewind($fp) is equivalent to fseek($fp, 0) except that it returns a bool instead of an int status code. Use rewind when you just want to go back to the start.
Why does fseek have no effect on my file?
Handles opened in append mode ('a' or 'a+') always write at the end regardless of the pointer, and non-seekable streams such as pipes and sockets reject the seek. Check stream_get_meta_data($fp)['seekable'] and open with 'r+' when you need to write at an offset.
More examples
Reading fixed-width records by index
Each record is exactly 8 bytes, so record N starts at N * 8 and can be read directly. The output is gamma, alpha, delta — proof that the file was never scanned sequentially, which is why this stays fast on a file of any size.
<?php
$file = "records.bin";
$size = 8;
file_put_contents($file, "alpha beta gamma delta ");
$fp = fopen($file, "rb");
foreach ([2, 0, 3] as $i) {
fseek($fp, $i * $size);
echo $i, ": ", trim(fread($fp, $size)), "\n";
}
fclose($fp);
unlink($file);Tailing the end of a log
A negative offset with SEEK_END puts the pointer 30 bytes before the end, so only the final lines are read no matter how large the log is. The first line printed may be a partial line, which is why real tail implementations discard everything before the first newline.
<?php
$file = "app.log";
file_put_contents($file, implode("\n", array_map(fn($i) => "line $i", range(1, 40))) . "\n");
$fp = fopen($file, "rb");
fseek($fp, -30, SEEK_END);
$tail = fread($fp, 8192);
fclose($fp);
echo trim($tail), "\n";
unlink($file);Seeking past the end pads with null bytes
The seek to offset 6 succeeds even though the stream only held 2 bytes. Writing there produces a 7-byte stream whose hex dump shows 4142 00000000 5a — the four null bytes PHP inserted to fill the hole.
<?php
$fp = fopen("php://memory", "r+b");
fwrite($fp, "AB");
fseek($fp, 6);
fwrite($fp, "Z");
rewind($fp);
$data = stream_get_contents($fp);
echo strlen($data), "\n";
echo bin2hex($data), "\n";
fclose($fp);
Discussion