Скажи кэшированию… иногда :)
by Larin
Когда я писал статью о Smaty, я не думал, что она вызовет столько внимания со стороны программистов. Как выяснилось шаблонизаторы, все еще остаются актуальной темой.
В комментариях к этой статье, зашел разговор и о кэшировании. Вопрос кэширования данных волнует многих начинающих программистов. Походив по форумам и сообществам, я понял тема актуальныя. :)
Конечно существуют готовые решения для кэширования данных, но я предложу свою реализацию. Она довольно проста и поможет понять сам механизм кэширования.
Мы не будем кэшировать старницу целиком, данный подход устарел. В реалиях “двух нулей” активно используется кэширование отдельных блоков. А если еще точнее, то мы будем кэшировать только чистые данные, т.е. никакого HTML.
И так, код:
<?php
abstract class Cache
{
public function __construct() {}
abstract function save($value, $valueID);
abstract function load($valueID, $time);
abstract function delete($valueID);
}
?>
И класс кэширования в файл:
<?php
class CacheFile extends Cache
{
private $path;
public function __construct($path = 'cache')
{
$this->path = $path;
}
public function save($value, $valueID)
{
$str_val = serialize($value);
$file_name = $this->pathCache($valueID) .
$this->nameCache($valueID);
$f = fopen($file_name, 'w+');
if (flock($f, LOCK_EX)) {
fwrite($f, $str_val);
flock($f, LOCK_UN);
}
fclose($f);
unset($str_val);
}
public function load($valueID, $time)
{
$file_name = $this->getPathCache($valueID) .
$this->nameCache($valueID);
if (!file_exists($file_name)) return false;
if ((filemtime($file_name) + $time) < time()) {
return false;
}
if (!$data = file($file_name)) return false;
return unserialize(implode('', $data));
}
public function delete($valueID)
{
$file_name = $this->getPathCache($valueID) .
$this->nameCache($valueID);
unlink($file_name);
}
private function pathCache($valueID)
{
$md5 = $this->nameCache($valueID);
$first_literal = array($md5{0}, $md5{1}, $md5{2}, $md5{3});
$path = $this->path . DIRSEP;
foreach ($first_literal as $dir) {
$path .= $dir . DIRSEP;
if (!file_exists(site_path . $path)) {
if (!mkdir(site_path . $path, 0777)) return false;
}
}
return site_path . $path;
}
private function getPathCache($valueID)
{
$md5 = $this->nameCache($valueID);
$first_literal = array($md5{0}, $md5{1}, $md5{2}, $md5{3});
return site_path . $this->path . DIRSEP .
implode(DIRSEP, $first_literal) . DIRSEP;
}
private function nameCache($valueID)
{
return md5($valueID);
}
}
?>
Константы site_path и DIRSEP задаем в конфигурационном файле, следующим образом:
<?php
define ('DIRSEP', DIRECTORY_SEPARATOR);
define ('site_path', dirname(dirname(__FILE__)) . DIRSEP);
?>
Методы getPathCache($valueID) и pathCache($valueID) нужны для уменьшения количества файлов в одном каталоге, путем построения дерева каталогов. Это существенно увеличит скорость чтения при большом количестве файлов кэша.
Пример использования:
<?php
function megaCalculation()
{
//Здесь происходят ресурсоемкие операции
//и ВОЗВРАЩАЕТСЯ какое-либо значение
}
$cache = new CacheFile('cache');
//Кэшируем на 10 минут
if (false === $var = $cache->load(’mega’, 10*60)) {
$var = megaCalculation();
$cache->save($var, 'mega');
}
?>
Вот и все. Кэширование – это просто. :)
Внимательный читатель спросит: “А зачем было делать класс Cache?” Все просто. Кэширование бывает не только на файлах. И в этом классе мы описываем обязательные методы для всех производных классов.
Можно еще кэшировать при помощи MemCache, баз данных (например, Berkeley DB) и т.д. Но это уже тема для следующих статей. :)