fclose

Closes an open file pointer, flushing any buffered writes and releasing the handle.

Signature
fclose(resource $stream): bool
  • Added in PHP 4.0

Parameters

ParameterTypeDescription
$streamresourceA 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() or unlink() 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

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

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.

Example · php
<?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".

Example · php
<?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.

Example · php
<?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

  • Be the first to comment on this lesson.