В этой статье я хотел бы рассказать о принципах кэширования и как его можно осуществить на PHP. Само по себе кэширование позволяет избежать лишней нагрузки на сервер, если это возможно. Представьте ситуацию когда у вас на сайте для формирования страницы делается большое количество SQL запросов и операций, которые используют очень много ресурсов сервера. Если материал на странице не обновлялся, то делать эти запросы и операции каждый раз не зачем, ведь результат один. Вот в таких случаях и используеться кэш. А при обновлении материала он удаляется(очищается) и создается новый. Сейчас я покажу пример самого простого кэша.
<?php
//начинаем буферизацию (все что будет после этой функции не будет выводитсься, а будет сохраняться в буфер)
ob_start();
//далее мы можем делать что угодно, например выводить контент
echo 'Контент страницы';
//теперь забираем все что выводили из буфера и чистим его
$content = ob_get_clean();
//теперь сохраняем страницу в файл, это и есть кэш
file_put_contents('cache.dat', $content);
echo $content;
?>
Тут я показал как создать кэш, а сейчас я покажу как можно проверить его существование и не создавать его заново, если он есть.
<?php
//проверяем есть ли кэш
if (file_exsits('cache.dat')) {
$content = file_get_contents('cache.dat');
} else {
//формируем страницу и создаем кэш
}
echo $content;
?>
И так это самый простой способ что либо закэшировать, но как вы заметили тут мы работаем постояно с одним файлом а на сайте страниц может быть много и нам надо знать какой файл к какой странице относиться. Для этого мы можем делать имя файла уникальным для каждой страницы. Например брать строку запроса и делать имя файла с ее кэшем в виде md5 хеша этой строки запроса.
<?php
$cache_file = md5($_SERVER['REQUEST_URI']) . '.dat';
if (file_exsits($cache_file)) {
$content = file_get_contents($cache_file);
} else {
ob_start();
echo 'Контент страницы';
$content = ob_get_clean();
file_put_contents($cache_file, $content);
}
echo $content;
?>
Вот так например:). Но и тут еще не все а что делать если зарегистрированый юзер и гость видят разное содержимое на одной и той же странице? Нам надо это учесть. Допустим мы узнаем о том что пользователь зареган из сессии, тогда мы можем делать приставку к имени файла для зарегистрированых юзеров.
<?php
$cache_file = md5($_SERVER['REQUEST_URI']) . '.dat';
if (isset($_SESSION['user'])) $cache_file = 'autorized_' . $cache_file;
?>
Ну вот пока это все. Осваивайте эту технологию а я в следующей части расскажу как кэшировать отдельные блоки на странице и покажу более совершенный способ кэширования.
И так, теперь мы знаем как реализовать на сайте простое кэширование, используя PHP. Но это подойдет разве что для гостевой или чего нибудь еще в таком духе. А если у нас в распоряжении сайт со множеством групп пользователей и каждый видит уникальную страницу? Конечно можно просто делать приставку к имени файла в виде названия группы. В таком случае все будет пучком, но если кэш одной страницы весит 5 кб, то теперь, если у нас скажем 10 групп пользователей, кэш этой же страницы будет весить уже 50 кб. И для нормального сайта с 5000 страниц размер кэша может составить 250 000 кб или 250 Мб. Казалось бы не так много, но в реальных условиях все куда хуже и плохо реализованный кэш может завалить вас гигабайтами ненужной информации.
Выход из этого положения - блочное кэширование. Блочное кэширование - это когда мы кэшируем не всю страницу сразу, а отдельные ее элементы. Это также помогает решить нам такие проблемы как разные сроки хранения у разных блоков и предотвращение кэширования некоторых частей сайта. Например если у вас на сайте есть блок, выводящий время, то закэшировав его, время на часах не будет меняться. Не правда ли это как то лажово? :)
И так немного примеров:
<?php //узнаем группу юзера если она есть. Далее это будет приставка к нашим кэшам с админбаром
$user_group = (!empty($_SESSION['user']['group'])) ? $_SESSION[''user]['group'] : '';
$adm_bar_cache = $user_group . '_adm_bar.dat';
if (file_exists($adm_bar_cache)) {
$adm_bar = file_get_contents($adm_bar_cache);
} else {
ob_start();
//формируем админбар
$adm_bar = ob_get_clean();
file_put_contents($adm_bar_cache,$adm_bar);
}
//теперь формируем основную часть
$pref = md5($_SERVER['REQUEST_URI'] . $_SERVER['QUERY_STRING']);
$content_cache = $pref . '.dat';
if (file_exists($content_cache)) {
$adm_bar = file_get_contents($content_cache);
} else {
ob_start();
//формируем контент
$content = ob_get_clean();
file_put_contents($content_cache, $content);
}
//теперь у нас есть контент и админ бар. Надо их соединить и сформировать некэширующиеся блоки
$not_cached = date("Y-m-d"); //это то что не кэшируется
$uotput = $adm_bar . $content . $not_cached;?>
И так немного расскажу что же я проделал. Сперва я сформировал приставку для файлов кэша админской панели, проверив есть ли в сессии идентификатор группы и если он есть, то взял в качестве приставки именно этот идентификатор, а если нет, то оставил эту приставку пустой. Можно сделать и более элегантное решение, но это же пример епта. :) Далее я сформировал админбар. Это вы помните из прошлой статьи, я проверил кэш и если его нет я его создал, а если есть, то взял все из него.
Далее я таким же макаром собрал контент часть. Только вместо приставки я уже юзал не группу юзера, а строку запроса.
<?php $_SERVER['REQUEST_URI'] . $_SERVER['QUERY_STRING']?>
это файл к которому идет обращение и все что после "?", то есть GET. Ну а дальше я просто соединил это, собрав все в кучу. В реальной жизни конечно соединять прийдется другим способом, в зависимости от того как устроены ваши шаблоны, но с этим уже никаких проблем возникнуть не должно. Например можно поставить блок на свое законное место при помощи замены какой то метки.
<?php $content = str_replace('{ADMIN_BAR}', $adm_bar, $content);?>
Ну или что-то в таком духе.
С этим все понятно, но как чистить кэш по времени или при создании/добавлении материала? Вот об этом я и расскажу дальше. Сразу предупреждаю, тут я покажу самые простые примеры, а за более сложными схемами обращаться на форум. На форуме я с радостью перетру любые темы. И так я предлагаю записывать время создания файла кэша первой строкой в тот самый файл и при чтении просто проверять это время, а при выдачи кэша из файла, обрезать эту строку со временем.
<?php file_put_contents('cache.dat', (time() + 86400) . "\n" . $content);?>
Таким образом мы записали первой строкой в файл тукущую метку времени + 86400 секунд(одни сутки). Это подразумевает что кэш мы будем хранить ровно сутки. Для этого при его получении делаем следующее:
<?php if (file_exists($cache_file)) {
$cache = file_get_contents($cache_file);
if (preg_match('#^(\d+)#', $cache, $match)) {
//если метка времени в файле больше текущей то мы вырезаем ее и делаем содержимое файла контентом
if ($match[1] > time()) $content = substr($cache, strlen($match[1]));
}
}
//тут например можно проверить пустой ли контент и если да, то начать его формировать и сохранять в кэш
//Если с кэшем у нас все было ОК и мы получили контент из кэша то на этом этапе он не будет пустым и следовательно
//это действие пропустится
if (empty($content)) {
//формируем
}?>
Ну я еще хотел рассказать как можно удалять кэш при создании материала, но понял что здесь и рассказывать нечего. Например после добавления записи в базу можно удалять все файлы из папки кэша. Вот и все. Но это очень не разумно на больших сайтах, так как материалы могут добавляться каждую минуту и тогда смысл от всего кэширования теряется и даже наоборот - сайт только тормозит. Выход в этой ситуации - удаление кэша по ключам. То есть если добавилась новоть, удаляем только кэш новостей, а если добавилась статья, удаляем кэш статей. Но как это реализовать я расскажу в следующей части этого цикла статей. А пока вы можете попрактиковатсья с тем о чем говорилось тут, я лишь дам подсказку: В файле кэша можно хранить не только метку времени;)