Locking files with flock()

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, such as NTFS (Windows), ext3/ext4 (Linux) and HFS+ (Mac). 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!"

 

Want to learn PHP 7?

Hacking with PHP has been fully updated for PHP 7, and is now available as a downloadable PDF. Get over 1200 pages of hands-on PHP learning today!

If this was helpful, please take a moment to tell others about Hacking with PHP by tweeting about it!

Next chapter: Permissions >>

Previous chapter: Checking uploaded files

Jump to:

 

Home: Table of Contents

Copyright ©2015 Paul Hudson. Follow me: @twostraws.