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'
Is a good solution?
Function “exec” ordinarily not available on a web-hostings, but PHP have atomic and fast function and this is rename which atomic work only the same partition. Not only write to file must be atomic, but read too. check http://cz2.php.net/function.stream_wrapper_register
http://php.net/function.flock
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.
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.
Thanks, Hayden. I’ve updated the function to use flock.
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
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
After php 5.1.0 you can just use file_put_contents($filename, $data, LOCK_EX);