Skip to content

Instantly share code, notes, and snippets.

@smallslime
Created April 24, 2016 08:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save smallslime/d5847c30ec177f46c573f38532305ec7 to your computer and use it in GitHub Desktop.
Save smallslime/d5847c30ec177f46c573f38532305ec7 to your computer and use it in GitHub Desktop.
<?php
namespace QMTV\System;
/**
* Class FileCache
*
* @author liuzhen
*/
class FileCache
{
protected $sCachePath;
protected $mCBKey2File;
/**
* @param string $sCacheDir
* @param int $iCreateMode
* @param mixed $mCBKey2File
*
* @throws \RuntimeException
*/
public function __construct($sCacheDir, $iCreateMode = 0777, $mCBKey2File = null)
{
$this->sCachePath = rtrim($sCacheDir, '/') . '/';
if (!file_exists($this->sCachePath)) {
if (!@mkdir($this->sCachePath, $iCreateMode, true)) {
throw new \RuntimeException("[CACHE] ; Create dir[$sCacheDir] failed");
}
}
$this->mCBKey2File = $mCBKey2File;
}
/**
* @param string $sKey
*
* @return array [<mixed>data, <bool>has_out_date, <string>file_path]
*/
public function get($sKey)
{
return $this->getWithDetailInfo($sKey)[0];
}
public function getWithDetailInfo($sKey)
{
$sFile = $this->getFilePath($sKey);
if (!file_exists($sFile)) {
return [null, 0, true, $sFile];
}
$aData = file($sFile);
if (count($aData) !== 2) {
return [null, 0, true, $sFile];
}
$iExpireAT = (int)$aData[0];
return [json_decode($aData[1]), $iExpireAT, time() - $iExpireAT > 0, $sFile];
}
/**
* @param string $sKey
* @param mixed $mValue
* @param int $iExpire
*
* @return bool
*/
public function set($sKey, $mValue, $iExpire)
{
$sFile = $this->getFilePath($sKey);
return (bool)file_put_contents($sFile, sprintf("%d\n%s", time() + $iExpire, json_encode($mValue)), LOCK_EX);
}
/**
* @param $sKey
*
* @return bool
*/
public function delete($sKey)
{
$sFile = $this->sCachePath . md5($sKey);
return file_exists($sFile) ? unlink($sFile) : true;
}
/**
* @return bool
*/
public function flush()
{
$rDir = opendir($this->sCachePath);
while (($sFile = readdir($rDir)) !== false) {
if (ltrim($sFile, '.') !== '') {
unlink($this->sCachePath . $sFile);
}
}
closedir($rDir);
return true;
}
protected function getFilePath($sKey)
{
return $this->sCachePath . ($this->mCBKey2File === null ? md5($sKey) : call_user_func($this->mCBKey2File,
$sKey));
}
public static function getAndSetFromFileCache(
FileCache $FileCache,
$sKey,
$iExpire,
$iExpireEffect,
$mCB,
$aParam = []
) {
list($mData, $iExpireAt, $bHasOutDate, $sFile) = $FileCache->getWithDetailInfo($sKey);
# 未过期
if (!$bHasOutDate) {
return $mData;
}
# 已过期
$sLockFile = $sFile . '.lock';
$rF = fopen($sLockFile, 'w');
# 未拿到锁
if (!flock($rF, LOCK_EX | LOCK_NB)) {
if ($iExpireAt == 0 || $iExpireAt + $iExpireEffect < time()) {
# 过期不可用($iExpireAt + $this->iExpireEffect < time()) / 不存在($iExpireAt == 0) 返回空
$FileCache->delete($sKey);
return null;
} else {
# 过期可用 返回 old 数据
return $mData;
}
}
# 拿到锁, 取数据更新
$mData = call_user_func_array($mCB, $aParam);
if ($mData !== null) {
$FileCache->set($sKey, $mData, $iExpire);
}
flock($rF, LOCK_UN);
fclose($rF);
return $mData;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment