微信 `access_token` 有效期 7200 秒,頻繁請求會觸發頻率限制。無框架環境下需自行實作快取儲存,本類採用 JSON 檔案儲存,支援多鍵值、TTL 過期自動清理。
<?php
class Cache {
public $cacheFile = "cache.json";
public $dir = "./cache/";
/** 設定快取 */
public function setCache($name, $val, $expires_time) {
$file = $this->hasFile();
$str = file_get_contents($file);
$arr = $str ? json_decode($str, true) : [];
if (empty($val)) {
unset($arr[$name]); // 值為空視為刪除
} else {
$arr[$name] = [
'value' => $val,
'expires_time' => $expires_time, // TTL 秒數
'add_time' => time(), // 寫入時間戳
];
}
file_put_contents($file, json_encode($arr), LOCK_EX);
}
/** 讀取快取 */
public function getCache($name) {
$file = $this->hasFile();
$str = file_get_contents($file);
$allArr = $str ? json_decode($str, true) : [];
if (!isset($allArr[$name])) return false;
$item = $allArr[$name];
// 過期檢查:當前時間 > 寫入時間 + TTL
if (time() > ($item['add_time'] + $item['expires_time'])) {
$this->removeCache($name);
return false;
}
return $item['value'];
}
/** 刪除快取 */
public function removeCache($name) {
$this->setCache($name, '', 0);
}
/** 確保快取檔案存在 */
private function hasFile() {
if (!is_dir($this->dir)) {
mkdir($this->dir, 0755, true);
}
$file = $this->dir . $this->cacheFile;
if (!file_exists($file)) {
touch($file);
}
return $file;
}
}
?>
<?php
$cache = new Cache();
$cache->dir = "../cache/"; // 自訂快取目錄
// 寫入 access_token (有效期 7000 秒,預留 200 秒緩衝)
$cache->setCache('wx_access_token', 'ACCESS_TOKEN_STRING', 7000);
// 讀取
$token = $cache->getCache('wx_access_token');
if ($token === false) {
// 快取過期/不存在 → 向微信 API 請求新 token
$token = requestNewAccessToken();
$cache->setCache('wx_access_token', $token, 7000);
}
echo "Current Token: $token";
// 手動刪除
$cache->removeCache('wx_access_token');
?>
| 問題 | 解決方案 |
|---|---|
| 高併發讀寫衝突 | `file_put_contents(..., LOCK_EX)` 已加獨佔鎖;極高併發建議改用 Redis/Memcached |
| 檔案過大影響效能 | 定期清理過期項目;或分檔儲存 (每鍵一檔) |
| 多伺服器共享 | 檔案系統不支援共享,必須用 Redis/資料庫集中快取 |
| 資料結構擴充 | 支援標籤/分組、序列化物件、壓縮長字串 |
| 效能監控 | 記錄 hit/miss 率、讀寫延遲,接入 Prometheus |
<?php
class WxTokenCache extends Cache {
const KEY = 'wx_access_token';
const BUFFER = 200; // 提前 200 秒刷新
public function getToken() {
$token = $this->getCache(self::KEY);
if ($token !== false) return $token;
// 雙重檢查鎖防止併發重複請求
$lockKey = self::KEY . '_lock';
if ($this->getCache($lockKey)) {
sleep(1); // 等待其他進程刷新
return $this->getCache(self::KEY);
}
$this->setCache($lockKey, '1', 10); // 10 秒鎖
$token = $this->requestTokenFromWx();
$this->setCache(self::KEY, $token, 7200 - self::BUFFER);
$this->removeCache($lockKey);
return $token;
}
private function requestTokenFromWx() {
// 實際調用微信 API 邏輯
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET";
$result = json_decode(file_get_contents($url), true);
return $result['access_token'] ?? '';
}
}
?>
<?php
if (extension_loaded('apcu')) {
apcu_store('wx_access_token', $token, 7000);
$token = apcu_fetch('wx_access_token');
apcu_delete('wx_access_token');
}
?>