89 lines
3.3 KiB
PHP
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);
|
|
}
|
|
}
|