fwrite
Writes a string to an open file or stream and returns the number of bytes written.
fwrite(resource $stream, string $data, ?int $length = null): int|false- Added in PHP 4
Parameters
| Parameter | Type | Description |
|---|---|---|
$stream | resource | An open stream handle, typically returned by fopen(). |
$data | string | The string to write. Binary-safe. |
$lengthoptional | ?int= null | If given, writing stops after this many bytes or at the end of $data, whichever comes first. |
Return value
int|falseThe number of bytes actually written, or false on failure.
Errors
Throws a TypeError if $stream is not an open stream resource; emits E_WARNING and returns false when the stream is not open for writing.
Changelog
- PHP 8.0.0$length is nullable now; previously the default was 0 with special-case meaning.
fwrite pushes a string into an open stream at the current pointer position and returns how many bytes it managed to write. It is binary-safe, so null bytes and arbitrary binary data pass through untouched. fputs is an alias of it.
When to reach for it
Use it when you are producing output incrementally — writing a report line by line, streaming rows into a CSV, appending to a log — or when you need to hold a handle open across several writes so you can keep an flock. For a single write of a value you already have in memory, file_put_contents is one call instead of three.
fwrite(STDERR, $msg) is the standard way to send CLI diagnostics somewhere that will not pollute piped stdout.
What trips people up
The return value is a byte count, not a success flag. On sockets and pipes a partial write is normal and not an error, so robust code loops over the remainder:
$len = strlen($data);
for ($w = 0; $w < $len; $w += $n) {
$n = fwrite($fp, substr($data, $w));
if ($n === false) break;
}Note that 0 and false are different outcomes and only false is a failure, so test with === false.
Writes are also buffered. Data is not on disk until fflush or fclose, which is why a crashed script can leave a truncated file.
Example
When to use it
- Stream a large CSV export row by row so the whole result set is never held in memory.
- Append to a log file with an flock held across the open, write and close.
- Write CLI errors to STDERR so they stay separate from piped program output.
Frequently asked questions
What is the difference between fwrite and file_put_contents?
file_put_contents opens, writes and closes a file in one call, while fwrite writes to a handle you manage. Use file_put_contents for a one-shot write and fwrite when you write repeatedly or need to keep a lock open.
Is fputs the same as fwrite?
Yes, fputs is an alias of fwrite with identical behaviour and signature. New code should use fwrite.
Why did fwrite return fewer bytes than my string length?
On non-blocking streams, sockets and pipes the operating system may accept only part of the buffer, which is a partial write rather than an error. Loop over the unwritten remainder until the total matches strlen($data).
More examples
Streaming a CSV instead of building it in memory
Each row is written and released immediately, so memory use stays flat whether there are three rows or three million. The output is the finished CSV; with file_put_contents you would have to concatenate the whole document first.
<?php
$file = "export.csv";
$rows = [[1, 'alice', 30], [2, 'bob', 41], [3, 'carol', 27]];
$fp = fopen($file, "wb");
fwrite($fp, "id,name,age\n");
foreach ($rows as $row) {
fwrite($fp, implode(',', $row) . "\n");
}
fclose($fp);
echo file_get_contents($file);
unlink($file);Handling a short write
The loop keeps writing until the whole string is consumed, which is required on sockets and pipes where one call often writes only part of the buffer. On a memory stream it completes in a single pass, printing "wrote 100 of 100 bytes".
<?php
$fp = fopen("php://memory", "r+b");
$data = str_repeat("x", 100);
$written = 0;
$len = strlen($data);
while ($written < $len) {
$n = fwrite($fp, substr($data, $written));
if ($n === false) { echo "write failed\n"; break; }
$written += $n;
}
echo "wrote $written of $len bytes\n";
fclose($fp);Appending with a lock
Mode 'a' always writes at the end of the file no matter where the pointer is, and the exclusive lock keeps concurrent processes from interleaving half-lines. fflush pushes the buffer out before the lock is released.
<?php
$file = "app.log";
foreach (["started", "working", "done"] as $msg) {
$fp = fopen($file, "ab");
flock($fp, LOCK_EX);
fwrite($fp, date('Y-m-d') . " $msg\n");
fflush($fp);
flock($fp, LOCK_UN);
fclose($fp);
}
echo file_get_contents($file);
unlink($file);
Discussion