fstat

Returns size, timestamps and permission information for an already open stream.

Signature
fstat(resource $stream): array|false
  • Added in PHP 4

Parameters

ParameterTypeDescription
$streamresourceAn open stream handle, typically returned by fopen().

Return value

array|falseAn array of file statistics with both numeric and named keys (dev, ino, mode, nlink, uid, gid, rdev, size, atime, mtime, ctime, blksize, blocks), or false on failure.

Errors

Throws a TypeError if $stream is not an open stream resource, and returns false for streams whose wrapper cannot report statistics, such as most network streams.

fstat is stat for a handle instead of a path. Because it asks the operating system about the file descriptor you already hold, it reports on the exact file you opened — not on whatever happens to sit at that path right now.

When to reach for it

Two situations make it the right choice. The first is freshness: filesize and filemtime read PHP's stat cache and can hand back numbers that are already stale after you write, while fstat queries the descriptor directly and is never cached. The second is safety: between a file_exists check and a later fopen the path can be replaced (a TOCTOU race). Open first, then fstat the handle, and there is no window.

It is also the only way to get a size for streams that have no meaningful path, such as php://memory and tmpfile() handles.

What trips people up

The returned array is duplicated: every field appears once under a numeric index and once under its name, so count() is 26 and a naive foreach or json_encode shows everything twice. Slice with array_slice($stat, 13) or read only the named keys.

Also, mode is a raw integer combining file type and permission bits — format it with substr(sprintf('%o', $stat['mode']), -4) to get the familiar octal.

Example

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

When to use it

  • Get the true current size of a file you just wrote, without a clearstatcache() call.
  • Check the size of a tmpfile() or php://memory stream, which has no path to pass to filesize().
  • Compare mtime before and after a lock to detect that another process changed the file you hold open.

Frequently asked questions

What is the difference between fstat and stat?

stat() takes a path and follows symlinks, while fstat() takes an open handle and reports on that exact descriptor. fstat is immune to the file being moved or replaced after you opened it, and it bypasses the stat cache.

Why does fstat return 26 elements instead of 13?

Each field is returned twice, once with a numeric index and once by name, for backwards compatibility with old code that used positions. Use array_slice($stat, 13) to keep only the named keys.

How do I get readable permissions from fstat?

The mode key is an integer holding the file type and permission bits together; convert it with substr(sprintf('%o', $stat['mode']), -4) to get the familiar four-digit octal such as 0644.

More examples

fstat is never stale, filesize can be

filesize() answers from the stat cache populated by the first call, so it may still report 5 after the write. fstat() goes to the descriptor and reports 10. Without fstat you would need clearstatcache() to get the same answer.

Example · php
<?php
$file = "grow.txt";
file_put_contents($file, "12345");

$fp = fopen($file, "a+b");
echo "filesize before: ", filesize($file), "\n";
fwrite($fp, "67890");
fflush($fp);

$stat = fstat($fp);
echo "filesize after (cached): ", filesize($file), "\n";
echo "fstat after (live):      ", $stat['size'], "\n";
fclose($fp);
unlink($file);

Sizing a stream that has no filename

tmpfile() gives a handle with no usable path, so filesize() is not an option; fstat() reports 1024 anyway. Masking mode with 0170000 extracts the file-type bits, and 0100000 identifies a regular file.

Example · php
<?php
$fp = tmpfile();
fwrite($fp, str_repeat("x", 1024));

$stat = fstat($fp);
echo "temp stream size: ", $stat['size'], "\n";
echo "is a regular file: ";
var_dump(($stat['mode'] & 0170000) === 0100000);
fclose($fp);

Reading only the named keys

The first 13 entries are the numeric duplicates, so array_slice($stat, 13) leaves only the associative half — the form you want before dumping, logging or json_encode()ing the result.

Example · php
<?php
$fp = fopen("php://memory", "r+b");
fwrite($fp, "abc");

$stat = fstat($fp);
$named = array_slice($stat, 13);
print_r(array_intersect_key($named, array_flip(['size', 'nlink', 'blksize'])));
fclose($fp);

Discussion

  • Be the first to comment on this lesson.