PHP atomic write function

Motivation:

An atomic write function for those who appreciate the convenience of file_put_contents(), but need an atomic operation.  Please leave a comment if you have a more convenient and/or efficient solution.

Update (5/31/11):

Now using flock, as recommended by Petr and Hayden in their comments below, to avoid race condition. Thanks for the help!

Source code:


function atomic_put_contents($filename, $data)
{
    // Copied largely from http://php.net/manual/en/function.flock.php
    $fp = fopen($filename, "w+");
    if (flock($fp, LOCK_EX)) {
        fwrite($fp, $data);
        flock($fp, LOCK_UN);
    }
    fclose($fp);
}

Example usage:


$content = 'some content';

atomic_put_contents('path/file.ext', $content);

//creates file called path/file.ext containing 'some content'

7 thoughts on “PHP atomic write function

    1. thanks, Petr! I don’t have any experience w/ flock, but I’ll take a look at it. I heard writing and mv-ing a file was a simple, effective method for manipulating a file atomically, and it’s worked for my humble needs, but I’d prefer to use something more standard.

  1. This code has race conditions. It is possible that the file $tmp_path will be removed by another process after line 10, but before line 13. If you truly need atomicity, use flock() as Petr suggested.

  2. I realize this post is quite old but in case anyone else ends up here:

    I believe fopen using w+ will truncate the file, even if you don’t get the lock, so I think you could truncate a file that someone else is reading (assuming it’s not locked)?, it might be better to use mode “c” or “c+”? You probably then need to truncate with ftruncate unless you’re sure the contents are less or equal to what you’re writing

  3. You must use “c” with ftruncate instead of using “w+”, otherwise it’s not really atomic because another request can see an empty file for a moment (or corrupted?), even using flock.

    function atomic_put_contents($filename, $data)
    {
    // Copied largely from http://php.net/manual/en/function.flock.php
    if(($fp = fopen($filename, “c”)) !== false)
    {
    if (flock($fp, LOCK_EX))
    return ftruncate($fp, 0) && fwrite($fp, $data) !== false && flock($fp, LOCK_UN) && fclose($fp);
    }

    return false;
    }
    //Removed the else from my previous post

  4. After php 5.1.0 you can just use file_put_contents($filename, $data, LOCK_EX);

Comments are closed.