fclose
Closes an open file pointer, flushing any buffered writes and releasing the handle.
fclose(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. |
Return value
booltrue on success or false on failure.
Errors
Throws a TypeError when the argument is not a valid open stream resource — including a handle that has already been closed.
fclose() flushes whatever PHP still holds in the stream's write buffer, hands the file descriptor back to the operating system, and invalidates the resource.
When to reach for it
PHP closes every handle automatically when the script ends or when the last variable referencing it goes out of scope, so in a short web request an omitted fclose() rarely causes visible harm. It matters when timing does:
- Another process needs to see the complete file — until you close (or
fflush()), the tail of your writes may still be sitting in PHP's buffer. - You hold an advisory lock with
flock(); closing releases it. - A long-running worker opens files in a loop and would otherwise exhaust its file descriptor limit.
- You are about to
rename()orunlink()the file, which on Windows fails while a handle is open.
What trips people up
In PHP 8 a closed resource is no longer an acceptable argument. Calling fclose() a second time — from a finally block that also runs after the happy path closed the handle — throws a TypeError and takes the request down. Guard with is_resource($h), or set the variable to null after closing.
Closing does not delete anything, and it does not guarantee the bytes are physically on disk; that is what fsync() is for.
Example
When to use it
- Release a flock() lock so a waiting worker can proceed
- Close a CSV export before handing the path to another process or a download response
- Free descriptors inside a loop that opens thousands of files in one long-running job
- Close a handle before rename() or unlink(), which fail on an open file under Windows
Frequently asked questions
Do I have to call fclose in PHP?
Not strictly — PHP closes handles when the script ends or the variable goes out of scope. Call it explicitly when another process must see the finished file, when you hold a flock(), or when a long-running loop would otherwise run out of file descriptors.
What happens if I call fclose twice?
In PHP 8 the second call throws a TypeError, because a closed resource is no longer a valid argument. Guard with is_resource($handle) or set the variable to null after closing.
Does fclose guarantee the data is on disk?
No. It flushes PHP's buffer to the operating system, but the OS may still hold the data in its page cache. Call fsync() before closing if you need durability across a power failure.
More examples
Closing safely more than once
The is_resource() guard plus nulling the variable makes the close idempotent, which is what you want when both the success path and a finally block try to clean up. Without it, the second call throws a TypeError under PHP 8.
<?php
function closeOnce(&$handle): void
{
if (is_resource($handle)) {
fclose($handle);
$handle = null;
}
}
$h = fopen("tmp.txt", "w");
fwrite($h, "data");
closeOnce($h);
closeOnce($h); // no TypeError the second time
var_dump($h); // NULL
echo file_get_contents("tmp.txt"), "\n";
unlink("tmp.txt");Using a closed handle throws
Every stream function rejects a closed resource in PHP 8, so the failure surfaces as a catchable TypeError rather than the silent false-plus-warning of PHP 7. The output shows abc followed by "caught TypeError".
<?php
$h = fopen("data.txt", "w+");
fwrite($h, "abc");
rewind($h);
echo fread($h, 3), "\n"; // abc
fclose($h);
try {
fread($h, 3);
} catch (TypeError $e) {
echo "caught ", $e::class, "\n";
}
unlink("data.txt");Closing releases a flock lock
The second handle cannot take the exclusive lock while the first holds it, so the non-blocking attempt returns false. fclose() on the first handle drops the lock and the retry succeeds. This is why forgetting fclose() in a worker can stall every other worker on the same lock file.
<?php
$file = "queue.lock";
$a = fopen($file, "c");
var_dump(flock($a, LOCK_EX | LOCK_NB)); // true
$b = fopen($file, "c");
var_dump(flock($b, LOCK_EX | LOCK_NB)); // false - still held
fclose($a); // lock released here
var_dump(flock($b, LOCK_EX | LOCK_NB)); // true
fclose($b);
unlink($file);
Discussion