file_exists() is vulnerable to race conditions and clearstatcache() is not adequate to avoid it.
The following function is a good solution:
<?php
function file_exists_safe($file) {
if (!$fd = fopen($file, 'xb')) {
return true; // the file already exists
}
fclose($fd); // the file is now created, we don't need the file handler
return false;
}
?>
The function will create a file if non-existent, following calls will fail because the file exists (in effect being a lock).
IMPORTANT: The file will remain on the disk if it was successfully created and you must clean up after you, f.ex. remove it or overwrite it. This step is purposely omitted from the function as to let scripts do calculations all the while being sure the file won't be "seized" by another process.
NOTE: This method fails if the above function is not used for checking in all other scripts/processes as it doesn't actually lock the file.
FIX: You could flock() the file to prevent that (although all other scripts similarly must check it with flock() then, see https://www.php.net/manual/en/function.flock.php). Be sure to unlock and fclose() the file AFTER you're done with it, and not within the above function:
<?php
function create_and_lock($file) {
if (!$fd = fopen($file, 'xb')) {
return false;
}
if (!flock($fd, LOCK_EX|LOCK_NB)) { // may fail for other reasons, LOCK_NB will prevent blocking
fclose($fd);
unlink($file); // clean up
return false;
}
return $fd;
}
if ($lock = create_and_lock("foo.txt")) {
// do stuff
flock($fd, LOCK_UN); // unlock
fclose($fd); // close
}
?>
SEE ALSO: https://linux.die.net/man/2/open about O_CREAT|O_EXCL (which is used with the 'x' modifier for fopen()) and problems with NFS