1. Review the implementation of the put() method in Illuminate\Filesystem\Filesystem
public function put($path, $contents, $lock = false)
{
return file_put_contents($path, $contents, $lock ? LOCK_EX : 0);
}
2. Reference: In concurrent scenarios, if you do not wrap it as follows, file_get_contents may return empty https://www.php.net/manual/zh/function.file-put-contents.php#112831 . It is important to understand that LOCK_EX does not prevent reading the file unless you also explicitly acquire a read lock (shared lock) using the PHP ‘flock’ function. In other words, in concurrent scenarios, if you do not wrap it as follows, file_get_contents may return empty. If you have code that performs file_get_contents on a file, modifies the string, and then re-saves it using file_put_contents, you had better ensure you do this correctly, otherwise your file will randomly erase itself.
<?php
$myfile=fopen('test.txt','rt');
flock($myfile,LOCK_SH);
$read=file_get_contents('test.txt');
fclose($myfile);
?>3. I decided to simulate a scenario in the test environment where file_put_contents() is used to simultaneously write to the same file path, and then observe whether reading the file content would result in an empty result.
require.php
<?php
$i = rand();
$path = 'return.php';
$contents = [
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j'
];
if (file_exists($path)) {
$read = file_get_contents($path);
if (empty($read)) {
file_put_contents('empty-' . rand() . '.php', $read, LOCK_EX);
}
}
$code = '<?php';
$code .= "\n\n";
$contents = array_merge($contents, [$i]);
$code .= 'return ' . var_export($contents, true) . ';';
file_put_contents($path, $code, LOCK_EX);
?>4. Reference: Using Alibaba Cloud Performance Testing Service (PTS) to simulate concurrent HTTP requests . Reviewing the program execution results, I confirmed that over 20,000 files were generated, namely: return.php, and a large number of files prefixed with empty-. As shown in Figure 1

5. From this, I conclude that if you have code that saves file content using file_put_contents, even though LOCK_EX has been added, there is a high probability of reading empty file content in high-concurrency scenarios.
6. I decided to explicitly acquire a read lock (shared lock) using the PHP ‘flock’ function before reading the file content. The final code implementation is as follows
require.php
<?php
$i = rand();
$path = 'return.php';
$contents = [
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j'
];
if (file_exists($path)) {
$myfile = fopen($path, 'r');
flock($myfile, LOCK_SH);
$read = file_get_contents($path);
fclose($myfile);
if (empty($read)) {
file_put_contents('empty-' . rand() . '.php', $read, LOCK_EX);
}
}
$code = '<?php';
$code .= "\n\n";
$contents = array_merge($contents, [$i]);
$code .= 'return ' . var_export($contents, true) . ';';
file_put_contents($path, $code, LOCK_EX);
?>7. Reference: Using Alibaba Cloud Performance Testing Service (PTS) to simulate concurrent HTTP requests . After running the concurrency test again, I reviewed the program execution results. There were no longer any files prefixed with empty-, which indicates that explicitly acquiring a read lock (shared lock) using the PHP ‘flock’ function is effective. However, during program execution, the content of the return.php file was still empty at times, except that in this case, it did not read the file content directly but was waiting instead. As shown in Video 1
8. However, reviewing the test reports shows that performance decreased slightly after adding the read lock. The performance comparison before and after adding the read lock is as follows. Test report before locking: Average RT (ms): 11, TPS (Average/Peak): 832/903. Test report after locking: Average RT (ms): 12, TPS (Average/Peak): 825/872. Term explanations: Average RT (ms) RT refers to the business Response Time, and Average RT is the average RT of all APIs, measured in ms; the smaller, the better. TPS (Average/Peak) TPS refers to the number of transactions processed by the system per second (Transaction Per Second), including the average and peak TPS: Average: Represents the average TPS for the scenario during the stress test period. Peak: Represents the highest TPS for the scenario during the stress test period; the larger, the better. As shown in Figure 2 and Figure 3


9. Therefore, whether to add a read lock is something you need to weigh yourself. If you do not add a read lock, it is recommended to check whether the content is empty when reading the file; if it is empty, you need to find a way to handle it additionally.
