Приглашаем посетить
Культура (niv.ru)

Section 13.9.  Locking Files with flock( )

Previous
Table of Contents
Next

13.9. Locking Files with flock( )

The fopen( ) function, when called on a file, does not stop that same file from being opened by another script. This 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. Windows forces the use of flock( ), whether or not you ask for it.

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:

    $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:

    $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 must create that file before running the script. 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 10 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 10 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 10 more seconds until it finally terminates.

Sometimes it is not desirable to have your scripts wait for a file to become unlocked; 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 wait for a lock to become available.

Here is how that looks in code:

    $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!".

If you intend to have several users accessing the same file frequently, locking as shown above is not sufficient to guarantee data consistency. The problem is that between the call to fopen( ) and flock( ), there is a race condition: it is possible that another user may get in and change our file before we have locked it. Of course, we can't lock a file without opening it first, so the solution is to use a lock fileoften called a semaphore file. To write to our real file, we must first successfully lock the matching semaphore file; without that lock, we ought not to write to the real file. A semaphore file is just a normal file like any otherif you want to get permission to lock myfile.txt, create an empty semaphore file called myfile.txt.sem and have people lock that first.


Previous
Table of Contents
Next