Кэширование php файлов

Привет всем! Недавно задумался об оптимизации одного из своих сайтов - он представляет из себя каталог продукции, и много информации берется из mysql базы данных. Мне захотелось свести к минимуму количество запросов к БД, да и на процессор хостера нагрузку уменьшить, поэтому я приступил к созданию кэширующего движка. Все оказалось проще простого :)

Мой сайт устроен следующим образом:
благодаря url rewriting'у обращения ко всем страницам идут через один и тот же index.php, т.е. если я пишу http://mysite.com/hello.html, то сервер обрабатывает запрос как если бы я написал http://mysite.com/index.php?page=hello, а index.php уже сам решает, что откуда брать и как выводить (кстати, если вы не знаете, как такое сделать у себя, отпишитесь в комментариях, расскажу отдельной заметкой; если знаете - идем дальше).
Для кэширования лучше и не придумаешь: код я добавил только в index.php и он автоматически будет кэшировать все страницы сайта, к которым я обращаюсь.

Ко всему прочему, я предусмотрел сжатие кэшированных страниц в gzip, чтобы серверу хостера не нужно было это делать на лету. А кому это не нужно, просто уберет соответствующий код из нижеследующих примеров.

Перво-наперво создадим в корне сайта папку cache и не будем задавать глупых вопросов, зачем она =) и поставим на нее права 777, чтобы скрипт записывать файлы туда мог. Второ-навторво (пардон мон франсе) почитаем немного теории (хотя, если вам проще разбираться на примерах, то можете сразу переходить дальше).

О file_exists(), header(), readfile(), fopen() и fwrite() я не пишу, это либо слишком просто, либо понятно по названию (ну на крайняк почитайте в гугле).

Нам понадобятся следующие функции:
ob_start() - этой функцией мы PHP процессору сообщаем о том, что весь вывод нужно переадресовывать на внутренний буфер. Т.е. не отправлять сразу браузеру, а сохранять в буфере, пока мы не дадим команду этот буфер вывести.

ob_implicit_flush($on_off) - передавая в параметре true или false, мы включаем или выключаем неявный поток. По умолчанию off, но если включено, то для каждой команды print/echo или другой команды вывода данные сразу же посылаются в браузер.

ob_get_contents() - функция возвращает содержимое внутреннего буфера, в котором мы накапливали весь "вывод".

ob_end_clean() - используя эту функцию, мы очищаем буфер вывода и отключаем его буферизацию. Данная функция должна быть использована до отправки контента в браузер.

gzencode($text, $compression_level) - с помощью этой функции мы сжимаем строку $text с уровнем сжатия $compression_level (не выставляйте его больше 3, смысла нет - в размере много не выиграете, а нагрузка на сервер будет значительной).

Итак, в самом начале начале своего index.php я определяю, сможет ли браузер пользователя прочесть сжатые данные:

if ( strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip') !== false ) $gzip = 'x-gzip';
if ( strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false ) $gzip = 'gzip';

Переменную $gzip мы используем чуть дальше, а пока мы вычислим md5-хэш запроса. Для этого будем использовать переменную $_SERVER['REQUEST_URI'], т.е. если пользователь заходит на http://mysite.com/hello.html, то $_SERVER['REQUEST_URI'] будет содержать /hello.html. А для чего md5? Да просто чтобы не было лишнего геморроя со строками типа /hello.html?param1=hey или /hello.html?par2=you&par3=hoho. А с md5 мы получим уникальное имя для нашего файла кэша.
$cache_file = md5($_SERVER['REQUEST_URI']);

Далее мы проверяем: если переменная $gzip назначена (см. выше), то сжатие поддерживается и мы проверяем, существует ли наш файл кэша (откуда он там возьмется станет понятно в конце).
if ( isset($gzip) and file_exists('cache/'.$cache_file.'.gz.html') ) // обратите внимание, что я его специально обозвал так, чтобы он заканчивался на .gz.html, это чтобы отличать его от несжатых файлов
{
 header('Content-Encoding: '.$gzip);
 readfile('cache/'.$cache_file.'.gz.html');
 // если браузер поддерживает сжатие и сжатый кэш-файл существует, то отправляем браузеру заголовок, мол, лови сжатый документ, и вываливаем ему содержимое
 // а вообще, если вам на это сжатие наплевать, то уберите из кода все, что его касается
 exit;
}
else if ( file_exists('cache/'.$cache_file.'.html') ) // если сжатие не поддерживается или сжатый файл не найден, проверяем, есть ли у нас несжатый
{
 readfile('cache/'.$cache_file.'.html');
 // если несжатый есть, выводим его
 exit;
}

Ну а дальше все до безумия просто (я серьезно, один мой друг загремел в дурку, когда прочел эту статью):
ob_start(); // включаем буферизацию
ob_implicit_flush(0); // отключаем неявный поток

и пишем все, что нашей душеньке угодно:
<html>
<head>
<title>Тра-ля-ля</title>
</head>
<body>
А чо писать-то?.. 8| С..ка, дебил, бляяяяяя
</body>
</html>

А в конце дописываем пхп-код:
$contents = ob_get_contents(); // получаем из буфера все, что туда наклали, и записываем в переменную $contents
ob_end_clean(); // очищаем буфер, как я и говорил, до того, как отправляем эти данные браузеру

// а заодно и сохраним всю эту ересь
$file=fopen('cache/'.$cache_file.'.html',"w+");
fwrite($file, $contents, strlen($contents));
fclose($file);
 
$gz = gzencode($contents, 2); // сжимаем
$file=fopen('cache/'.$cache_file.'.gz.html',"w+"); // сохраняем сжатую версию с именем, заканчивающимся на .gz.html, если помните, чтобы отличать от несжатой версии
fwrite($file, $gz, strlen($gz));
fclose($file);

// ну и не забываем вывести это все в браузер 
echo $contents;

Voila! Как видите, при первом обращении к странице, ее содержимое сохраняется в файлы, и в следующий раз данные берутся из этих файлов, а не из БД.

Пример кода можно скачать здесь 

Но так и быть, я продолжу и скажу еще пару слов о кэшировании запросов к базе данных.

Если вы не хотите делать свой сайт "статическим", как я описал выше, а просто снизить количество обращений к БД, то хорошим вариантом будет кэширование mysql запросов. Точнее закэшируем мы результат запроса, а в последующем просто будем проверять наличие файла кэша. Если есть файл, берем данные из него, нет файла - обращаемся к базе и сохраняем результат в кэш. Но сразу оговорюсь, что такое PHP MySQL кэширование делать желательно только для данных, которые обновляются нечасто. Или периодически этот кэш обновлять, скажем, раз в пару часов.

Для этого напишем простенькую функцию

function toJson($mysql_result) // да-да, хранить данные будем в виде json-строки
{
 $res = array();
 while( $row = mysql_fetch_assoc($mysql_result)) 
 {
 $res[] = $row; // получаем ассоциативный массив $row и добавляем его в наш массив $res
 }
 
 // после добавления всех строк кодируем данные в json
 return json_encode($res);
}

Ну в общем-то все. Дальше действуем приблизительно так:
$result = mysql_query('SELECT * FROM `girls` WHERE `hot` = 1'); // запрос может быть любой, само-собой

$cont = toJson($result);
file_put_contents('cache/db.txt', $cont); // расположение файла, его имя и расширение придумайте сами

А в последующем, когда необходимо, читаем этот файл, вместо обращения к БД:
if ( file_exists('cache/db.txt') )
{
 $result = json_decode(file_get_contents('cache/db.txt'));
}

После этого переменная $result представляет собой массив объектов, для перебора которых можно легко использовать foreach или обращаться к ним по индексу.
Например: $var = $result[5]->photo; или $var = $result[2]->phone;

Если вам удобнее все же с массивами, то в функции json_decode передайте вторым параметром true:
$text = json_decode(file_get_contents('cache/db.txt'), true);

и тогда обращаться к данным можно так:
$var = $result[5]['photo']; или $var = $result[2]['phone'];

Вот это один из вариантов, как можно реализовать на PHP кэширование запросов. Если возникают вопросы, я как всегда открыт для них в комментариях. 

 Жду с нетерпением
ваших комментариев!
 

Подписаться на RSS

Вы можете нажать "подписаться", чтобы следить за моими новостями!
Так вы всегда будете в курсе появления новостей на сайте =)
О том, что такое RSS можно прочитать здесь.

Подписаться

Подписаться на Twitter

Я специально зарегистрировался в Твиттере, чтобы вы могли следить за обновлениями на сайте =)

Подписаться

Envato marketplace А эти люди занимаются прокатом карнавальных костюмов и масок в Минске. К слову, я им делал сайт.