Кэширование в PHP

Автор статьи: Harry Fuecks (Перевод: Муллин Сергей (SiMM), Кузьма Феськов)
Сайт Автора: php.russofile.ru
E-mail Автора: kuzma@russofile.ru
Дата публикации: 25.04.2006


Блочная буферизация

Упрощённый подход кэширует выводимый буфер как одну страницу. Однако этот подход лишает вас реальных возможностей, предоставляемых функциями управления выводом PHP, улучшающих производительность вашего сайта методом соответственно различающихся сроков жизни вашего контента.

Вне всякого сомнения, некоторые части отправляемой вами посетителю страницы изменяются очень редко, например, такие как шапку, меню и нижний колонтитул. Однако другие части, типа таблиц, содержащих обсуждения в форуме, могут изменяться довольно часто. Буферизация вывода может использоваться к кэшированию разделов страницы в отдельных файлах, затем создавать из них страницу – решение, устраняющее необходимость повторных запросов к базе данных, циклов while и т.д. Вы можете назначать каждому блоку страницы дату истечения срока, после которой пересоздаётся кэш-файл, или кроме того, вы можете включить в ваше приложение механизм, который будет удалять кэш-файл каждый раз, когда сохранённый в нём контент изменён.

Вот пример, демонстрирующий этот принцип:

Пример 5.3. 3.php (начало)
<?php
  
/
  * Запись кэш-файла
  * @param string contents – содержание буфера
  * @param string filename – имя файла, используемое при создании кэш-файла
  * @return void
  */
  
function writeCache($content, $filename) {
    
$fp = fopen('./cache/' . $filename, 'w');
    
fwrite($fp, $content);
    
fclose($fp);
  }

  
/
  * Проверка кэш-файлов
  * @param string filename – имя проверяемого кэш-файла
  * @param int expiry – максимальный «возраст» файла в секундах
  * @return mixed содержимое кэша или false
  */
  
function readCache($filename, $expiry) {
    if (
file_exists('./cache/' . $filename)) {
      if ((
time() - $expiry) > filemtime('./cache/' . $filename))
        return
FALSE;
      
$cache = file('./cache/' . $filename);
      return
implode('', $cache);
    }
    return
FALSE;
  }
?>

Первые две определённые нами функции, writeCache и readCache, используются соответственно для создания кэш-файлов и проверки их существования. Функция writeCache получает данные для кэширования в первом аргументе, и имя файла, используемое при создании кэш-файла. Функция readCache получает имя кэш-файла в первом параметре, вместе со временем в секундах, после которого кэш-файл должен считаться устаревшим. Если она сочтёт кэш-файл допустимым, скрипт вернёт его содержимое, в противном случае он вернёт FALSE, чтобы показать, что-либо кэш-файла не существует, либо он устарел.

В этом примере я использовал процедурный подход. Однако я не советую делать это на практике, поскольку это закончится очень грязным кодом (смотри последующие решения с лучшей альтернативой) и, вероятно, вызовет проблемы с блокировкой файла (например, что случится, когда кто-то обращается к кэшу в момент его обновления?).

Давайте продолжим этот пример. После того, как запущена буферизация вывода, начинается обработка. Сначала скрипт вызывает readCache, чтобы узнать, существует ли файл 3_header.cache, он содержит шапку страницы – заголовок HTML и начало тела. Мы используем функцию date PHP чтобы вывести время, когда страница фактически была сгенерирована, таким образом вы увидите различные кэш-файлы в работе, когда страница будет отображена.

Пример 5.4. 3.php (продолжение)
<?php
  
// Начинаем буферизацию вывода
  
ob_start();

  
// Обработка шапки
  
if (!$header = readCache('3_header.cache', 604800)) {
    
// Вывод шапки
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Страница, кэшированная поблочно</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
</head>
<body>
Время создания шапки: <?php echo date('H:i:s'); ?> <br />
<?php
    $header
= ob_get_contents();
    
ob_clean();
    
writeCache($header,'3_header.cache');
  }
?>

Что же случается когда кэш-файл не найден? Выводится некоторый контент и присваивается переменной при помощи ob_get_contents, после чего буфер очищается функцией ob_clean. Это позволяет нам перехватывать вывод по частям и сопоставлять их с индивидуальными кэш-файлами при помощи writeCache. Заголовок страницы теперь хранится как файл, который может быть использован без нашего вмешательства в пересборку страницы. Давайте вернёмся на секунду к началу условного оператора. Когда мы вызывали readCache, мы передали ей время жизни кэша в 604800 секунд (одна неделя), readCache использует время модификации кэш-файла, чтобы определить, является ли кэш-файл всё ещё допустимым.

Для содержимого (тела) страницы мы по прежнему будем использовать тот же процесс. Однако на сей раз при вызове readCache мы будем использовать время жизни кэша в пять секунд, кэш-файл будет модифицироваться каждый раз, когда он «старше» 5 секунд:

Пример 5.5. 3.php (продолжение)
<?php
  
// Обработка тела страницы
  
if (!$body = readCache('3_body.cache', 5)) {
    echo
'Время создания тела: ' . date('H:i:s') . '<br />';
    
$body = ob_get_contents();
    
ob_clean();
    
writeCache($body, '3_body.cache');
  }
?>

Нижний колонтитул эффективно изменять так же, как заголовок. После этого буферизация вывода останавливается и отображается содержимое трёх переменных, содержащих данные страницы:

Пример 5.6. 3.php (окончание)
<?php
// Обработка нижнего колонтитула страницы
  
if (!$footer = readCache('3_footer.cache', 604800)) {
?>

Время создания нижнего колонтитула: <?php echo date('H:i:s'); ?> <br />
</body>
</html>
<?php
    $footer
= ob_get_contents();
    
ob_clean();
    
writeCache($footer, '3_footer.cache');
  }
  
// останавливаем буферизацию
  
ob_end_clean();

  
// Выводим содержимое страницы
  
echo $header . $body . $footer;
?>

Конечный результат выглядит примерно так:

Время создания шапки: 17:10:42
Время создания тела: 18:07:40
Время создания нижнего колонтитула: 17:10:42

Заголовок и нижний колонтитул обновляются еженедельно, в время как тело модифицируется, когда оно старее 5 секунд.

Блок-схема на рисунке 5.1 суммирует методологию блочной буферизации.

Схема блочной буферизации выводаPHP - site_title.files/caching_.gif">
Рисунок 5.1. Блок-схема блочной буферизации вывода