rename

Renames or moves a file or directory, overwriting an existing destination file.

Signature
rename(string $from, string $to, ?resource $context = null): bool
  • Added in PHP 4.0

Parameters

ParameterTypeDescription
$fromstringThe current path. Its stream wrapper must match the one used in to.
$tostringThe new path. An existing file here is overwritten; an existing directory causes a warning.
$contextoptional?resource= nullA stream context resource created with stream_context_create().

Return value

booltrue on success, false on failure.

Errors

Emits an E_WARNING when the source is missing, the destination directory does not exist, the two paths use different stream wrappers, or the destination is an existing non-empty directory.

rename() is PHP's move as well as its rename — there is no separate move(). It works on files and directories, and it can move between directories as long as both paths use the same stream wrapper.

When to reach for it

The best reason to use it is atomic publishing. On POSIX systems, renaming over an existing file within the same filesystem is atomic: no reader ever sees a half-written file. Write to config.json.tmp, then rename() it over config.json, and concurrent readers get either the old version or the new one, never a truncated one. The same trick powers atomic cache writes and blue-green deploy symlink swaps.

What trips people up

The atomicity guarantee only holds within one filesystem. Moving from /tmp to /var/www when they are separate mounts becomes a copy-then-delete under the hood, which is neither atomic nor cheap; put the temp file in the destination directory instead.

Other sharp edges: an existing destination file is silently overwritten, but an existing destination directory makes the call fail with a warning. The destination's parent directory must already exist — rename() will not create it. And for HTTP uploads always use move_uploaded_file(), which performs the upload validation rename() skips.

Example

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

When to use it

  • Publish a regenerated config or cache file atomically by writing to a .tmp path and renaming over the original.
  • Rotate a log file to app.log.1 before opening a fresh one.
  • Move a processed import out of an incoming/ queue into a done/ directory.

Frequently asked questions

How do I move a file in PHP?

Use rename($from, $to) with different directories in the two paths — there is no separate move function. The destination directory must already exist, and both paths must use the same stream wrapper.

Does rename overwrite an existing file?

Yes, an existing destination file is replaced without warning. An existing destination directory is different: the call fails and emits a warning instead.

Is rename atomic in PHP?

Only when the source and destination are on the same filesystem, where it maps to the atomic rename(2) syscall. Across mount points PHP falls back to copy-then-delete, which is not atomic.

More examples

Atomic write with a temp file

The second write replaces the first without any moment where config.json is empty or half-written. The temp file is created beside the target so the rename stays on one filesystem and therefore stays atomic.

Example · php
<?php
function atomicWrite(string $path, string $data): bool {
    $tmp = $path . '.' . bin2hex(random_bytes(4)) . '.tmp';
    if (file_put_contents($tmp, $data) === false) { return false; }
    if (!rename($tmp, $path)) { unlink($tmp); return false; }
    return true;
}

atomicWrite('config.json', json_encode(['version' => 1]));
atomicWrite('config.json', json_encode(['version' => 2]));
echo file_get_contents('config.json'), "\n";
unlink('config.json');

Overwrite rules for files and directories

Renaming onto an existing file destroys it without asking, which is why you should check the destination when the overwrite is not intended. Renaming onto a non-empty directory fails instead, so directory moves need the destination cleared first.

Example · php
<?php
file_put_contents('a.txt', 'A'); file_put_contents('b.txt', 'B');
rename('a.txt', 'b.txt');            // silently overwrites b.txt
echo file_get_contents('b.txt'), "\n";  // A

mkdir('d1'); mkdir('d2'); file_put_contents('d2/keep.txt', 'x');
var_dump(@rename('d1', 'd2'));       // false - d2 exists and is not empty

unlink('b.txt'); unlink('d2/keep.txt'); rmdir('d1'); rmdir('d2');

Rotating a log file

The rename moves the old log aside in one step, then a fresh file takes its place. Any process still holding an open handle keeps writing to the renamed inode, which is why log rotators send a reopen signal afterwards.

Example · php
<?php
file_put_contents('app.log', "old entries\n");

if (file_exists('app.log') && filesize('app.log') > 0) {
    rename('app.log', 'app.log.1');
}
file_put_contents('app.log', "fresh start\n");

echo 'archived: ', file_get_contents('app.log.1');
echo 'current:  ', file_get_contents('app.log');
unlink('app.log'); unlink('app.log.1');

Discussion

  • Be the first to comment on this lesson.