Hudzilla.org - the homepage of Paul Hudson
Contents > Files Wish List | Report Bug | About Me ]

8.11     Locking files with flock()

This is NOT the latest copy of this book; click here for the latest version.

bool flock ( resource handle, int operation [, int &wouldblock])

The key problem with file system operations is the precarious situation you are in if two scripts attempt to write to a file at the same time. The fopen() function, when called on a file, does not stop that same file from being opened by another script, which means you might find one script reading from a file as another is writing, or, worse, two scripts writing to the same file simultaneously.

The solution to this problem is to use file locking, which is implemented in PHP using the flock() function. When you lock a file, you have the option of marking it a read-only lock, thereby sharing access to the file with other processes, or an exclusive lock, allowing you to make changes to the file. On Unix, flock() is advisory , meaning that the OS is free to ignore it. On Windows, flock() is mandatory, meaning that files are locked by the OS whether you ask for it or not!

The flock() function takes a file handle as its first parameter, and a lock operation as its second parameter. File handles you know already, and the operations are simple: LOCK_SH requests a shared lock, LOCK_EX requests an exclusive lock, and LOCK_UN releases a lock. Calling flock() will return true if the file lock was retrieved successfully, or false if it failed. So, for example, flock() could be used like this:

<?php
    $fp
= fopen( $filename,"w"); // open it for WRITING ("w")
    
if (flock($fp, LOCK_EX)) {
        
// do your file writes here
        
flock($fp, LOCK_UN); // unlock the file
    
} else {
        
// flock() returned false, no lock obtained
        
print "Could not lock $filename!\n";
    }
?>

File locking requires a fairly modern file system, which does not include the original version of Microsoft's FAT file system, commonly used on Windows 95 and 98. NTFS, as well as FAT32, are both fine. Furthermore, the Network File System (NFS), commonly used to provide file sharing across Unix boxes, is not suitable for use with flock().

The file locking mechanism in PHP automatically makes processes queue up for their locks by default. For example, save this next script as flock.php:

<?php
    $fp
= fopen("foo.txt", "w");
    if (
flock($fp, LOCK_EX)) {
        print
"Got lock!\n";
        
sleep(10);
        
flock($fp, LOCK_UN);
    }
?>

That script attempts to lock the file foo.txt, so you will need to create that file. The script locks it with LOCK_EX, which means no other program can lock that file. Once the lock is obtained, the script sleeps for ten seconds, then unlocks the file and quits. If a lock cannot be obtained because another application has a lock, the script waits at the flock() call for the lock to be released, then locks it itself and continues.

To test this out, open up two command prompts and run the script twice. The first script run will get a lock immediately and print "Got lock!", then sleep for ten seconds. If while the first script is sleeping you launch the second script, it will wait ("block") on the flock() call and wait for the first script to finish. When the first script finishes, the second script will succeed in getting its lock, print out "Got lock!" then sleep for ten more seconds until it finally terminates.

Sometimes it is not desirable to have your scripts wait for a file to become unlocked, and in this situation you can add an extra option to the second parameter using the bitwise OR operator, |. If you pass in LOCK_NB ORed with your normal second parameter, PHP will not block when it requests a file lock. This means that if the file lock is not available, flock() will return immediately with false rather than hang around waiting for a lock to become available.

Here is how that looks in code:

<?php
    $fp
= fopen("foo.txt", "w");
    if (
flock($fp, LOCK_EX | LOCK_NB)) {
        echo
"Got lock!\n";
        
sleep(10);
        
flock($fp, LOCK_UN);
    } else {
        print
"Could not get lock!\n";
    }
?>

This time, the first script will get the lock and print "Got lock!", whereas the second will fail to get the lock, return immediately, and print "Could not get lock!"

