I hope that you are already familiar with the awesome file locking mechanism that PHP and your OS provide. If not, here is a ...
Brief introduction to file locking
File locking is a mechanism that allows you to prevent read and write access to a specific file for a specific time. You request a write lock on a file, and if it is provided to you (it can be denied too), you can be sure that no other application/instance/process can write to the same file until you release the lock.
Why use file locking
The main reason is that you do not want invalid/broken data. Imagine you use a file to store some JSON data. In one moment you want to read the data, modify it and then write it back. You will be surprised to find out that at the exact same moment (milliseconds apart of course) some other application is doing the same thing and overwriting your modifications. In such case, you should be using file locking.
File locking in PHP
PHP provides a very simple, yet effective mechanism to acquire and release locks. Here is an example:
$pointer = fopen("file.json", "c+"); // open the file for writing
if (flock($pointer, LOCK_EX)) { // acquire a writer lock
// do some data manipulation
flock($pointer, LOCK_UN); // release the lock
} else {
// the file cannot be locked
}
fclose($pointer);
If a file is locked (for reading or writing) in one application, all other applications that request locks on the same file will be paused until the lock is released in the first application. This is the default blocking mode, and it works great.
Now we are ready to continue with the main topic of this post ...
What happens when you read a locked file
In the following lines, I will show you few ways that you can use to read a file while it is locked for writing (and modified) and the results you will get. Also, for a bonus, I will add the result of the filesize() function.
Reading a locked file with file_get_contents()
This is one of the worst way to read a file while it is locked and modified, because:
- file_get_contents() will return an empty string (like in "")
- filesize() will return the actual number of bytes written to the file
$content = file_get_contents('file.json'); // will return empty string
clearstatcache('file.json'); // clear the file cache for the next function
$size = filesize('file.json'); // will return the actual file size in the moment
Reading a locked file with fread() without a reading lock
This is the other worst way to read a file while it is locked and modified, because:
- fread() will return an empty string (like in "")
- filesize() will return the actual number of bytes written to the file
$pointer = fopen('file.json', 'r');
$content = fread($pointer, 1000); // will return empty string
clearstatcache('file.json'); // clear the file cache for the next function
$size = filesize('file.json'); // will return the actual file size in the moment
fclose($pointer);
Reading a locked file with fread() with a reading lock (shared lock)
This is the option to pick if you are OK waiting for the write lock to be released. PHP automatically pauses the execution of the application, so you do not have to do anything special. Just keep in mind the max_execution_time option in your PHP config.
$pointer = fopen('file.json', 'r');
if(flock($pointer, LOCK_SH)){ // will block execution until the write lock is released
$content = fread($pointer, 1000); // will return the correct content
clearstatcache('file.json'); // clear the file cache for the next function
$size = filesize('file.json'); // will return the correct size
}
fclose($pointer);
Reading a locked file with fread() with a reading, nonblocking lock
This is the advanced option. You can request a lock while the file is locked and modified in another application, but instead of waiting for file lock release, the execution will continue.
$pointer = fopen('file.json', 'r');
$wouldblock = null; // a variable that will be set to 1 if the file is already locked
if (flock($pointer, LOCK_SH | LOCK_NB, $wouldblock)) { // LOCK_NB tells flock() to continue execution of the application
$content = fread($pointer, 1000); // will return the correct content
clearstatcache('file.json'); // clear the file cache for the next function
$size = filesize('file.json'); // will return the correct size
} else {
if ($wouldblock) {
// the file is currently locked by another application but we can do something else
} else {
// cannot acquire lock for other reason
}
}
fclose($pointer);
Final thoughts
As you can see file locking is not a scary thing. It is quite useful if done properly. I will be happy for you to take the following from this post:
Always lock your files (when reading and writing) if multiple applications/instances/processes have write access to those files.
One more thing ...
I would like to recommend you a library of mine that brings locking benefits to more complex tasks - tasks that you do not want to interfere with one another. The library is simply called Lock, and you can find it at GitHub at https://github.com/ivopetkov/lock. Its based on file locking and works great in apps that share a common filesystem.