Files
kefu/im/server-fix-20260831/HomeNewsCache.php
T
2026-09-03 08:38:17 +08:00

89 lines
3.3 KiB
PHP

<?php
// One cache entry for the public homepage list; request arguments never enter its key.
function txiaw_read_home_news_cache($file)
{
$raw = @file_get_contents($file);
if ($raw === false) {
return false;
}
$entry = json_decode($raw, true);
if (!is_array($entry) || !isset($entry['created'], $entry['items']) ||
!is_numeric($entry['created']) || !is_array($entry['items']) ||
$entry['created'] > time() + 5) {
return false;
}
return $entry;
}
function txiaw_home_news_cached($cacheDirectory = null, $loader = null)
{
if ($cacheDirectory === null) {
$cacheDirectory = dirname(__DIR__) . '/Runtime/TxiawHomeNews';
}
if ($loader === null) {
$loader = function () {
return gxl_mysql_news('field:news_id,news_cid,news_name;limit:100;order:news_addtime desc');
};
}
$file = $cacheDirectory . '/news-list-v1.json';
$entry = txiaw_read_home_news_cache($file);
if ($entry !== false && time() - $entry['created'] < 60) {
return $entry['items'];
}
if (!is_dir($cacheDirectory) && !@mkdir($cacheDirectory, 0750, true) && !is_dir($cacheDirectory)) {
return call_user_func($loader);
}
$lock = @fopen($cacheDirectory . '/news-list-v1.lock', 'c');
if ($lock === false) {
return call_user_func($loader);
}
if (!flock($lock, LOCK_EX | LOCK_NB)) {
fclose($lock);
// Other requests can use a recently expired value while one request refreshes it.
if ($entry !== false && time() - $entry['created'] < 300) {
return $entry['items'];
}
// A cold cache gets a bounded wait; never keep an entire PHP pool waiting on a lock.
for ($attempt = 0; $attempt < 20; $attempt++) {
usleep(10000);
$entry = txiaw_read_home_news_cache($file);
if ($entry !== false && time() - $entry['created'] < 60) {
return $entry['items'];
}
}
// Preserve page availability if the cache filesystem or refresher fails.
// The indexed query and Nginx limits still bound database work in this fallback.
return call_user_func($loader);
}
try {
// Another request may have refreshed between our first read and lock acquisition.
$latest = txiaw_read_home_news_cache($file);
if ($latest !== false && time() - $latest['created'] < 60) {
return $latest['items'];
}
try {
$items = call_user_func($loader);
} catch (Exception $exception) {
if ($entry !== false && time() - $entry['created'] < 300) {
return $entry['items'];
}
throw $exception;
}
if (is_array($items)) {
$encoded = json_encode(array('created' => time(), 'items' => $items));
$temporary = $encoded === false ? false : @tempnam($cacheDirectory, '.news-');
if ($temporary !== false) {
@chmod($temporary, 0640);
$written = @file_put_contents($temporary, $encoded);
if ($written !== strlen($encoded) || !@rename($temporary, $file)) {
@unlink($temporary);
}
}
}
return $items;
} finally {
flock($lock, LOCK_UN);
fclose($lock);
}
}