Author's Note: Traditionally programs implicitly release their locks when they terminate, if they haven't already explicitly released them. That is, if a program quits with a file lock in place, the OS releases the file lock. This works great for situations where the program crashes, but how does it work when the file lock is obtained by a PHP script that's served up by an Apache process that doesn't die? There's a possibility here that the lock simply isn't released until the Apache child gets killed (usually after several thousand requests!), which is quite nasty.

I've given this a quick test, but I can't seem to make this problem occur: each time I run the test script above through Apache without the sleep or unlocking, I get "got lock!" - the script seems to successfully get a lock. More testing is required: I've a feeling that this might be down to the platform it's running on. Of course, you should always release your file locks, but that's a different problem entirely...





<< 8.10.2 Checking uploaded files: is_uploaded_file()   8.12 Permissions >>
Table of Contents
Want to see this stuff in print? PHP in a Nutshell takes the core topics covered here, adds in thousands of edits from the editorial team and myself, and combines them to make an unbeatable reference for PHP programmers at all levels.



My latest book has hundreds more tips on how to use PHP, Apache, and MySQL, plus Perl, Python, shell scripts, performance tuning, and more!



Top-right shadow
 
Bottom-left shadow Bottom shadow

Comments from other readers
Hlk - 30 Aug 2008

hi!

this article was very helpful, but insufficient. i need more!
let me give an example;

let there be two scripts to run, and a shared file which is also a php script updated dynamically by the first script and included by the second script.

here they are:

script1.php: first, reads inc.php, then decides for the new contents of it, then updates it by truncating it and writing the new contents. while doing these jobs it must lock inc.php, so that it cannot be included by some other script.

script2.php: includes inc.php in some parts of the program

inc.php: the shared file mentioned above

file locking works perfect for files to read/write purposes. But what if i want to include a file like this one?

A PHP User - 30 Aug 2008

oh god! thanks. that was what i was looking for.
i was wondering if we have to explicitly make the process sleep until it gets the lock, or it is done implicitly? now i've got the answer: no need to sleep in a loop, it's done automatically.

Clarkepeters - 30 Aug 2008

exactly what I needed.
I especially like the "sleep" test, that was a good idea.

anonymous - 30 Aug 2008

is there a locking mechanism in php to simulate atomic operations , where I can run something like
lock(); some code ; unlock();
and be sure that no one else is going to use "some code" while it is being executed , I can use the method you've described
but I don't really need to lock a file , just code .

Thanks

Max - 30 Aug 2008

Very useful page, as I was worried exactly about two PHP processes overwriting one file. Thanks !!

Kai MacTane - 30 Aug 2008

According to the <a href="http://www.php.net/manual/en/function.flock.php">PHP manual page on flock()</a>, the lock is automatically released by an <tt>fclose()</tt> call... and <tt>fclose()</tt> is automatically invoked on all open filehandles at the end of every script.

So, in essence, the "if a program quits with a file lock in place" situation can't arise with PHP; even if you deliberately leave off your <tt>flock()</tt> and <tt>fclose()</tt> statements because you're specifically <em>trying</em> to make a dangling or orphaned lock, PHP will just close and unlock it for you anyway.

writeson@charter.net - 30 Aug 2008

Whoops,

And I should add to my previous comment about trouble printing, this occurred in Firefox on my Win2000 machine at work.

Doug Farrell

writeson@charter.net - 30 Aug 2008

Hi,
Your page on flock() was very useful to me, and just what I was looking for to help me understand how to use it. However, trying to print this page out puts it in a funny format and clips off the top of the page till about the middle of the first code example.

Just thought you'd like to know. :)

Doug Farrell



Add comment
Please note that by posting a comment here you are committing it to the public domain. This is important so that others can make use of your code themselves, and also so that I can incorporate helpful notes directly into the main text. Comments are limited to 2000 characters in length.

If you are reporting an error in the content, please tell me directly.

Your name/email address:
Your comment:
 
Now, in order to verify that you're a real person, please answer this simple question: what is five plus two?
The answer is:
(please write in
numbers, eg 19)


Top-right shadow
 
Bottom-left shadow Bottom shadow