Preventing Concurrent Access Issues with WeChat Access Tokens Using PHP File Lock (flock)
This article explains a real-world case where storing WeChat access tokens in a JSON file caused race conditions, analyzes the root cause, and demonstrates how to use PHP's flock() function with proper locking to safely read and write the token, including sample code.
A WeChat public account project stores the access_token (valid for 2 hours) in a JSON file. When the token expires, concurrent requests may read or write the file simultaneously, leading to errors.
Example of the stored JSON:
<code>{"access_token":"easWasdw32323", "expire":1588219064}</code>Pseudocode used originally:
<code>function getToken($tokenFile) {
$tokenJson = file_get_contents($tokenFile);
if (!$tokenJson) {
$token = loadToken($tokenFile);
} else if (json_decode($tokenJson, true)['expire'] <= time()) {
$token = loadToken($tokenFile);
} else {
$token = json_decode($tokenJson, true)['access_token'];
}
return $token;
}
function loadToken($tokenFile) {
$fp = fopen($tokenFile, 'r+');
$tokenJson = ...; // call WeChat API to get token
fwrite($fp, json_encode($tokenJson));
return $tokenJson['access_token'];
}</code>Problem: after the token expires, two simultaneous requests (A and B) may both try to refresh it. Request A updates the file, but before the write completes, request B reads the partially‑written file and obtains an invalid token, causing intermittent failures.
Solution: use a file‑locking mechanism. PHP provides flock() to acquire shared (LOCK_SH) or exclusive (LOCK_EX) locks on a file handle, ensuring that only one process writes at a time.
Function prototype:
<code>flock(resource $handle, int $operation [, int &$wouldblock]) : bool</code>Lock constants:
LOCK_SH – shared lock for reading
LOCK_EX – exclusive lock for writing
LOCK_UN – release lock
LOCK_NB – non‑blocking lock (not supported on Windows)
Demo 1 (demo1.php) acquires an exclusive lock, sleeps for 5 seconds, then releases the lock:
<code><?php
$file = 'data.txt';
$handler = fopen($file, 'a+') or die('Failed to open file');
if (flock($handler, LOCK_EX)) {
sleep(5);
flock($handler, LOCK_UN);
} else {
echo 'Lock failed';
}
fclose($handler);
</code>Demo 2 (demo2.php) tries to acquire the same exclusive lock and write a string:
<code><?php
$file = 'data.txt';
$handler = fopen($file, 'a+') or die('Failed to open file');
if (flock($handler, LOCK_EX)) {
fwrite($handler, 'sometest string');
flock($handler, LOCK_UN);
} else {
echo 'Lock failed';
}
fclose($handler);
</code>Running demo1.php followed immediately by demo2.php shows that demo2.php cannot write until demo1.php releases the lock, proving the effectiveness of the locking strategy.
After learning the locking technique, the token‑handling code is revised to acquire an exclusive lock before reading or writing the token file, ensuring that concurrent requests no longer corrupt the token data.
<code>function getToken($tokenFile) {
$tokenJson = file_get_contents($tokenFile);
if (!$tokenJson) {
$token = loadToken($tokenFile);
} else if (json_decode($tokenJson, true)['expire'] <= time()) {
$token = loadToken($tokenFile);
} else {
$token = json_decode($tokenJson, true)['access_token'];
}
return $token;
}
function loadToken($tokenFile) {
$fp = fopen($tokenFile, 'w'); // obtain exclusive lock
if (flock($fp, LOCK_EX)) {
$tokenJson = ...; // call WeChat API
fwrite($fp, json_encode($tokenJson));
flock($fp, LOCK_UN);
} else {
return false;
}
return $tokenJson['access_token'];
}
</code>php中文网 Courses
php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.