Fix and refactor based on unit tests results

SVN:trunk[5165]
This commit is contained in:
Eric Espié
2017-12-22 14:08:58 +00:00
parent 3e35dafefb
commit 907505ccf9

View File

@@ -29,35 +29,11 @@
function apc_cache_info($cache_type = '', $limited = false)
{
$aInfo = array();
$sRootCacheDir = apc_emul_get_cache_filename('');
$aInfo['cache_list'] = apc_emul_get_cache_entries($sRootCacheDir);
$sRootCacheDir = apcFile::GetCacheFileName();
$aInfo['cache_list'] = apcFile::GetCacheEntries($sRootCacheDir);
return $aInfo;
}
function apc_emul_get_cache_entries($sEntry)
{
$aResult = array();
if (is_dir($sEntry))
{
$aFiles = array_diff(scandir($sEntry), array('.', '..'));
foreach($aFiles as $sFile)
{
$sSubFile = $sEntry.'/'.$sFile;
$aResult = array_merge($aResult, apc_emul_get_cache_entries($sSubFile));
}
}
else
{
$sKey = basename($sEntry);
if (strpos($sKey, '-') === 0)
{
$sKey = substr($sKey, 1);
}
$aResult[] = array('info' => $sKey);
}
return $aResult;
}
/**
* @param array|string $key
* @param $var
@@ -71,40 +47,11 @@ function apc_store($key, $var = NULL, $ttl = 0)
$aResult = array();
foreach($key as $sKey => $value)
{
$aResult[] = apc_emul_store_unit($sKey, $value, $ttl);
$aResult[] = apcFile::StoreOneFile($sKey, $value, $ttl);
}
return $aResult;
}
return apc_emul_store_unit($key, $var, $ttl);
}
/**
* @param string $sKey
* @param $value
* @param int $iTTL time to live
* @return bool
*/
function apc_emul_store_unit($sKey, $value, $iTTL)
{
if ($iTTL > 0)
{
// hint for ttl management
$sKey = '-'.$sKey;
}
$sFilename = apc_emul_get_cache_filename($sKey);
// try to create the folder
$sDirname = dirname($sFilename);
if (!file_exists($sDirname))
{
if (!@mkdir($sDirname, 0755, true))
{
return false;
}
}
$bRes = !(@file_put_contents($sFilename, serialize($value), LOCK_EX) === false);
apc_emul_manage_new_entry($sFilename);
return $bRes;
return apcFile::StoreOneFile($key, $var, $ttl);
}
/**
@@ -118,45 +65,11 @@ function apc_fetch($key)
$aResult = array();
foreach($key as $sKey)
{
$aResult[$sKey] = apc_emul_fetch_unit($sKey);
$aResult[$sKey] = apcFile::FetchOneFile($sKey);
}
return $aResult;
}
return apc_emul_fetch_unit($key);
}
/**
* @param $sKey
* @return bool|mixed
*/
function apc_emul_fetch_unit($sKey)
{
// Try the 'TTLed' version
$sValue = apc_emul_readcache_locked(apc_emul_get_cache_filename('-'.$sKey));
if ($sValue === false)
{
$sValue = apc_emul_readcache_locked(apc_emul_get_cache_filename($sKey));
if ($sValue === false)
{
return false;
}
}
$oRes = @unserialize($sValue);
return $oRes;
}
function apc_emul_readcache_locked($sFilename)
{
$file = @fopen($sFilename, 'r');
if ($file === false)
{
return false;
}
flock($file, LOCK_SH);
$sContent = @fread($file, @filesize($sFilename));
flock($file, LOCK_UN);
fclose($file);
return $sContent;
return apcFile::FetchOneFile($key);
}
/**
@@ -165,36 +78,7 @@ function apc_emul_readcache_locked($sFilename)
*/
function apc_clear_cache($cache_type = '')
{
$sRootCacheDir = apc_emul_get_cache_filename('');
apc_emul_delete_entry($sRootCacheDir);
return true;
}
function apc_emul_delete_entry($sCache)
{
if (is_dir($sCache))
{
$aFiles = array_diff(scandir($sCache), array('.', '..'));
foreach($aFiles as $sFile)
{
$sSubFile = $sCache.'/'.$sFile;
if (!apc_emul_delete_entry($sSubFile))
{
return false;
}
}
if (!@rmdir($sCache))
{
return false;
}
}
else
{
if (!@unlink($sCache))
{
return false;
}
}
apcFile::DeleteEntry(utils::GetCachePath());
return true;
}
@@ -204,98 +88,246 @@ function apc_emul_delete_entry($sCache)
*/
function apc_delete($key)
{
return apc_emul_delete_entry(apc_emul_get_cache_filename($key));
if (empty($key))
{
return false;
}
$bRet1 = apcFile::DeleteEntry(apcFile::GetCacheFileName($key));
$bRet2 = apcFile::DeleteEntry(apcFile::GetCacheFileName('-'.$key));
return $bRet1 || $bRet2;
}
function apc_emul_get_cache_filename($sKey)
{
$sPath = str_replace(array(' ', '/', '\\', '.'), '-', $sKey);
return utils::GetCachePath().'apc-emul/'.$sPath;
}
/** Manage the cache files when a new cache entry is added
* @param string $sNewFilename new cache file added
*/
function apc_emul_manage_new_entry($sNewFilename)
class apcFile
{
// Check only once per request
static $aFilesByTime = null;
static $iFileCount = 0;
$iMaxFiles = MetaModel::GetConfig()->Get('apc_cache_emulation.max_entries');
if ($iMaxFiles == 0)
{
return;
}
if (!$aFilesByTime)
{
$sRootCacheDir = apc_emul_get_cache_filename('');
$aFilesByTime = apc_emul_list_files_time($sRootCacheDir);
$iFileCount = count($aFilesByTime);
if ($iMaxFiles !== 0)
{
asort($aFilesByTime);
}
}
else
{
$aFilesByTime[$sNewFilename] = time();
$iFileCount++;
}
if ($iFileCount > $iMaxFiles)
{
$iFileNbToRemove = $iFileCount - $iMaxFiles;
foreach($aFilesByTime as $sFileToRemove => $iTime)
{
@unlink($sFileToRemove);
if ($iFileNbToRemove-- === 0)
{
break;
}
}
$aFilesByTime = array_slice($aFilesByTime, $iFileCount - $iMaxFiles, null, true);
$iFileCount = $iMaxFiles;
}
}
static public $aFilesByTime = null;
static public $iFileCount = 0;
/** Get the list of files with their associated access time
* @param string $sCheck Directory to scan
* @param array $aFilesByTime used by recursion
* @return array
*/
function apc_emul_list_files_time($sCheck, &$aFilesByTime = array())
{
// Garbage collection
$aFiles = array_diff(@scandir($sCheck), array('.', '..'));
foreach($aFiles as $sFile)
/** Get the file name corresponding to the cache entry.
* If an empty key is provided, the root of the cache is returned.
* @param $sKey
* @return string
*/
static public function GetCacheFileName($sKey = '')
{
$sSubFile = $sCheck.'/'.$sFile;
if (is_dir($sSubFile))
$sPath = str_replace(array(' ', '/', '\\', '.'), '-', $sKey);
return utils::GetCachePath().'apc-emul/'.$sPath;
}
/** Get the list of entries from a starting folder.
* @param $sEntry string starting folder.
* @return array list of entries stored into array of key 'info'
*/
static public function GetCacheEntries($sEntry)
{
$aResult = array();
if (is_dir($sEntry))
{
apc_emul_list_files_time($sSubFile, $aFilesByTime);
$aFiles = array_diff(scandir($sEntry), array('.', '..'));
foreach($aFiles as $sFile)
{
$sSubFile = $sEntry.'/'.$sFile;
$aResult = array_merge($aResult, self::GetCacheEntries($sSubFile));
}
}
else
{
$iTime = apc_emul_get_file_time($sSubFile);
if ($iTime !== false)
$sKey = basename($sEntry);
if (strpos($sKey, '-') === 0)
{
$aFilesByTime[$sSubFile] = $iTime;
$sKey = substr($sKey, 1);
}
$aResult[] = array('info' => $sKey);
}
return $aResult;
}
/** Delete one cache entry.
* @param $sCache
* @return bool true if the entry was deleted false if error occurs (like entry did not exist).
*/
static public function DeleteEntry($sCache)
{
if (is_dir($sCache))
{
$aFiles = array_diff(scandir($sCache), array('.', '..'));
foreach($aFiles as $sFile)
{
$sSubFile = $sCache.'/'.$sFile;
if (!self::DeleteEntry($sSubFile))
{
return false;
}
}
if (!@rmdir($sCache))
{
return false;
}
}
else
{
if (!@unlink($sCache))
{
return false;
}
}
self::ResetFileCount();
return true;
}
/** Get one cache entry content.
* @param $sKey
* @return bool|mixed
*/
static public function FetchOneFile($sKey)
{
// Try the 'TTLed' version
$sValue = self::ReadCacheLocked(self::GetCacheFileName('-'.$sKey));
if ($sValue === false)
{
$sValue = self::ReadCacheLocked(self::GetCacheFileName($sKey));
if ($sValue === false)
{
return false;
}
}
$oRes = @unserialize($sValue);
return $oRes;
}
/** Add one cache entry.
* @param string $sKey
* @param $value
* @param int $iTTL time to live
* @return bool
*/
static public function StoreOneFile($sKey, $value, $iTTL)
{
if (empty($sKey))
{
return false;
}
@unlink(self::GetCacheFileName($sKey));
@unlink(self::GetCacheFileName('-'.$sKey));
if ($iTTL > 0)
{
// hint for ttl management
$sKey = '-'.$sKey;
}
$sFilename = self::GetCacheFileName($sKey);
// try to create the folder
$sDirname = dirname($sFilename);
if (!file_exists($sDirname))
{
if (!@mkdir($sDirname, 0755, true))
{
return false;
}
}
$bRes = !(@file_put_contents($sFilename, serialize($value), LOCK_EX) === false);
self::AddFile($sFilename);
return $bRes;
}
/** Manage the cache files when adding a new cache entry:
* remove older files if the mamximum is reached.
* @param $sNewFilename
*/
static protected function AddFile($sNewFilename)
{
if (strpos(basename($sNewFilename), '-') !== 0)
{
return;
}
$iMaxFiles = MetaModel::GetConfig()->Get('apc_cache_emulation.max_entries');
if ($iMaxFiles == 0)
{
return;
}
if (!self::$aFilesByTime)
{
self::ListFilesByTime();
self::$iFileCount = count(self::$aFilesByTime);
if ($iMaxFiles !== 0)
{
asort(self::$aFilesByTime);
}
}
else
{
self::$aFilesByTime[$sNewFilename] = time();
self::$iFileCount++;
}
if (self::$iFileCount > $iMaxFiles)
{
$iFileNbToRemove = self::$iFileCount - $iMaxFiles;
foreach(self::$aFilesByTime as $sFileToRemove => $iTime)
{
@unlink($sFileToRemove);
if (--$iFileNbToRemove === 0)
{
break;
}
}
self::$aFilesByTime = array_slice(self::$aFilesByTime, self::$iFileCount - $iMaxFiles, null, true);
self::$iFileCount = $iMaxFiles;
}
}
/** Get the list of files with their associated access time
* @param string $sCheck Directory to scan
*/
static protected function ListFilesByTime($sCheck = null)
{
if (empty($sCheck))
{
$sCheck = self::GetCacheFileName();
}
// Garbage collection
$aFiles = array_diff(@scandir($sCheck), array('.', '..'));
foreach($aFiles as $sFile)
{
$sSubFile = $sCheck.'/'.$sFile;
if (is_dir($sSubFile))
{
self::ListFilesByTime($sSubFile);
}
else
{
if (strpos(basename($sSubFile), '-') === 0)
{
self::$aFilesByTime[$sSubFile] = @fileatime($sSubFile);
}
}
}
}
return $aFilesByTime;
}
/** Get the file access time if TTL is managed
* @param string $sFilename
* @return bool|int returns the file atime or false if not relevant
*/
function apc_emul_get_file_time($sFilename)
{
if (strpos(basename($sFilename), '-') === 0)
/** Read the content of one cache file under lock protection
* @param $sFilename
* @return bool|string the content of the cache entry or false if error
*/
static protected function ReadCacheLocked($sFilename)
{
return @fileatime($sFilename);
$file = @fopen($sFilename, 'r');
if ($file === false)
{
return false;
}
flock($file, LOCK_SH);
$sContent = @fread($file, @filesize($sFilename));
flock($file, LOCK_UN);
fclose($file);
return $sContent;
}
return false;
static protected function ResetFileCount()
{
self::$aFilesByTime = null;
self::$iFileCount = 0;
}
}