How do you copy a file in PHP without overwriting an existing file?

14,456

Solution 1

The obvious solution would be to call file_exists to check to see if the file exists, but doing that could cause a race condition. There is always the possibility that the other file will be created in between when you call file_exists and when you call copy. The only safe way to check if the file exists is to use fopen.

When you call fopen, set the mode to 'x'. This tells fopen to create the file, but only if it doesn't exist. If it exists, fopen will fail, and you'll know that you couldn't create the file. If it succeeds, you will have a created a file at the destination that you can safely copy over. Sample code is below:

// The PHP copy function blindly copies over existing files.  We don't wish
// this to happen, so we have to perform the copy a bit differently.  The
// only safe way to ensure we don't overwrite an existing file is to call
// fopen in create-only mode (mode 'x').  If it succeeds, the file did not
// exist before, and we've successfully created it, meaning we own the
// file.  After that, we can safely copy over our own file.

$filename = 'sourcefile.txt'
$copyname = 'sourcefile_copy.txt'
if ($file = @fopen($copyname, 'x')) {
    // We've successfully created a file, so it's ours.  We'll close
    // our handle.
    if (!@fclose($file)) {
        // There was some problem with our file handle.
        return false;
    }

    // Now we copy over the file we created.
    if (!@copy($filename, $copyname)) {
        // The copy failed, even though we own the file, so we'll clean
        // up by itrying to remove the file and report failure.
        unlink($copyname);
        return false;
    }

    return true;
}

Solution 2

I think you answered your own question - check to make sure the destination file exists before performing the copy. If the file exists, skip the copy.

Update: I see you really did answer your own question. You mention race conditions, but if you do find that the file already exists, how do you know that:

  • the file that is already there is really the one you want to copy
  • the other process copying the file has completed its job (the file data is all there)
  • the other process copying the file isn't going to fail (and leave an incomplete file, or delete the new file)

I think you should consider these questions when designing a solution to your problem.

Solution 3

A honey badger function, which just doesn't care about race-conditions, but works cross-platform.

function safeCopy($src, $dest) {
    if (is_file($dest) === true) {
        // if the destination file already exists, it will NOT be overwritten.        
        return false;
    }

    if (copy($src, $dest) === false) {
        echo "Failed to copy $src... Permissions correct?\n";
        return false;
    }

    return true;   
}
Share:
14,456
Douglas Mayle
Author by

Douglas Mayle

It's me

Updated on June 06, 2022

Comments

  • Douglas Mayle
    Douglas Mayle almost 2 years

    When you use the PHP copy function, the operation blindly copies over the destination file, even if it already exists. How do you copy a file safely, only performing the copy if there is no existing file?

  • Nathan Strong
    Nathan Strong over 15 years
    You still have a race condition. But you're on the right track--if the fopen succeeds, use fwrite() to perform the copy, and unlink the source file when complete.
  • Professor of programming
    Professor of programming over 2 years
    In addition to what Nathan said, open with an exclusive lock, to prevent writes from other programs during the copy.