unlink

Deletes a file from the filesystem.

Signature
unlink(string $filename, ?resource $context = null): bool
  • Added in PHP 4.0

Parameters

ParameterTypeDescription
$filenamestringPath to the file. If it is a symbolic link, the link itself is deleted, not its target.
$contextoptional?resource= nullA stream context resource created with stream_context_create(), used by wrappers that support deletion.

Return value

boolReturns true on success or false on failure.

Errors

Emits an E_WARNING on failure, for example when the file does not exist or the containing directory is not writable.

Changelog

  • PHP 7.3.0On Windows it is now possible to unlink() files that still have open handles, though the name cannot be reused until every handle is closed.

unlink() removes a directory entry, exactly like the C function of the same name. The data is only actually freed once the link count reaches zero and no process holds the file open — which is why deleting a file another process is reading is safe on Unix: the reader keeps working against the now-nameless inode.

When to reach for it

  • Removing an upload after it has been processed or moved to permanent storage.
  • Expiring cache and session files.
  • Cleaning up temp files created by tempnam(), which nothing else deletes.

What trips people up

Deletion is governed by the directory's permissions, not the file's. You can delete a read-only file in a directory you can write to, and you cannot delete a world-writable file in a directory you cannot write to.

It only handles files. Passing a directory yields false and a warning — use rmdir(). On Windows, a symlink that points at a directory must also go through rmdir(), even though it is a link.

A missing file counts as failure and emits E_WARNING. If the file's absence is acceptable, guard with is_file() rather than reaching for the @ operator, which hides real errors too. And remember the stat cache: file_exists() can still return true immediately after a successful delete until you call clearstatcache().

Example

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

When to use it

  • Delete the original upload once a resized copy has been written to permanent storage.
  • Purge cache files whose mtime is older than the TTL during a scheduled cleanup run.
  • Remove the temp file created by tempnam() in a finally block so failures do not leak disk space.

Frequently asked questions

Why does unlink say permission denied when the file is writable?

Deleting is controlled by the permissions on the directory that holds the file, not the file itself. You need write and execute permission on that directory.

How do I delete a directory with unlink?

You cannot — unlink() only removes files and returns false with a warning on a directory. Use rmdir(), and empty the directory first. On Windows, symlinks to directories also require rmdir().

Does unlink delete a symlink or the file it points to?

It deletes the symlink itself and leaves the target intact. That matches the C unlink() behaviour, and is why removing a symlinked cache entry never destroys the real data.

More examples

Delete only when the file is really a file

The is_file() guard keeps directories and missing paths from reaching unlink(), so no E_WARNING is emitted and no @ suppression is needed.

Example · php
<?php
file_put_contents('demo.txt', 'x');
mkdir('demo_dir');

foreach (['demo.txt', 'demo_dir', 'nope.txt'] as $p) {
    if (is_file($p)) {
        echo $p, ': ', unlink($p) ? "deleted\n" : "failed\n";
    } else {
        echo $p, ": not a plain file, skipped\n";
    }
}
rmdir('demo_dir');

An open handle keeps working after the delete

On Unix, unlink() removes the name while the open handle keeps the inode alive, so the read still succeeds. This is exactly how tmpfile() gets its self-cleaning behaviour.

Example · php
<?php
file_put_contents('demo.txt', "still readable\n");
$fh = fopen('demo.txt', 'r');

unlink('demo.txt');
clearstatcache();
var_dump(file_exists('demo.txt'));

echo fgets($fh);   // the inode is still alive
fclose($fh);

Expire cache files by age

Only the backdated file crosses the one-hour threshold, so it is unlinked while the fresh one survives. This is the core of most file-based cache garbage collectors.

Example · php
<?php
file_put_contents('old.cache', 'stale');
file_put_contents('new.cache', 'fresh');
touch('old.cache', time() - 7200);
clearstatcache();

foreach (['old.cache', 'new.cache'] as $f) {
    if (time() - filemtime($f) > 3600) {
        unlink($f);
        echo "removed $f\n";
    } else {
        echo "kept $f\n";
    }
}
unlink('new.cache');

Discussion

  • Be the first to comment on this lesson.