Php простое кэширование


Содержание

Простое кэширование PHP

У меня есть динамическая таблица стилей, созданная из следующего кода.

Каков наилучший способ кэширования этого файла и загрузки его, если файл существует.

Я думаю, что я сейчас здоров.

И для проверки я делаю:

Там простая реализация кэша здесь, которые могут быть использованы в коде, как ваша.

Почему бы не кешировать весь сайт, а не только файл CSS. Проверьте этот кэш.

Это простой пример. Кэширование вашего CSS может сделать вот так:

PHP Cache вся веб-страница: вы можете использовать phpFastCache для кэширования всей веб-страницы тоже. Это простой пример, но в реальном коде вы должны разделить его на 2 файла: cache_start.php и cache_end.php. Cache_start.php будет хранить начальный код до ob_start(); и cache_end.php начнется с GET HTML WEBPAGE. Затем ваш index.php будет включать cache_start.php в начало и cache_end.php в конце файла.

Сократить вызовы базы данных Класс кэширования PHP для базы данных: на вашем сайте есть 10 000 посетителей, которые подключены к сети, и ваша динамическая страница должна отправлять 10 000 одинаковых запросов в базу данных при каждой загрузке страницы. С phpFastCache ваша страница отправляет только 1 запрос в БД и использует кеш, чтобы обслуживать 9999 других посетителей.

Я предлагаю вам кеш APC. Это стабильный и быстрый кеш памяти для PHP. Я использую его для всего своего приложения, но вы можете использовать его только для сохранения переменной, если хотите. Вы можете сохранить содержимое $ stylesheets [‘open_sans’] (используя функцию apc_store) в переменной кэша, а затем оттуда будет использоваться (вы будете использовать apc_fetch) из оперативной памяти (быстро), до тех пор, пока вы хотите установить время истечения.

Посмотрите другие вопросы по меткам php caching или Задайте вопрос

Простое кэширование страниц

Простое кэширование страниц. Для чайников. На PHP.

Кэширование — замечательная вещь. Часто (теперь — почти всегда) страницы на сайтах генерятся динамически. Это теперь модно. Однако же — в реальности — почти всегда страница собирается заново гораздо чаще, чем она изменяется. Грубо говоря, мы опубликовали новый документ, и при каждом обращении к нему он заново считывается, скажем, из базы данных, заново прогоняется через шаблон и прочее и прочее. Мы снова и снова делаем одну и ту же работу.

А можно просто один раз сделать, а потом сохранить результат этой работы. И при каждом запросе выдавать готовый результат, а не делать все заново. Это и есть кэширование.

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

Единственная проблема — это устаревание кэша. Допустим, что данные на странице изменились, а кэш страницы — еще нет, и пользователю будет выдаваться старая версия страницы. Способы борьбы:

1. Выставлять более-менее приемлимое время устаревания кэша. Например, через 10 минут страница устаревает и кэш генерится заново. Минусы: возможна ситуация, когда пользователю 9 минут будет показываться старая страница.

2. Кнопка « очистить кэш». В некоторых системах вообще нет кнопки « очистить кэш», вместо нее есть кнопка « перегенерить сайт целиком». Нажимаем на эту кнопку — и весь сайт генерится в статичные файлы, то есть, фактически, в кэш. Минусы: стрельба по воробьям из пушки. Мы поменяли одну страницу, а перегенерить приходится весь сайт.

3. « Умная» очистка кэша. Очищается только кэш той страницы, которую мы изменили. Минус: часто изменение одной страницы затрагивает и несколько других. Главное — понять, каких именно и очистить кэш у них тоже.

Лично у меня реализованы все три метода.

А теперь — в двух словах, как сделать себе кэш. Чудесные функции PHP — ob_start и иже с ней позволяют не выводить страницу в браузер, а, например, сохранить ее в переменную. Про это я уже писал.

Это вставляем в начале страницы:

Как это работает:

Берем адрес страницы, вычисляем из него md5. Это будет использоваться, как идентификатор страницы. Например, для УРЛа technology/php/caching md5 будет всегда одим и тем же. Этим мы и воспользуемся.

Файлы кэша будут лежать в директории cache. Смотрим, сколько секунд (filemtime) исполнилось файлу с кэшэм данной страницы. Если он не очень старый (меньше 600 секунд) и вообще есть — выводим его (include).

Если нет — то включаем ob_start и продолжаем дальше.

Это вставляем в конец страницы:

$cache = ob_get_contents();
ob_end_clean ();
echo $cache;

$fp = @fopen («cache/$crc», «w»);
@fwrite ($fp, $cache);
@fclose ($fp);

Как это работает:

Считываем содержимое буфера (ob_get_contents). Получаем в переменной $cache то, что должно было выводится в браузер. Выводим в бразуер, раз должно (echo).

Записываем содержимое буфера ($cache) в директорию cache в файл $crc.

Все. Теперь при следующем обращении к странице с этим адресом скрипт (смотрите первую часть) будет смотреть, есть ли соответствующий файл в кэше и если он еще не устарел — просто выводить его и прекращать обрабатывать страницу (exit).

Примерно так оно и работает. Таким образом, страница выводится из кэша где-то за 0.001-0.004 секунды. Выигрыш процессорного времени налицо.

Разумеется, я описал только общий принцип, у меня сделано несколько по-другому.

Php простое кэширование

В данной статье рассматривается один из самых простых способов кэширования подкачиваемых файлов, SQL запросов и других медленно создаваемых частей ваших страниц. Язык программирования: PHP

Что должно быть на главной странице ?? Правильно, там должно быть понемножку от каждого раздела сайта. И конечно Ваша страница строится автоматически, исходя из содержимого форумов, новостных лент и других разделов сайта.

Сколько SQL запросов выполняется при загрузке главной страницы ?? Сколько блоков подкачивается с других сайтов ??

Сейчас мы рассмотрим простой и эффективный способ кэширования данных такого рода.

В качестве примера рассмотрим случай импорта новостей с внешнего сервера: _http://news.novgorod.ru/ultramode.txt

Наша задача получить этот файл, обработать его и вывести его пользователю. Программа делающая это, выглядит следующим образом:

При каждой загрузке нашей страницы происходит обращение к внешнему серверу news.novgorod.ru, оттуда по протоколу HTTP скачивается файл ultramode.txt. А теперь представим ситуацию, что сервер находится на другом конце планеты и соединение, а тем более передача файла занимает достаточно больше время. Или более того, сервер может быть временно недоступен.

Вывод. Зачем каждый раз подгружать файл ultamode.txt, если новости обновляются не чаще одного раза в два-три часа?? Правильно. Надо кэшировать.

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

Алгоритм работы следующий:
Считываем кэшированную версию
Если кэшированная версия устарела, то на ее место считываем файл _http://news.novgorod.ru/ultramode.txt, заменяем первую строку на текущее время и сохраняем в кэш.
Оформляем и выводим данные пользователю.

Ниже приведен код проделывающий все это:

PHP — как сделать толковый кэш?

Есть сайт — новостная лента. Двиг — самописный, PHP + MySQL. В данный момент с каждым посещением страницы информация формируется из БД.

Срочно надо перейти с динамики на статику.

То, как я придумал это сделать

  • При первом посещении странички c >
  • При повторном посещении этой странички — смотрим дату создания файла 1500.html, если дата меньше 15 минут от тек. времени, инклудом выводим 1500.html. Если больше 15 минут — снова берем из БД и перезаписываем файл.

Вопросы
1. Комментарии к новостям. Получается, при добавлении нового комментария необходимо будет убивать файл 1500.html? Сайт живой, комментов много. Вот не знаю, я хард не задолбаю такими запросами?

2. Статистика количества просмотров новости. При каждом посещении продолжать делать «UPDATE `views` = `views` + 1» или может есть какое-то другое решение? А то выходит не до конца статика получится, если будут постоянно запросы в БД улетать.

Поделитесь, пожалуйста, мыслями по поводу такой реализации. Может кто-то уже внедрял что-то подобное, какие могут возникнуть проблемы? Может сущетсвуют подводные камни, о которых я пока не подозреваю?

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

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

Прежде чем показывать код, Вы должны заранее осознать алгоритм. Кэширование — это просто сохранение сгенерированной страницы в отдельный файл . То есть получили HTML-код страницы и сохранили его в отдельный файл. Затем при следующем обращении к скрипту заново страница не генерируется, а берётся HTML-код из этого файла. Разумеется, если при генерации речь может идти даже о десятках секунд (бывает и такое), то здесь речь идёт о сотых долях секунды. Разница очевидна.

Теперь сам код. Вам нужно создать 2 специальных файла. Первый файл будет заниматься выводом кэша, а второй — созданием кэша.

Начнём с первого файла, который назовём read_cache.php :

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

Теперь создадим файл write_cache.php , который будет заниматься записью в файл того, что у нас накопилось в буфере:

И теперь в любом PHP-файле на сайте, отвечающем за вывод страницы, можно включить кэширование следующим образом:

Таким образом, теперь Вы можете все свои проблемные страницы начать кэшировать. Но не забывайте, что пока кэш живёт, любые обновления пользователь видеть не будет . Поэтому делайте кэширование на PHP только для тех страниц, которые редко обновляются.

Основной проблематикой кэширования является быстрота реакции на запросы к основным системам хранения и обработки входящей и исходящей структурированной информации.

Представьте, что необходимо осуществить быструю передачу информации, однако скорость доступа к данным крайне низкая. Или другая ситуация: скорость хорошая, но мало доступной памяти или ширина канала недостаточная, или процессорные и дисковые факторы мешают осуществить задачу. В этом случае кэширование – это единственный выход из ситуации.

Виды кэширования

Необходимо понимать, что работу с данными можно производить как на стороне клиента, так и на сервере. Притом, серверная обработка данных централизована и имеет ряд несомненных преимуществ (особенно для службы поддержки).

Существует несколько видов кэширования, предлагаем рассмотреть каждый вид, его особенности и рекомендации по применению:

1. Браузерное кэширование или клиентское кэширование
1.1 Кэширование файлов и картинок

Это первый уровень кэширования, который состоит в отдаче заголовка «expired» и заголовка «304 Not Modified» . Наиболее эффективным считается кэширование на 2 недели.

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

1.2 Кэширование https
1.3 Кэширование центра сертификации

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

1.4 Кэширование страниц

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

Как правило, кэш подразделяется по типу пользователей:

Данное разделение обусловлено уникальностью контента, для каждого авторизованного пользователя и общностью контента для гостевых пользователей. В большинстве сайтов не авторизованный пользователь не может изменять содержимое сайта, а значит и влиять на его содержимое.

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

2. Серверное кэширование
2.1 Кэширование страницы целиком

Пожалуй, любой когда-либо мечтал о сайте, работающем со скоростью «ping» или быстрее.
Но и у этого типа кэша есть свои минусы: например, невозможность кэшировать страницы для авторизованного пользователя, либо пользователя, содержимое страницы которого зависит от текущих переменных пользователя.

Используйте этот кэш, если серверу известны все статичные состояния внешних данных, такие как: uri, get (без дополнительных параметров), пользователь не авторизован — то есть, фактически, это идеальное состояние страницы для гостевых пользователей. Учитывайте тот факт, что при таком кэшировании архитектура сайта или приложения всегда должна однотипно обрабатывать входящие запросы и отдавать однотипные ответы. Такое состояние есть в любом приложении или сайте, его нужно лишь отследить и применить к нему кэш.

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

2.2 Кэширование результатов компиляции php-файлов

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

2.3 Кэширование отдельных блоков страницы

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

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

2.4 Кэширование php на основе неразделяемых ресурсов
2.5 Кэширование php на основе общих ресурсов
2.6 Кэширование mysql на основе query cache

Наверняка, вы регулярно сталкивались с ситуацией, когда необходимо отдать новые материалы, дата публикации которых уже разрешена текущим timestamp? Проще говоря,

Что происходит в данном коде? Первые 5 строк создают имя файла кеша в соответствии с текущем PHP файлом. Например, если мы используем файл с именем list.php , файл кеша будет иметь вид cached-list.html .

Строка 6 создает переменную $cachetime , которая определяет время жизни кеша.

Строки с 9 по 13 определяют условное выражение, которое служит для проверки наличия файла с именем, определенным в переменной $cachefile . Если файл существует, вставляется комментарий и файл, определенный в переменной $cachefile . Затем выражение exit прерывает выполнение скрипта и файл отправляется браузеру клиента. То есть, если найден статичный файл, то PHP код не будет выполняться сервером.

Строка 14 создает буфер, если файл, определенный переменной $cachefile не найден.

Шаг второй. Создаем файл bottom-cache.php

Теперь создаем второй файл PHP с именем bottom-cache.php и копируем в него следующий код:

Если файл с именем, определенным в переменной $cachefile отсутствует на сервере, выполняется данный код и создается файл. При следующем обращении к странице статичный $cachefile будет обслуживать браузер клиента вместо выполнения всего кода скрипта PHP.

Шаг три. Включаем файлы кеширования в код страницы

Теперь у нас есть два необходимых файла. Просто включаем их в страницу PHP, которую нужно кешировать. Файл top-cache.php нужно включить в начало страницы, а файл bottom-cache.php — в конце:

Теперь, если проверить кеширование на медленных страницах, то можно убедиться, насколько они стали быстрее загружаться.

В старые добрые времена, когда создание web-сайтов представляло из себя такое простое занятие, как набор нескольких HTML -страниц, отправка web-страниц в браузер была простой отправкой файла web-сервером. Посетители сайта могли видеть эти небольшие, исключительно текстовые странички, почти мгновенно (если не считать пользователей медленных модемов). Как только страница была загружена, браузер кэширует её где-нибудь на локальном компьютере, чтобы в случае повторного запроса страницы, можно было взять его локальную версию из кэша, послав лишь короткий запрос, чтобы убедиться, что страница на сервере не была изменена. Запросы обрабатывались быстро и как можно эффективней, и все были счастливы (кроме использующих модемы 9600 бод).

Появление динамических web-страниц изменило положение вещей в худшую сторону, эффективно «сломав» эту модель обслуживания web-страниц благодаря наличию двух проблем:

  1. Когда сервером получен запрос динамической web-странички, производится некоторая промежуточная обработка, например синтаксический анализ (парсинг) скрипта движком PHP , которая должна быть завершена. Благодаря этому получаем задержку перед тем, как web-сервер начнёт отправку вывода в браузер. Для простого PHP -скрипта это не существенно, но для более сложного приложения движок PHP может выполнить много действий прежде чем страница будет готова для отправки. Эти дополнительные действия приводят к заметной задержке между запросами пользователей и реальным отображением страниц в их браузерах.
  2. Типичный web-сервер, например Apache, использует время модификации файла чтобы правильно сообщить web-браузеру состояние кэша запрашиваемой странички. Для динамических web-страниц, фактически PHP -скрипт может изменяться только изредка, в то время как отображаемый им контент, возможно располагающийся в базе данных, изменяется часто. Web-сервер не имеет возможности знать о наличии изменений в базе данных, тем не менее он не отправляет дату последней модификации. Если клиент (браузер) не получает никакого признака того, как долго данные являются корректными, он предполагает, что в следующий раз необходимо запросить страничку по новой. Web-сервер всегда будет отвечать обновлённой версией странички, независимо от того, изменились ли данные. Чтобы избежать этого недостатка большинство web-разработчиков используют мета-тэги или HTTP -заголовки, чтобы сообщить браузеру никогда не использовать кэшированную версию странички. Однако это отрицает естественную способность web-браузера кэшировать web-страницы и обладает некоторыми существенными недостатками. Например, содержание динамической странички может изменяться раз в сутки, поэтому выгода, получаемая от наличия даже 24-часового кэширования странички браузером, очевидна.

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

Как я предотвращаю кэширование страницы браузерами?

Прежде чем мы рассмотрим методы клиентского и серверного кэширования, в первую очередь мы должны понять, как вообще предотвратить кэширование страниц web-браузером (и прокси-серверами). Основной способ достижения этого использует мета-тэги HTML:

Вставив прошедшую дату в мета-тэг Expires, вы сообщаете браузеру, что кэшированная копия странички всегда является устаревшей. Это значит, что браузер никогда не должен кэшировать страницу. Мета-тэг Pragma: no-cache довольно хорошо поддерживаемое соглашение, которому следует большинство web-браузеров. Обнаружив этот тэг, они обычно не кэшируют страницу (хотя никаких гарантий нет, это всего лишь соглашение).

Это хорошо звучит, но есть две проблемы, связанные с использованием мета-тэгов:

  1. Если тэг не существовал когда страница была запрошена браузером впервые, но появляется позже (например, вы модифицировали включаемый файл pageheader.php который является шапкой каждой web-страницы), браузер останется в блаженном неведении и воспользуется свей кэшированной копей оригинала.
  2. Прокси-серверы, кэширующие web-страницы, как например общий ISP , вообще не будет исследовать непосредственно содержимое HTML -документа. Вместо этого они полагаются только на web-сервер, с которого пришли документы, и протокол HTTP . Иными словами, web-браузер может считать, что не должен кэшировать страницу, но прокси-сервер между браузером и вашим web-сервером вероятно не знает этого – и продолжит отправлять клиенту ту же самую, уже устаревшую, страницу.

Лучший подход состоит в том, чтобы использовать непосредственно протокол HTTP с помощью функции PHP header() , эквивалентно приведённым выше двум мета-тэгам:

Мы можем пойти на один шаг вперёд, воспользовавшись заголовком Cache-Control совместимым с браузерами, поддерживающими HTTP 1.1:

Header(«Expires: Mon, 26 Jul 1997 05:00:00 GMT»); header(«Cache-Control: no-store, no-cache, must-reval );

Это гарантирует, что никакой web-браузер или промежуточный прокси-сервер не будет кэшировать страницу, таким образом посетители всегда получат самую последнюю версию контента. Фактически, первый заголовок должен быть самодостаточным, это лучший способ гарантировать, что страница не кэшируется. Заголовки Cache-Control и Pragma добавлены с целью «подстраховаться». Хотя они не работают во всех браузерах или прокси, они отловят некоторые случаи, в которых Expires не работает должным образом (например, если дата на компьютере клиента установлена неправильно).

Конечно, полный отказ от кэширования обеспечивает нас проблемами, которые мы обсуждали в начале этой главы. Сейчас мы рассмотрим решение этих проблем.

Internet Explorer и кэширование загрузки файлов

Если при обслуживании загрузки файла PHP -скриптом используются такие заголовки, как например Content-Disposition: attachment, filename=myFile.pdf или Content-Disposition: inline, filename=myFile.pdf у вас будут проблемы с Internet Explorer ’ом, если вы сообщите браузеру не кэшировать страницу.

Internet Explorer оперирует загрузкой довольно необычным образом, выполняя два запроса к web-сайту. Первый запрос загружает файл и сохраняет его в кэше, пока не будет создан второй запрос (без сохранения отклика). Этот запрос вызывает процесс передачи файла конечному пользователю в соответствии с типом файла (например, запускает Acrobat Reader , если файл является PDF -документом). Это значит, что если вы отправили заголовки, запрещающие браузеру кэшировать страницу, Internet Explorer удалит файл между первым и вторым запросом, в результате чего конечный пользователь ничего не получит. Если файл, который вы отдаёте PHP -скриптом, не изменяется, одним из простейших решений будет убрать «запрещающие кэширование» заголовки из скрипта.

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

Как я могу захватить данные на стороне сервера для кэширования?

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

Несколько слов о кэшировании при помощи шаблонов

Как мне управлять кэшированием на стороне клиента средствами PHP?

Пришло время посмотреть на механизм, который позволит нам контролировать кеш на стороне клиента средствами PHP . Этот подход будет работать только если вы используете PHP в связке с сервером Apache , поскольку мы будем использовать функцию getallheaders() , чтобы получить заголовки, передаваемые браузером. Эта функция работает только в Apache .

Новые имена функций

Если вы используете PHP 4.3.0 с Apache , HTTP-заголовки доступны функцией apache_request_headers() и apache_response_headers() . Функция getallheaders() стала псевдонимом для новой функции apache_request_headers() .

Механизмом для работы с кэшем web-браузера вновь является HTTP . Множество заголовков вовлечёны в инструктирование web-браузеров и прокси-серверов независимо кэшировать страницу, ситуация осложняется тем фактом, что некоторые из них доступны только с HTTP 1.1.

Проверка HTTP-заголовков в вашем браузере

Простым но очень удобным инструментом для проверки заголовков запросов и откликов является LiveHttpHeaders – аддон к браузеру Mozilla . Необходимо точно знать, какие заголовки посылает ваш скрипт, особенно когда вы имеете дело с заголовками кэширования HTTP .

Для простоты мы рассмотрим только заголовки кэширования HTTP 1.0, а именно Expires , Last-Modified и If-Modified-Since , а также статус-код HTTP 304 (Not Modified) .

Другие заголовки, доступные с HTTP 1.1, например Cache-Control и ETag , предназначены для обеспечения расширенного механизма, который может использоваться совместно с состоянием web-сессии, иными словами, версия данной страницы, отображаемой неавторизованному посетителю, может значительно отличаться от отображаемой авторизованному пользователю. Заголовки HTTP 1.1 изначально добавлялись для того, чтобы позволить кэшировать такие страницы.

Истечение срока жизни страницы

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

Пример 7. 6.php

Функция setExpires отправляет заголовок HTTP Expires с будущим временем, заданном в секундах. Вышеприведённый пример показывает текущее время по Гринвичу и выводит ссылку, которая вам позволяет перейти на страницу вновь. Используя кнопку Refresh вашего браузера, вы можете сообщить браузеру о желании обновить кэш. Используя ссылку, вы увидите, что время изменяется только раз в 10 секунд.

Даты и время в HTTP

Даты в HTTP всегда вычисляются относительного меридиана времени Гринвича (GMT). Функция PHP gmdate() точно такая же функция, как date() , за исключением того, что она автоматически компенсирует время по Гринвичу, основанное на системных часах и настройках региона вашего сервера.

Когда браузер сталкивается с заголовком Expires , он кэширует страницу. Все последующие запросы страницы, сделанные до указанного времени истечения срока жизни, используют версию страницы из кэша, никаких запросов к web-серверу при этом не происходит.

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

Время изменения страницы

Более практично использовать заголовки Last-Modified и If-Modified-Since , доступные в HTTP 1.0. Технически он известно как выполнение условного GET-запроса, вы возвращаете любой контент, основываясь на условии пришедшего заголовка запроса If-Modified-Since .

При использовании этого метода вы должны отправлять заголовок Last-Modified каждый раз, когда обращаются к вашему PHP-скрипту. При следующем запросе страницы браузером, он отправит заголовок If-Modified-Since , содержащий время, по которому ваш скрипт может определить, обновлялась ли страница со времени последнего запроса. Если это не так, ваш скрипт посылает код статуса HTTP 304 , чтобы указать, что страница не изменялась, не выводя при этом содержимого страницы.

Устанавливаем время модификации кэш-файла этой строкой: $lastModified = filemtime($cache_file);

Затем, используя время модификации кэш-файла, мы посылаем заголовок Last-Modified . Нам нужно посылать её для каждой предоставляемой страницы, чтобы вынудить браузер посылать нам заголовок If-Modified-Since с каждым запросом.

// Выдаём заголовок HTTP Last-Modified header(«Last-Modified: » . gmdate(«D, d M Y H:i:s», $lastModified) . » GMT»); \n \n»; echo » \n»; echo » \n»; echo » \n»; ?>

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

Будьте осторожны при тестировании любого кэширования, выполненного в таком стиле, если вы сделаете это неправильно, вы можете заставить ваших посетителей всегда иметь устаревшие копии вашего сайта.

Кэширование ваших страниц в 5 шагов

Кэширование ваших страниц может оказаться красивым и полезным механизмом, особенно, если они генерируются средствами PHP и делают множество SQL запросов. Как только вы примените кэширование, ваш сервер тут же снизит нагрузку и перестанет съедать много памяти на генерацию страниц — он просто будет загружать их из КЭШа. Я покажу вам, как PHP может кэшировать страницы и, в дальнейшем, вы сможете тратить на это минут 5.

Расмотрим технологию кэширования пошагам:

  1. В домашней директории создаем файлы .htaccess , start_cache.php , end_cache.php , а также папку с названием cache_files .
  2. Папке cache_files необходимо проставить атрибуты 777 .
  3. Внутри .htaccess файла пропишите следующие строки: php_value auto_prepend_file /home/username/public_html/start_cache.php php_value auto_append_file /home/username/public_html/end_cache.php Строку /home/username/public_html/ необходимо заменить на путь к вашей домашней директории.
  4. В скрипт start_cache.php помещаем следующий код: Не забывайте исправлять путь /home/username/public_html/ на путь к вашей домашней директории.
  5. А следующий код поместите в скрипт end_cache.php :

Все ваши страницы будут кэшироваться на 3600 секунд = 1 час. Этот параметр вы легко можете поменять в скрипте start_cache.php . Кэш страниц будет сохранен в папке cache_files .

Совершенно очевидно, что в данном случае атрибуты 777 являются определенным нарушением безопасности. В связи с чем, рекомендую вынести папку cahce_files за пределы public_html , например, поместить ее на один уровень выше. Это закроет доступ к находящимся в ней файлам пользователей вашего сайта, но никак не повлияет на работоспособность системы.

Также, у данного метода есть еще один серьезный недостаток: автор статьи складывает весь кэш в одну папку, что, при достаточном количестве страниц на вашем сайте, вызовет проблему, например, в системах Unix наблюдается достаточное замедление работоспособности при наличие в папке более чем 1000 файлов. В связи с чем, в алгоритм необходимо внести ряд изменений и раскладывать файлы по отдельным подпапкам внутри папки cache_files . Например, используя для этого первые 3-4 символа md5 КЭШа.

Для динамических ресурсов вполне возможно выбрать время кэширования в несколько (5-10) секунд или 1-2 минуты, что уже значительно снизит нагрузку на сервер, но не нанесет вреда интерактивности сайта.

Для страниц, для которых особо важна интерактивность, можно ввести исключения в .htaccess , что позволит именно им постоянно изменяться, а для остальных страниц можно применять кэширование.

Регенерация содержания на лету

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

Это делается следующим набором директив:

RewriteCond % !-s RewriteRule ^page\.html$ page.php

Здесь, запрос к page.html приводит к внутреннему запуску соответствующего page.php, если page.html все-ещё отсутствует или имеет нулевой размер. Фокус здесь в том что page.php это обычный PHP скрипт который в дополнение к собственному выводу, записывает свой вывод в файл page.html. Запустив это один раз, сервер передает данные page.html. Когда вебмастер хочет обновить содержание, он просто удаляет page.html (обычно с помощью cronjob).

Проблема с кэшированием страниц у Internet Explorer.

У IE при работе с заголовком «Vary» встречается одна неприятная ошибочка, связанная с кэшированием страниц. Проблема решается добавлением в.htaccess следующих строк:

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

Сами кэши бываю двух видов — локальные и общие. Локальный это кеш, хранимый непосредственно на диске у клиента, создаваемый и управляемый его браузером. Общий — кэш прокси-сервера организации или провайдера и может состоять из одного или нескольких прокси-серверов. Локальный кеш присутствует, наверное в каждом браузере, общими пользуется значительная часть людей использующих Internet. И если малую часть сайтов сейчас оценивают по расходу трафика, то скорость загрузки — важный критерий, который должен учитываться при разработке Вашего web-проекта.
Для динамических страниц, создаваемых в результате работы PHP-программы, казалось бы, кэширование вредно. Содержание страницы формируются по запросу пользователя на основе какого-либо источника данных. Однако, кэширование может быть полезным. Управляя им Вы можете сделать работу с Вашим сервером комфортнее для пользователя, разрешая загрузку из кэш определенных страниц, предотвращая тем самым их повторную выгрузку с Вашего сервера и экономя пользователю время и трафик.

Кэшировать или нет?

Возможность сохранения в кэш страницы определяется динамичностью информации в источнике данных. Таким образом необходимость использования кэша определяется Вами, исходя из планируемого времени жизни страницы.

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

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

Общие принципы сохранения страниц в кэш

PHP-программа может управлять кэшированием результатов ее работы формируя дополнительные поля в заголовке HTTP ответа вызовом функции Header().
Несколько общих утверждений характерных не только для PHP-программ:

  • Страницы передаваемые по POST никогда не сохраняются в кэш.
  • Страницы запрашиваемые по GET и содержащие параметры (в URL присутствует ‘?’) не сохраняются в кэш, если не указано обратное.
Илон Маск рекомендует:  Псевдокласс placeholder-shown в CSS

Таким образом в большинстве ситуаций дополнительных инструкций в программу добавлять не надо. Основные моменты на которые следует обратить внимание можно свести к двум:

  • запрет кэширования документов, кэшируемых по умолчанию
  • кэширование документов, не подлежащих кэшированию по умолчанию.

Запрет кэширования документов, кэшируемых по умолчанию

Эта задача возникает для PHP-скриптов вызываемых без параметров или являющимися индексами директорий, однако формирующих данные персонально под пользователя (например на основе cookies или user agent) или работающих на основе быстро изменяющихся данных. По спецификации HTTP/1.1 мы можем управлять следующими полями:

Expires
Задает дату истечения срока годности документа. Задание ее в прошлом определяет запрет кэш для данной страницы.

Cache-control: no-cache
Управление кэш. Значение no-cache определяет запрет кэш данной страницы. Для версии протокола HTTP/1.0 действует «Pragma: no-cache».

Last-Modified
Дата послднего изменения содержимого. Поле актуально только для статических страниц. Apache заменяет это поле значением поля Date для динамически генерируемых страниц, в том числе для страниц содержащих SSI.

На сайте www.php.net дается следующий код для запрета кеширования.

header(«Expires: Mon, 26 Jul 1997 05:00:00 GMT»); // Date in the past
header(«Last-Modified: » . gmdate(«D, d M Y H:i:s») . » GMT»); // always modified
header(«Cache-Control: no-cache, must-revalidate»); // HTTP/1.1
header(«Pragma: no-cache»); // HTTP/1.0

Однако, данный заголовок избыточен. В большинстве случаев достаточно:

Чтобы пометить документ как «уже устаревший» следует установить Expires равным полю Date .
header(«Expires: » . gmdate(«D, d M Y H:i:s») . » GMT»);

Ну и не следует забывать, что формы, запрошенные по POST также не подлежат кэшированию.

Кэширование документов, не подлежащих кэшированию по умолчанию

Обратная задача, может показаться на первый взгляд абсурдной. Однако и в этом существует потребность. Кроме простой минимизации трафика при разработке web-программы следует учитывать комфортность работы с ней пользователя. Например, некоторые страницы Вашего сервера формируются на основе статических данных большого объема. Возможность включения их в кэш существенно улучшит скорость работы сервера для пользователя и частично освободит Ваш от многочисленных повторных генераций такой страницы. Заголовок разрешающий сохранение на прокси-серверах:

Статья по теме: Поисковое продвижение интернет-магазина в Яндексе и Google: чек-лист аудита факторов ранжирования

Если страница учитывает информацию сохраненную в браузере пользователя (тип и версию браузера, ключи, авторизацию и т. д.) такую страницу нельзя сохранить на прокси, однако возможно ее сохранение в локальном кэш браузера:

Кэширование до истечения корректности

Описанные выше решения довольно прямолинейны, хотя и подходят для большинства задач. Но протокол HTTP/1.1 имеет средства для более тонкого управления кэш страниц, и существуют задачи требующие применения этих механизмов. Как пример — веб-приложения, работающие с данными большого объема и прогнозируемой динамичностью. Корректность данных может устанавливаться как по дате прогнозируемого обновления, так и по изменению содержания. Для этих случаев используются разные заголовки управления кэшем.

Кэширование с прогнозируемым обновлением

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

$dt_tmp=getdate(date(«U»));
header(«Expires: » . gmdate(«D, d M Y H:i:s», date(«U»)-(86400*($dt_tmp[«wday»]-8))) . » GMT»);
header(«Cache-control: public»);

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

Другой подход, применяемый при более оперативном обновлении информации и одновременной высокой посещаемости сервера (иначе кэширование не будет эффективным) состоит в использовании заголовка Cache-control: max-age=секунды, определяющий время по истечении которого документ считается устаревшим и имеющий больший приоритет при вычислении «свежести» документа.

Простая и эффективная система кеширования PHP. Реализация кэширования на PHP

Основной проблематикой кэширования является быстрота реакции на запросы к основным системам хранения и обработки входящей и исходящей структурированной информации.

Представьте, что необходимо осуществить быструю передачу информации, однако скорость доступа к данным крайне низкая. Или другая ситуация: скорость хорошая, но мало доступной памяти или ширина канала недостаточная, или процессорные и дисковые факторы мешают осуществить задачу. В этом случае кэширование – это единственный выход из ситуации.

Виды кэширования

Необходимо понимать, что работу с данными можно производить как на стороне клиента, так и на сервере. Притом, серверная обработка данных централизована и имеет ряд несомненных преимуществ (особенно для службы поддержки).

Существует несколько видов кэширования, предлагаем рассмотреть каждый вид, его особенности и рекомендации по применению:

1. Браузерное кэширование или клиентское кэширование
1.1 Кэширование файлов и картинок

Это первый уровень кэширования, который состоит в отдаче заголовка «expired» и заголовка «304 Not Modified» . Наиболее эффективным считается кэширование на 2 недели.

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

1.2 Кэширование https
1.3 Кэширование центра сертификации

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

1.4 Кэширование страниц

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

Как правило, кэш подразделяется по типу пользователей:

Данное разделение обусловлено уникальностью контента, для каждого авторизованного пользователя и общностью контента для гостевых пользователей. В большинстве сайтов не авторизованный пользователь не может изменять содержимое сайта, а значит и влиять на его содержимое.

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

2. Серверное кэширование
2.1 Кэширование страницы целиком

Пожалуй, любой когда-либо мечтал о сайте, работающем со скоростью «ping» или быстрее.
Но и у этого типа кэша есть свои минусы: например, невозможность кэшировать страницы для авторизованного пользователя, либо пользователя, содержимое страницы которого зависит от текущих переменных пользователя.

Используйте этот кэш, если серверу известны все статичные состояния внешних данных, такие как: uri, get (без дополнительных параметров), пользователь не авторизован — то есть, фактически, это идеальное состояние страницы для гостевых пользователей. Учитывайте тот факт, что при таком кэшировании архитектура сайта или приложения всегда должна однотипно обрабатывать входящие запросы и отдавать однотипные ответы. Такое состояние есть в любом приложении или сайте, его нужно лишь отследить и применить к нему кэш.

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

2.2 Кэширование результатов компиляции php-файлов

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

2.3 Кэширование отдельных блоков страницы

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

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

2.4 Кэширование php на основе неразделяемых ресурсов
2.5 Кэширование php на основе общих ресурсов
2.6 Кэширование mysql на основе query cache

Наверняка, вы регулярно сталкивались с ситуацией, когда необходимо отдать новые материалы, дата публикации которых уже разрешена текущим timestamp? Проще говоря,

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

Mysql_connect(«host», «user», «passwd»); $image=mysql(«db», «select pics,type from pictures where ); mysql_freeResult($image); mysql_close();

Для управления используется MD5 сумма содержимого изображения. Пока содержание не изменилось, сумма будет постояной. В случае изменения содержания в базе на сервере клиент выполнит запрос для повторного формирования содержания. Пока изображение постоянно содержимое будет отображаться из кэш.

Примечания для Russian Apache

И приятное (или неприятное) сообщение для пользователей Russian Apache. Так как сервер выдает старину по пользовательской кодировке он автоматически снабжает ВСЕ страницы (не только динамические) заголовками запрета кэширования.

Кеширование промежуточного кода (Opcode Caching)
Кэширование кода это один из самых легких и эффективных путей увеличения производительности в PHP. Использовании данного вида кэширования позволит избавиться от большого количества неэффективностей, возникающих при процессе запуска выполнения кода. Кэширование кода сохраняет промежуточный код в памяти для того чтобы не компилировать PHP-код каждый раз при запуске файла.

Существует множество библиотек для такого кэширования, например, APC , XCache , eAccelerator и Zend Platform .

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

/**
* Compile Files for APC
* The function runs through each directory and
* compiles each *.php file through apc_compile_file
* string $dir start directory
* void
*/
function compile_files($dir)
<
$dirs = glob($dir. DIRECTORY_SEPARATOR. «*», GLOB_ONLYDIR);
if (is_array($dirs) && count($dirs) > 0)
<
while(list(,$v) = each($dirs))
<
compile_files($v);
>
>
$files = glob($dir. DIRECTORY_SEPARATOR. «*.php»);
if (is_array($files) && count($files) > 0)
<
while(list(,$v) = each($files))
<
apc_compile_file($v);
>
>
>
compile_files(«/path/to/dir»);

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

if (!$config = apc_fetch(«config»))
<
require(«/path/to/includes/config.php»);
apc_store(«config», $config);
>

if (!$conf = apc_fetch(«pbs_config»))
<
$conf = new Zend_Config_Xml(PB_PATH_CONF. «/base.xml», «production»);
apc_store(«pbs_config», $conf);
>

Concurrency Level: 5
Time taken for tests: 30.33144 seconds
Complete requests: 684
Failed requests: 0
Write errors: 0

Concurrency Level: 5
Time taken for tests: 30.12173 seconds
Complete requests: 709
Failed requests: 0
Write errors: 0

Файловое кэширование результатов
В некоторых случаях сервер обрабатывает запросы, результатом которых является одинаковый контент. Есть возможность закэшировать подобные вид контента (полностью или его часть)
В данном тексте иллюстрируется пример на основе пакета Pear::Cache_Lite .

Полное кэширование вывода
Полное кэширование довольно тяжело выполнить на большинстве сайтов с постоянно обновляющимися данными из большого количества источников. Все это правда, однако, нет необходимости обновлять данные каждую секунду. Даже 5-10 минутная задержка при экстремально высокой загрузке сайта позволит вам увеличить производительность.
Пример ниже, сохраняет слепок страницы для будущего использования. Такой подход может помочь большому количеству пользователей.
Я не рекомендую использовать данное решение, но если вам нужно что-то быстрое, вы можете его использовать, рано или поздно вы увидите недостатки этого метода.
The Bootstrap Cache Example:

require(«/path/to/pear/Cache/Lite/Output.php»);
$options = array(
«cacheDir» => «/tmp/»,
«lifeTime» => 10
);

.htaccess
php_value auto_prepend_file /path/to/cache_start.php
php_value auto_append_file /path/to/cache_end.php
cache_start.php
require(«Cache/Lite/Output.php»);

$options = array(
«cacheDir» => «/tmp/»,
«lifeTime» => 10
);
$cache = new Cache_Lite_Output($options);
if (($cache->start($_SERVER[«REQUEST_URI»])))
exit;

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

require(«Cache/Lite.php»);
$options = array(
«cacheDir» => «/tmp/»,
«lifeTime» => 3600 //1 hour
);

if (!($categories = $cache->get(«categories»)))
<

$categories = «»;
$cache->save($categories, «categories»);
>
echo $categories;

require(«Cache/Lite.php»);
$options = array(
«cacheDir» => «/tmp/»,
«lifeTime» => 3600, //1 hour
«automaticSerialization» => true
);
$cache = new Cache_Lite($options);
if (!($categories = $cache->get(«categories»)))
<
$rs = mysql_query(«SELECT category_id, category_name FROM category»);
$categories = array();
while($row = mysql_fetch_assoc($rs))
<
$categories = $row;
>
$cache->store($categories, «categories»);
>
var_dump($categories);

Кэширование в оперативной памяти
Существует множетсво путей для того чтобы произвести кэширование в памяти: memcached, memory tables в базах данных, RAM disk и другие.
Memcached
С сайта memcache memcached это высокопроизводительная и распределенная кэширующая система, которая увеличивает скорость динамических веб-приложений путём снижения загрузки с базы данных.
О чем это говорит, о том, что можно сохранить данные на одном сервере, к которому будут обращаться другие сервера, это не зависит от вашего веб-сервера (как в случае кеширования промежуточного кода), так как memcached – это демон, который в большинстве случаев используется для кэширования результатов запросов к базам данных.
Пример работы с Memcache:

$post_ ];
$memcached = new Memcache;
$memcached->connect(«hostname», 11211);
if (!$row = $memcached->get(«post_id_». $post_id))
<
//yes this is safe, we type casted it already ;)
$rs = mysql_query(«SELECT * FROM post WHERE post_ > if ($rs && mysql_num_rows($rs) > 0)
<
$row = mysql_fetch_assoc($rs);
// cache compressed for 1 hour
$memcached->set(«post_id_». $post_id, $row, MEMCACHE_COMPRESSED, time() + 3600);
>
>
var_dump($row);

session.save_handler = memcache
session.save_path = «tcp://hostname:11211»

mount —bind -ttmpfs /path/to/site/tmp /path/to/site/tmp

Надеюсь, что описанное выше было достаточно информативно. Здесь не описан весь потенциал кэширования, например использование кэширования в распределенных базах данных или использование Squid. В будущих статьях я опишу и это…

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

Шаг первый. Создаем файл top-cache.php

Нам нужно создать два файла. Первый: создаем файл с именем top-cache.php и копируем в него следующий код:

Что происходит в данном коде? Первые 5 строк создают имя файла кеша в соответствии с текущем PHP файлом. Например, если мы используем файл с именем list.php , файл кеша будет иметь вид cached-list.html .

Строка 6 создает переменную $cachetime , которая определяет время жизни кеша.

Строки с 9 по 13 определяют условное выражение, которое служит для проверки наличия файла с именем, определенным в переменной $cachefile . Если файл существует, вставляется комментарий и файл, определенный в переменной $cachefile . Затем выражение exit прерывает выполнение скрипта и файл отправляется браузеру клиента. То есть, если найден статичный файл, то PHP код не будет выполняться сервером.

Строка 14 создает буфер, если файл, определенный переменной $cachefile не найден.

Шаг второй. Создаем файл bottom-cache.php

Теперь создаем второй файл PHP с именем bottom-cache.php и копируем в него следующий код:

Если файл с именем, определенным в переменной $cachefile отсутствует на сервере, выполняется данный код и создается файл. При следующем обращении к странице статичный $cachefile будет обслуживать браузер клиента вместо выполнения всего кода скрипта PHP.

Шаг три. Включаем файлы кеширования в код страницы

Теперь у нас есть два необходимых файла. Просто включаем их в страницу PHP, которую нужно кешировать. Файл top-cache.php нужно включить в начало страницы, а файл bottom-cache.php — в конце:

Теперь, если проверить кеширование на медленных страницах, то можно убедиться, насколько они стали быстрее загружаться.

В старые добрые времена, когда создание web-сайтов представляло из себя такое простое занятие, как набор нескольких HTML -страниц, отправка web-страниц в браузер была простой отправкой файла web-сервером. Посетители сайта могли видеть эти небольшие, исключительно текстовые странички, почти мгновенно (если не считать пользователей медленных модемов). Как только страница была загружена, браузер кэширует её где-нибудь на локальном компьютере, чтобы в случае повторного запроса страницы, можно было взять его локальную версию из кэша, послав лишь короткий запрос, чтобы убедиться, что страница на сервере не была изменена. Запросы обрабатывались быстро и как можно эффективней, и все были счастливы (кроме использующих модемы 9600 бод).

Появление динамических web-страниц изменило положение вещей в худшую сторону, эффективно «сломав» эту модель обслуживания web-страниц благодаря наличию двух проблем:

  1. Когда сервером получен запрос динамической web-странички, производится некоторая промежуточная обработка, например синтаксический анализ (парсинг) скрипта движком PHP , которая должна быть завершена. Благодаря этому получаем задержку перед тем, как web-сервер начнёт отправку вывода в браузер. Для простого PHP -скрипта это не существенно, но для более сложного приложения движок PHP может выполнить много действий прежде чем страница будет готова для отправки. Эти дополнительные действия приводят к заметной задержке между запросами пользователей и реальным отображением страниц в их браузерах.
  2. Типичный web-сервер, например Apache, использует время модификации файла чтобы правильно сообщить web-браузеру состояние кэша запрашиваемой странички. Для динамических web-страниц, фактически PHP -скрипт может изменяться только изредка, в то время как отображаемый им контент, возможно располагающийся в базе данных, изменяется часто. Web-сервер не имеет возможности знать о наличии изменений в базе данных, тем не менее он не отправляет дату последней модификации. Если клиент (браузер) не получает никакого признака того, как долго данные являются корректными, он предполагает, что в следующий раз необходимо запросить страничку по новой. Web-сервер всегда будет отвечать обновлённой версией странички, независимо от того, изменились ли данные. Чтобы избежать этого недостатка большинство web-разработчиков используют мета-тэги или HTTP -заголовки, чтобы сообщить браузеру никогда не использовать кэшированную версию странички. Однако это отрицает естественную способность web-браузера кэшировать web-страницы и обладает некоторыми существенными недостатками. Например, содержание динамической странички может изменяться раз в сутки, поэтому выгода, получаемая от наличия даже 24-часового кэширования странички браузером, очевидна.

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

Как я предотвращаю кэширование страницы браузерами?

Прежде чем мы рассмотрим методы клиентского и серверного кэширования, в первую очередь мы должны понять, как вообще предотвратить кэширование страниц web-браузером (и прокси-серверами). Основной способ достижения этого использует мета-тэги HTML:

Вставив прошедшую дату в мета-тэг Expires, вы сообщаете браузеру, что кэшированная копия странички всегда является устаревшей. Это значит, что браузер никогда не должен кэшировать страницу. Мета-тэг Pragma: no-cache довольно хорошо поддерживаемое соглашение, которому следует большинство web-браузеров. Обнаружив этот тэг, они обычно не кэшируют страницу (хотя никаких гарантий нет, это всего лишь соглашение).

Это хорошо звучит, но есть две проблемы, связанные с использованием мета-тэгов:

  1. Если тэг не существовал когда страница была запрошена браузером впервые, но появляется позже (например, вы модифицировали включаемый файл pageheader.php который является шапкой каждой web-страницы), браузер останется в блаженном неведении и воспользуется свей кэшированной копей оригинала.
  2. Прокси-серверы, кэширующие web-страницы, как например общий ISP , вообще не будет исследовать непосредственно содержимое HTML -документа. Вместо этого они полагаются только на web-сервер, с которого пришли документы, и протокол HTTP . Иными словами, web-браузер может считать, что не должен кэшировать страницу, но прокси-сервер между браузером и вашим web-сервером вероятно не знает этого – и продолжит отправлять клиенту ту же самую, уже устаревшую, страницу.

Лучший подход состоит в том, чтобы использовать непосредственно протокол HTTP с помощью функции PHP header() , эквивалентно приведённым выше двум мета-тэгам:

Мы можем пойти на один шаг вперёд, воспользовавшись заголовком Cache-Control совместимым с браузерами, поддерживающими HTTP 1.1:

Header(«Expires: Mon, 26 Jul 1997 05:00:00 GMT»); header(«Cache-Control: no-store, no-cache, must-reval );

Это гарантирует, что никакой web-браузер или промежуточный прокси-сервер не будет кэшировать страницу, таким образом посетители всегда получат самую последнюю версию контента. Фактически, первый заголовок должен быть самодостаточным, это лучший способ гарантировать, что страница не кэшируется. Заголовки Cache-Control и Pragma добавлены с целью «подстраховаться». Хотя они не работают во всех браузерах или прокси, они отловят некоторые случаи, в которых Expires не работает должным образом (например, если дата на компьютере клиента установлена неправильно).

Конечно, полный отказ от кэширования обеспечивает нас проблемами, которые мы обсуждали в начале этой главы. Сейчас мы рассмотрим решение этих проблем.

Internet Explorer и кэширование загрузки файлов

Если при обслуживании загрузки файла PHP -скриптом используются такие заголовки, как например Content-Disposition: attachment, filename=myFile.pdf или Content-Disposition: inline, filename=myFile.pdf у вас будут проблемы с Internet Explorer ’ом, если вы сообщите браузеру не кэшировать страницу.

Internet Explorer оперирует загрузкой довольно необычным образом, выполняя два запроса к web-сайту. Первый запрос загружает файл и сохраняет его в кэше, пока не будет создан второй запрос (без сохранения отклика). Этот запрос вызывает процесс передачи файла конечному пользователю в соответствии с типом файла (например, запускает Acrobat Reader , если файл является PDF -документом). Это значит, что если вы отправили заголовки, запрещающие браузеру кэшировать страницу, Internet Explorer удалит файл между первым и вторым запросом, в результате чего конечный пользователь ничего не получит. Если файл, который вы отдаёте PHP -скриптом, не изменяется, одним из простейших решений будет убрать «запрещающие кэширование» заголовки из скрипта.

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

Как я могу захватить данные на стороне сервера для кэширования?

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

Несколько слов о кэшировании при помощи шаблонов

Как мне управлять кэшированием на стороне клиента средствами PHP?

Пришло время посмотреть на механизм, который позволит нам контролировать кеш на стороне клиента средствами PHP . Этот подход будет работать только если вы используете PHP в связке с сервером Apache , поскольку мы будем использовать функцию getallheaders() , чтобы получить заголовки, передаваемые браузером. Эта функция работает только в Apache .

Новые имена функций

Если вы используете PHP 4.3.0 с Apache , HTTP-заголовки доступны функцией apache_request_headers() и apache_response_headers() . Функция getallheaders() стала псевдонимом для новой функции apache_request_headers() .

Механизмом для работы с кэшем web-браузера вновь является HTTP . Множество заголовков вовлечёны в инструктирование web-браузеров и прокси-серверов независимо кэшировать страницу, ситуация осложняется тем фактом, что некоторые из них доступны только с HTTP 1.1.

Проверка HTTP-заголовков в вашем браузере

Простым но очень удобным инструментом для проверки заголовков запросов и откликов является LiveHttpHeaders – аддон к браузеру Mozilla . Необходимо точно знать, какие заголовки посылает ваш скрипт, особенно когда вы имеете дело с заголовками кэширования HTTP .

Для простоты мы рассмотрим только заголовки кэширования HTTP 1.0, а именно Expires , Last-Modified и If-Modified-Since , а также статус-код HTTP 304 (Not Modified) .

Другие заголовки, доступные с HTTP 1.1, например Cache-Control и ETag , предназначены для обеспечения расширенного механизма, который может использоваться совместно с состоянием web-сессии, иными словами, версия данной страницы, отображаемой неавторизованному посетителю, может значительно отличаться от отображаемой авторизованному пользователю. Заголовки HTTP 1.1 изначально добавлялись для того, чтобы позволить кэшировать такие страницы.

Истечение срока жизни страницы

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

Пример 7. 6.php

Функция setExpires отправляет заголовок HTTP Expires с будущим временем, заданном в секундах. Вышеприведённый пример показывает текущее время по Гринвичу и выводит ссылку, которая вам позволяет перейти на страницу вновь. Используя кнопку Refresh вашего браузера, вы можете сообщить браузеру о желании обновить кэш. Используя ссылку, вы увидите, что время изменяется только раз в 10 секунд.

Даты и время в HTTP

Даты в HTTP всегда вычисляются относительного меридиана времени Гринвича (GMT). Функция PHP gmdate() точно такая же функция, как date() , за исключением того, что она автоматически компенсирует время по Гринвичу, основанное на системных часах и настройках региона вашего сервера.

Когда браузер сталкивается с заголовком Expires , он кэширует страницу. Все последующие запросы страницы, сделанные до указанного времени истечения срока жизни, используют версию страницы из кэша, никаких запросов к web-серверу при этом не происходит.

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

Время изменения страницы

Более практично использовать заголовки Last-Modified и If-Modified-Since , доступные в HTTP 1.0. Технически он известно как выполнение условного GET-запроса, вы возвращаете любой контент, основываясь на условии пришедшего заголовка запроса If-Modified-Since .

При использовании этого метода вы должны отправлять заголовок Last-Modified каждый раз, когда обращаются к вашему PHP-скрипту. При следующем запросе страницы браузером, он отправит заголовок If-Modified-Since , содержащий время, по которому ваш скрипт может определить, обновлялась ли страница со времени последнего запроса. Если это не так, ваш скрипт посылает код статуса HTTP 304 , чтобы указать, что страница не изменялась, не выводя при этом содержимого страницы.

Устанавливаем время модификации кэш-файла этой строкой: $lastModified = filemtime($cache_file);

Затем, используя время модификации кэш-файла, мы посылаем заголовок Last-Modified . Нам нужно посылать её для каждой предоставляемой страницы, чтобы вынудить браузер посылать нам заголовок If-Modified-Since с каждым запросом.

// Выдаём заголовок HTTP Last-Modified header(«Last-Modified: » . gmdate(«D, d M Y H:i:s», $lastModified) . » GMT»); \n \n»; echo » \n»; echo » \n»; echo » \n»; ?>

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

Будьте осторожны при тестировании любого кэширования, выполненного в таком стиле, если вы сделаете это неправильно, вы можете заставить ваших посетителей всегда иметь устаревшие копии вашего сайта.

Кэширование ваших страниц в 5 шагов

Кэширование ваших страниц может оказаться красивым и полезным механизмом, особенно, если они генерируются средствами PHP и делают множество SQL запросов. Как только вы примените кэширование, ваш сервер тут же снизит нагрузку и перестанет съедать много памяти на генерацию страниц — он просто будет загружать их из КЭШа. Я покажу вам, как PHP может кэшировать страницы и, в дальнейшем, вы сможете тратить на это минут 5.

Расмотрим технологию кэширования пошагам:

  1. В домашней директории создаем файлы .htaccess , start_cache.php , end_cache.php , а также папку с названием cache_files .
  2. Папке cache_files необходимо проставить атрибуты 777 .
  3. Внутри .htaccess файла пропишите следующие строки: php_value auto_prepend_file /home/username/public_html/start_cache.php php_value auto_append_file /home/username/public_html/end_cache.php Строку /home/username/public_html/ необходимо заменить на путь к вашей домашней директории.
  4. В скрипт start_cache.php помещаем следующий код: Не забывайте исправлять путь /home/username/public_html/ на путь к вашей домашней директории.
  5. А следующий код поместите в скрипт end_cache.php :

Все ваши страницы будут кэшироваться на 3600 секунд = 1 час. Этот параметр вы легко можете поменять в скрипте start_cache.php . Кэш страниц будет сохранен в папке cache_files .

Совершенно очевидно, что в данном случае атрибуты 777 являются определенным нарушением безопасности. В связи с чем, рекомендую вынести папку cahce_files за пределы public_html , например, поместить ее на один уровень выше. Это закроет доступ к находящимся в ней файлам пользователей вашего сайта, но никак не повлияет на работоспособность системы.

Также, у данного метода есть еще один серьезный недостаток: автор статьи складывает весь кэш в одну папку, что, при достаточном количестве страниц на вашем сайте, вызовет проблему, например, в системах Unix наблюдается достаточное замедление работоспособности при наличие в папке более чем 1000 файлов. В связи с чем, в алгоритм необходимо внести ряд изменений и раскладывать файлы по отдельным подпапкам внутри папки cache_files . Например, используя для этого первые 3-4 символа md5 КЭШа.

Для динамических ресурсов вполне возможно выбрать время кэширования в несколько (5-10) секунд или 1-2 минуты, что уже значительно снизит нагрузку на сервер, но не нанесет вреда интерактивности сайта.

Для страниц, для которых особо важна интерактивность, можно ввести исключения в .htaccess , что позволит именно им постоянно изменяться, а для остальных страниц можно применять кэширование.

Регенерация содержания на лету

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

Это делается следующим набором директив:

RewriteCond % !-s RewriteRule ^page\.html$ page.php

Здесь, запрос к page.html приводит к внутреннему запуску соответствующего page.php, если page.html все-ещё отсутствует или имеет нулевой размер. Фокус здесь в том что page.php это обычный PHP скрипт который в дополнение к собственному выводу, записывает свой вывод в файл page.html. Запустив это один раз, сервер передает данные page.html. Когда вебмастер хочет обновить содержание, он просто удаляет page.html (обычно с помощью cronjob).

Проблема с кэшированием страниц у Internet Explorer.

У IE при работе с заголовком «Vary» встречается одна неприятная ошибочка, связанная с кэшированием страниц. Проблема решается добавлением в.htaccess следующих строк:

Кэширование данных страниц в php

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

Что такое кэширование?

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

На сегодняшний день кэши бываю двух типов — локальные и общие.

  • Локальный — кеш, хранимый на диске у клиента, создаваемый, а также управляемый его интернет-браузером.
  • Общий — это кэш прокси-сервера провайдера, он может состоять из одного или ряда прокси-серверов.

Локальный кеш есть, наверное в каждом интернет-браузере, общими пользуется большая часть людей, которые используют интернет. И если малую часть веб-сайтов сегодня оценивают по расходу трафика, то скорость загрузки — очень важный критерий, который должен обязательно учитываться при разработке вашего веб-проекта.

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

Ключевые принципы сохранения страниц в кэш

PHP-приложение может управлять кэшированием результатов его работы формируя дополнительные поля в заголовке HTTP ответа вызовом специальной функции Header().

Несколько общих утверждений, которые характерны не только для PHP-приложений:

  • Странички, которые передаются по POST не сохраняются в кэш никогда.
  • Странички, которые запрашиваются по GET и содержат параметры (в URL есть ‘?’) не сохраняются в кэш, в случае если не указано обратное.

В большинстве ситуаций каких-то дополнительных инструкций в приложение добавлять не нужно. Основные нюансы, на которые надо обратить внимание:

  • запрет кэширования документов, которые кэшируются по умолчанию;
  • кэширование документов, которые не подлежат кэшированию по умолчанию.

Запрет на кэширования документов (которые кэшируются по умолчанию)

Эта задача возникает для PHP-скриптов, которые вызываются без параметров или являются индексами директорий, но формируют информацию персонально под пользователя (к примеру на основе user agent или же cookies) или работают на основе быстро изменяющихся сведений. Мы по спецификации HTTP/1.1 можем управлять такими полями:

  1. Expires — задает дату истечения срока годности определенного документа. Задание ее в прошлом определяет запрет кэш для этой странички.
  2. Cache-control: no-cache — управление кэшем. Значение no-cache определяет запрет кэш этой странички. Для версии протокола HTTP/1.0 здесь действует «Pragma: no-cache».
  3. Last-Modified — это дата последнего изменения определенного содержимого. Поле применяется исключительно для статических страничек. Apache заменяет данное поле значением поля Date для динамически генерируемых страничек, в частности для страниц, которые содержат SSI.

Чтобы запретить кэширование, достаточно прописать:

Для того, чтобы документ пометить как «устаревший», необходимо установить Expires равным полю Date.

Не следует также забывать о том, что формы, которые запрошены по POST кэшированию не подлежат.

Кэширование с прогнозируемым обновлением

Рассмотрим пример — прайс лист обновляемый именно по понедельникам. Вы знаете заранее о том, что содержание странички можно хранить в кэш до наступления следующей недели, что и нужно указать в заголовке ответа, обеспечивая необходимое поведение странички в кэш.

Главной задачей здесь является получить дату следующего понедельника в виде RFC-1123.

Данным способом можно очень эффективно управлять поведением странички в кэш. Можно выделить особые временные интервалы в течении которых содержание определенной странички остается постоянным.

Другой подход, который применяется при более оперативном обновлении данных и одновременной большой посещаемости сервера (в другом случае кэширование эффективным не будет) состоит в использовании специального заголовка Cache-control: max-age=секунды, который определяет время, по истечении которого документ уже считается устаревшим и имеющий гораздо больший приоритет при вычислении свежести конкретного документа.

Если Вы публикуете новости с интервалом в 60 минут:

Реализация кэширования на PHP

Вам необходимо создать два специальных файла. Первый будет делать вывод кэша, а второй — создавать кэш.

Начнем с первого файла, который назовем read_cache.php.

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

Теперь создадим специальный файл write_cache.php. Он будет записывать в файл то, что накопилось в буфере.

Теперь в любом php-файле, который отвечает за вывод, можно включить кэширование:

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

Реализация кэширования на PHP. Кэширование в браузере (PHP, Javascript)

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

Сами кэши бываю двух видов — локальные и общие. Локальный это кеш, хранимый непосредственно на диске у клиента, создаваемый и управляемый его браузером. Общий — кэш прокси-сервера организации или провайдера и может состоять из одного или нескольких прокси-серверов. Локальный кеш присутствует, наверное в каждом браузере, общими пользуется значительная часть людей использующих Internet. И если малую часть сайтов сейчас оценивают по расходу трафика, то скорость загрузки — важный критерий, который должен учитываться при разработке Вашего web-проекта.

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

Илон Маск рекомендует:  Руководство по флексбоксам


Кэшировать или нет?

Возможность сохранения в кэш страницы определяется динамичностью информации в источнике данных. Таким образом необходимость использования кэша определяется Вами, исходя из планируемого времени жизни страницы.

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

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

Общие принципы сохранения страниц в кэш

PHP-программа может управлять кэшированием результатов ее работы формируя дополнительные поля в заголовке HTTP ответа вызовом функции Header().

Несколько общих утверждений характерных не только для PHP-программ:

  • Страницы передаваемые по POST никогда не сохраняются в кэш.
  • Страницы запрашиваемые по GET и содержащие параметры (в URL присутствует «?») не сохраняются в кэш, если не указано обратное.

Таким образом в большинстве ситуаций дополнительных инструкций в программу добавлять не надо. Основные моменты на которые следует обратить внимание можно свести к двум:

  • запрет кэширования документов, кэшируемых по умолчанию
  • кэширование документов, не подлежащих кэшированию по умолчанию.

Запрет кэширования документов, кэшируемых по умолчанию

Эта задача возникает для PHP-скриптов вызываемых без параметров или являющимися индексами директорий, однако формирующих данные персонально под пользователя (например на основе cookies или user agent) или работающих на основе быстро изменяющихся данных. По спецификации HTTP/1.1 мы можем управлять следующими полями:

  • Expires — Задает дату истечения срока годности документа. Задание ее в прошлом определяет запрет кэш для данной страницы.
  • Cache-control: no-cache — Управление кэш. Значение no-cache определяет запрет кэш данной страницы. Для версии протокола HTTP/1.0 действует «Pragma: no-cache».
  • Last-Modified — Дата послднего изменения содержимого. Поле актуально только для статических страниц. Apache заменяет это поле значением поля Date для динамически генерируемых страниц, в том числе для страниц содержащих SSI.

На сайте www.php.net дается следующий код для запрета кеширования.

Header(«Expires: Mon, 26 Jul 1997 05:00:00 GMT»); // Date in the past header(«Last-Modified: » . gmdate(«D, d M Y H:i:s») . » GMT»); // always modified header(«Cache-Control: no-cache, must-revalidate»); // HTTP/1.1 header(«Pragma: no-cache»); // HTTP/1.0

Однако, я считаю, что данный заголовок избыточен. В большинстве случаев достаточно:

Чтобы пометить документ как «уже устаревший» следует установить Expires равным полю Date.

Header(«Expires: » . gmdate(«D, d M Y H:i:s») . » GMT»);

Ну и не следует забывать, что формы, запрошенные по POST также не подлежат кэшированию.

Кэширование документов, не подлежащих кэшированию по умолчанию

Обратная задача, может показаться на первый взгляд абсурдной. Однако и в этом существует потребность. Кроме простой минимизации трафика при разработке web-программы следует учитывать комфортность работы с ней пользователя. Например, некоторые страницы Вашего сервера формируются на основе статических данных большого объема. Возможность включения их в кэш существенно улучшит скорость работы сервера для пользователя и частично освободит Ваш от многочисленных повторных генераций такой страницы. Заголовок разрешающий сохранение на прокси-серверах:

Если страница учитывает информацию сохраненную в браузере пользователя (тип и версию браузера, ключи, авторизацию и т.д.) такую страницу нельзя сохранить на прокси, однако возможно ее сохранение в локальном кэш браузера:

Кэширование до истечения корректности

Описанные выше решения довольно прямолинейны, хотя и подходят для большинства задач. Но протокол HTTP/1.1 имеет средства для более тонкого управления кэш страниц, и существуют задачи требующие применения этих механизмов. Как пример — web-приложения работающие с данными большого объема и прогнозируемой динамичностью. Корректность данных может устанавливаться как по дате прогнозируемого обновления, так и по изменению содержания. Для этих случаев используются разные заголовки управления кэш.

Кэширование с прогнозируемым обновлением

Рассмотрим пример — прайс лист обновляемый по понедельникам. Вы заранее знаете, что содержание страницы можно хранить в кэш до наступления новой недели, что и следует указать в заголовке ответа обеспечивая нужное поведение страницы в кэш.

Основная задача — получить дату следующего понедельника в формате RFC-1123

$dt_tmp=getdate(date(«U»)); header(«Expires: » . gmdate(«D, d M Y H:i:s», date(«U»)-(86400*($dt_tmp[«wday»]-8))) . » GMT»); header(«Cache-control: public»);

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

Другой подход, применяемый при более оперативном обновлении информации и одновременной высокой посещаемости сервера (иначе кэширование не будет эффективным) состоит в использовании заголовка Cache-control: max-age=секунды, определяющий время по истечении которого документ считается устаревшим и имеющий больший приоритет при вычислении «свежести» документа.

Если Вы публикуете новости с интервалом в 30 минут:

Header(«Cache-control: public»);
header(«Cache-control: max-age=1800»);

Кэширование по содержанию

Еще более интеллектуальный вид управления предоставляет HTTP/1.1 на основе содержимого с помощью директив Vary. Я очень рекомендую применять его при формировании изображений или текстов большого объема, которые как показывает практика изменяются крайне редко. При этом у пользователя в случае возврата не будет происходить их повторной выгрузки, если содержание осталось прежним, и страница будет взята с Вашего сервера, если ее содержание изменилось.

Рассмотрим пример выдачи изображения из базы данных индентифицируемых по ID. Вызов страницы выглядит следующим образом:

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

Mysql_connect(«host», «user», «passwd»); $image=mysql(«db», «select pics,type from pictures where ); mysql_freeResult($image); mysql_close();

Для управления используется MD5 сумма содержимого изображения. Пока содержание не изменилось, сумма будет постояной. В случае изменения содержания в базе на сервере клиент выполнит запрос для повторного формирования содержания. Пока изображение постоянно содержимое будет отображаться из кэш.

Примечания для Russian Apache

И приятное (или неприятное) сообщение для пользователей Russian Apache. Так как сервер выдает старину по пользовательской кодировке он автоматически снабжает ВСЕ страницы (не только динамические) заголовками запрета кэширования.

Ни для кого не секрет, что чем быстрее загружается сайт тем удобнее на нем будет пользователям. Если страницы загружаются быстро пользователи не будут уходить из вашего сайта и поисковики будут относиться к сайту лучше. Для многих современных сайтов узким местом становиться движок выполнения динамических скриптов PHP.

Веб-сервер Nginx при правильной настройке может отдавать просто огромное количество страниц мгновенно, чего нельзя сказать про PHP на генерацию страницы может уходить до нескольких секунд. Но PHP тоже можно ускорить с помощью кэширования. В этой статье мы рассмотрим как настраивается кэширование php, каким оно бывает и зачем вообще это нужно. Для примера будем использовать связку php-fpm и Nginx, но информация из статьи подойдет и для других вариантов установки.

Особенность интерпретируемых языков в том, что при каждом запуске скрипта интерпретатор должен скомпилировать программу и проверить ее на ошибки. Но мы можем обойти. Есть два основных вида кэширования:

  • Кэширование готовых страниц — страница генерируется php, а потом пользователю отдается готовая страница без обращения к php. Я расскажу как это сделать через fastcgi, но не рекомендую применять такой метод для wordpress или других движков, их лучше кэшировать с помощью специальных плагинов;
  • Кэширование байт кода и инструкций — а это уже интересно, кэшируется не вся страница, а только некоторые инструкции, и куски байт кода, которые не изменяются при вызовах скрипта. Перед тем как выполнять скрипт, интерпретатор должен преобразовать его в понятный для него формат, при кэшировании такое преобразование выполняется только первый запуск, а дальше берется версия из кэша;
  • Кэширование сессий — по умолчанию php сохраняет сессии пользователей в файлы и мы можем немного ускорить его работу, если будем сохранять сессии в оперативную память.

Кэширования байткода в PHP

Начиная с PHP 5.5 в интерпретатор языка была добавлена поддержка кэширования байткода из ZendFramework. В новых версиях этот кэш позволяет очень сильно увеличить производительность вашего ресурса, например, есть сведения, что на PHP 7 Wordpres и другие движки работают чуть ли не в два раза быстрее. Перед тем как настраивать кєширование opcode php, нужно установить его пакет:

sudo apt install php-opcache

Или для Red Hat дистрибутивов:

sudo yum install php-opcache

Затем, чтобы включить кэширование нужно добавить несколько строк в php.ini, можно также создать отдельный файл в /etc/php/conf.d/

zend_extension=opcache.so;
opcache.error_log=/var/log/php-fpm/opcache-error.log
opcache.enable=1;
opcache.memory_consumption=256;
opcache.interned_strings_buffer=8;
opcache.max_accelerated_files=4000;
opcache.reval > opcache.fast_shutdown=0;
opcache.enable_cli=0;
opcache.reval > opcache.val > opcache.max_file_size=0;
opcache.file_cache= /var/www/сайт/opcache;

Рассмотрим что означают эти строки, чтобы вы знали какие значения установить. Первая строка загружает расширение, здесь ничего менять не нужно.

  • opcache.error_log — указывает файл для записи лога ошибок, будет полезно при отладке;
  • opcache.log_verbosity_level — указывает насколько подробным должен быть лог файл, значение от 1 до 4;
  • opcache.enable — включает кэширование;
  • opcache.enable_cli — включает кэширование страниц php для консольной версии;
  • opcache.memory_consumption — количество оперативной памяти для хранения кэша;
  • opcache.max_accelerated_files — число скриптов/файлов, которые нужно кэшировать;
  • opcache.validate_timestamps — проверять время изменения данных в файле скрипта;
  • opcache.revalidate_freq — частота проверки для предыдущего параметра;
  • opcache.revalidate_path — установите в 0 чтобы выполнять проверку при include только первый раз;
  • opcache.enable_file_override — кэширует запросы к атрибутам файлов, например, существование и т д;
  • opcache.blacklist_filename — список файлов, которые не нужно кэшировать;
  • opcache.max_file_size — максимальный размер файла скрипта для кэширования, 0 — не ограниченно;
  • opcache.interned_strings_buffer — допустимое количество строк в буфере;
  • opcache.fast_shutdown — использовать быстрый способ освобождения памяти.

После сохранения всех настроек вам останется только перезапустить php или ваш веб-сервер:

systemctl restart php-fpm

Для того чтобы убедиться, что все работает вы можете захотеть посмотреть какие скрипты уже закэшированы. Для этого можно использовать скрипт opcache-status . Просто сохраните скрипт в директорию веб-сервера, а затем дайте ему права:

chmod 777 /var/www/сайт/opcode.php

Здесь можно видеть подробную статистику по кєширвоанию, настройки и количество занятой памяти.

Хранение сессий в memcached

По умолчанию php хранит сессии в файловой системе, в некоторых случаях, вы можете достаточно сильно ускорить работу php, если перенесете хранение сессий из файлов в оперативную память, например, memcached. Сначала нужно установить memcached и php библиотеку для работы с ней:

sudo apt install memcached php-memcached

Или для систем на базе Red Hat:

sudo yum install memcached php-memcached

Сначала нам нужно настроить memcached, откройте файл /etc/sysconfig/memcached и найдите строку CACHESIZE, здесь нужно указать объем оперативной памяти, которая выделяется под кэш:

session.save_handler = memcache
session.save_path = «tcp://localhost:11211»

Осталось перезапустить ваш php интерпретатор:

systemctl restart php-fpm

Если вы хотите проверить все ли правильно кэшируется и есть ли вообще что-либо в кэше, можно использовать phpmemcacheadmin .

Кэширование страниц fastcgi

Я не советую использовать кэширование fastgci для сайтов WordPress, потому что там есть специальные плагины, которые могут точно контролировать кэш, очищать его когда нужно и вовремя обновлять. Но во всех остальных случаях кэш fastcgi может очень сильно ускорить работу сайта. Настраивается он в конфиге, где вы включаете fastgci, например, в конфигурации веб-сервера Nginx. Минимально для настройки кэша fastgci достаточно добавить в блок server такие строки:

fastcgi_cache_path /var/nginx/cache levels=1:2 keys_zone=MYAPP:100m inactive=60m;
fastcgi_cache_key «$scheme$request_method$host$request_uri»;

Первая строка настраивает сразу несколько параметров, во первых, она добавляет путь к кэшу, можно использовать любой, только чтобы папка существовала и у веб-сервера были права для записи в нее. Директива levels указывает сколько подпапок будет. Следующая строка указывает что будет использоваться в качестве ключа для кэша. Ключ будет хэширован в md5.

Теперь нужно настроить блок обработки php:

\.php$ <
fastcgi_pass unix:/var/run/php7-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_cache MYAPP;
fastcgi_cache_valid 200 60m;
>

Здесь мы обращаемся к уже настроенной зоне памяти MYAPP, а также указываем время жизни кэша в один час. Для проверки кэширования можно посмотреть содержимое папки:

ls -lR /var/nginx/cache/

С помощью таких методов ваши страницы будут загружаться намного быстрее. Если вам понадобится отключить кєширование php для отдельных страниц, то сначала создаем переменную no_cache со значением 0:

Затем проверяем нужные параметры, и если соответствует, то устанавливаем значение в 1:

if ($request_method = POST)
<
set $no_cache 1;
>

И на завершение передаем значение этой переменной таким директивам, это отключит кэширование когда не нужно:

fastcgi_cache_bypass $no_cache;
fastcgi_no_cache $no_cache;

В старые добрые времена, когда создание web-сайтов представляло из себя такое простое занятие, как набор нескольких HTML-страниц, отправка web-страниц в браузер была простой отправкой файла web-сервером. Посетители сайта могли видеть эти небольшие, исключительно текстовые странички, почти мгновенно (если не считать пользователей медленных модемов). Как только страница была загружена, браузер кэширует её где-нибудь на локальном компьютере, чтобы в случае повторного запроса страницы, можно было взять его локальную версию из кэша, послав лишь короткий запрос, чтобы убедиться, что страница на сервере не была изменена. Запросы обрабатывались быстро и как можно эффективней, и все были счастливы (кроме использующих модемы 9600 бод).

Появление динамических web-страниц изменило положение вещей в худшую сторону, эффективно «сломав» эту модель обслуживания web-страниц благодаря наличию двух проблем:

Когда сервером получен запрос динамической web-странички, производится некоторая промежуточная обработка, например синтаксический анализ (парсинг) скрипта движком PHP, которая должна быть завершена. Благодаря этому получаем задержку перед тем, как web-сервер начнёт отправку вывода в браузер. Для простого PHP-скрипта это не существенно, но для более сложного приложения движок PHP может выполнить много действий прежде чем страница будет готова для отправки. Эти дополнительные действия приводят к заметной задержке между запросами пользователей и реальным отображением страниц в их браузерах.

Типичный web-сервер, например Apache, использует время модификации файла чтобы правильно сообщить web-браузеру состояние кэша запрашиваемой странички. Для динамических web-страниц, фактически PHP-скрипт может изменяться только изредка, в то время как отображаемый им контент, возможно располагающийся в базе данных, изменяется часто. Web-сервер не имеет возможности знать о наличии изменений в базе данных, тем не менее он не отправляет дату последней модификации. Если клиент (браузер) не получает никакого признака того, как долго данные являются корректными, он предполагает, что в следующий раз необходимо запросить страничку по новой. Web-сервер всегда будет отвечать обновлённой версией странички, независимо от того, изменились ли данные. Чтобы избежать этого недостатка большинство web-разработчиков используют мета-тэги или HTTP-заголовки, чтобы сообщить браузеру никогда не использовать кэшированную версию странички. Однако это отрицает естественную способность web-браузера кэшировать web-страницы и обладает некоторыми существенными недостатками. Например, содержание динамической странички может изменяться раз в сутки, поэтому выгода, получаемая от наличия даже 24-часового кэширования странички браузером, очевидна.

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

Обратите внимание, что в главе «Кэширование» обсуждаются только решения, осуществляемые при помощи PHP. Они не должны быть перепутаны с решениями кэширования скриптов, работающими на основе оптимизации и кэширования откомпилированных PHP-скриптов. В эту группу можно включить Zend Accelerator, ionCube PHP Accelerator, Turck MMCache/eaccelerator, APC.

Как я предотвращаю кэширование страницы браузерами?

Прежде чем мы рассмотрим методы клиентского и серверного кэширования, в первую очередь мы должны понять, как вообще предотвратить кэширование страниц web-браузером (и прокси-серверами). Основной способ достижения этого использует мета-тэги HTML:

«Mon, 26 Jul 1997 05:00:00 GMT» />

Вставив прошедшую дату в мета-тэг Expires, вы сообщаете браузеру, что кэшированная копия странички всегда является устаревшей. Это значит, что браузер никогда не должен кэшировать страницу. Мета-тэг Pragma: no-cache довольно хорошо поддерживаемое соглашение, которому следует большинство web-браузеров. Обнаружив этот тэг, они обычно не кэшируют страницу (хотя никаких гарантий нет, это всего лишь соглашение).

Это хорошо звучит, но есть две проблемы, связанные с использованием мета-тэгов:

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

Прокси-серверы, кэширующие web-страницы, как например общий ISP, вообще не будет исследовать непосредственно содержимое HTML-документа. Вместо этого они полагаются только на web-сервер, с которого пришли документы, и протокол HTTP. Иными словами, web-браузер может считать, что не должен кэшировать страницу, но прокси-сервер между браузером и вашим web-сервером вероятно не знает этого – и продолжит отправлять клиенту ту же самую, уже устаревшую, страницу.

Лучший подход состоит в том, чтобы использовать непосредственно протокол HTTP с помощью функции PHP header, эквивалентно приведённым выше двум мета-тэгам:

header () ; header («Pragma: no-cache» ) ;

Мы можем пойти на один шаг вперёд, воспользовавшись заголовком Cache-Control, совместимым с браузерами, поддерживающими HTTP 1.1:

header ( «Expires: Mon, 26 Jul 1997 05:00:00 GMT» ) ; header ( «Cache-Control: no-store, no-cache, must-revalidate» ) ; header ( «Cache-Control: post-check=0, pre-check=0» , FALSE ) ; header («Pragma: no-cache» ) ;

Это гарантирует, что никакой web-браузер или промежуточный прокси-сервер не будет кэшировать страницу, таким образом посетители всегда получат самую последнюю версию контента. Фактически, первый заголовок должен быть самодостаточным, это лучший способ гарантировать, что страница не кэшируется. Заголовки Cache-Control и Pragma добавлены с целью «подстраховаться». Хотя они не работают во всех браузерах или прокси, они отловят некоторые случаи, в которых Expires не работает должным образом (например, если дата на компьютере клиента установлена неправильно).

Конечно, полный отказ от кэширования обеспечивает нас проблемами, которые мы обсуждали в начале этой главы. Сейчас мы рассмотрим решение этих проблем. Internet Explorer и кэширование загрузки файлов

Наше обсуждение о PDF, приведённое в главе 3, Альтернативные типы контента, объяснило, что проблемы могут возникать, когда вы имеете дело с кэшированием и загрузкой файлов. Если при обслуживании загрузки файла PHP-скриптом используются такие заголовки, как например Content-Disposition: attachment, filename=myFile.pd f или Content-Disposition: inline, filename=myFile.pdf , у вас будут проблемы с Internet Explorer’ом, если вы сообщите браузеру не кэшировать страницу.

Internet Explorer оперирует загрузкой довольно необычным образом, выполняя два запроса к web-сайту. Первый запрос загружает файл и сохраняет его в кэше, пока не будет создан второй запрос (без сохранения отклика). Этот запрос вызывает процесс передачи файла конечному пользователю в соответствии с типом файла (например, запускает Acrobat Reader, если файл является PDF-документом). Это значит, что если вы отправили заголовки, запрещающие браузеру кэшировать страницу, Internet Explorer удалит файл между первым и вторым запросом, в результате чего конечный пользователь ничего не получит. Если файл, который вы отдаёте PHP-скриптом, не изменяется, одним из простейших решений будет убрать «запрещающие кэширование» заголовки из скрипта.

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

Как я могу захватить данные на стороне сервера для кэширования?

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

Несколько слов о кэшировании при помощи шаблонов

Шаблонные движки типа Smarty часто говорят о кэшировании шаблонов. Обычно эти движки предлагают встроенный механизм для сохранения откомпилированной версии шаблона (т.е. генерируют из шаблона PHP-исходник), что предохраняет нас от необходимости парсить шаблон каждый раз, когда запрашивается страница. Это не нужно путать с кэшированием вывода, которое имеет отношение к кэшированию предоставляемого HTML (или другого вывода), который посылает PHP в браузер. Вы можете успешно использовать оба типа кэширования одновременно на одном и том же сайте.

Сейчас мы рассмотрим встроенный механизм кэширования на PHP, использующий буферизацию вывода, который может использоваться вами независимо от способа создания контента (с шаблонами или без шаблонов). Рассмотрим ситуацию в которой ваш скрипт отображает результат использую, к примеру, echo или print, чтобы выдать данные непосредственно в браузер. В таком случае вы можете использовать функции управления выводом PHP для хранения данных в буферной памяти, над которой ваш PHP-скрипт имеет и доступ, и контроль.

Вот простой пример:

Ob_start () ; // Выводим некоторый текст (который сохраняется в буфере); echo «1. Выводим это в буфер
» ; // Останавливаем буферизацию и очищаем буфер вывода ob_end_clean () ; // Выводим некоторый текст обычным образом echo «2. Нормальный вывод
» ; // Вывод содержимого буфера echo $buffer ;

Сам буфер хранит вывод как строку. Так, в вышеприведённом скрипте мы начинаем буферизацию с ob_start и используем echo, чтобы вывести что-либо. Затем мы используем ob_get_contents, чтобы выбрать данные, помещённые в буфер оператором echo, и сохранить их в строке. Функция ob_end_clean останавливает буферизацию вывода и уничтожает его содержимое, как альтернативу можно использовать ob_end_flush, чтобы вывести содержимое буфера.

Вышеописанный скрипт выведет:

2. Нормальный вывод 1. Выводим это в буфер

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

Заголовки HTTP и буферизация вывода

Буферизация вывода может помочь решить наиболее общую проблему, связанную с функцией header, не говоря уже о session_start и set_cookie. Обычно, если вы вызываете любую из этих функций после того, как начался вывод страницы, вы получите противное сообщение об ошибке. При включенной буферизации вывода единственным типом вывода, избегающим буферизации, являются HTTP-заголовки. Используя ob_start в самом начале выполнения вашего приложения, вы можете посылать заголовки в любой понравившейся точке программы, не сталкиваясь с обычными ошибками. Затем, как только вы будете уверены, что больше выводить HTTP-заголовки не потребуется, вы можете сразу же вывести содержимое страницы из буфера. (прим. переводчика: следует заметить что подобное использование данной функции крайне неоправдано. В большинстве случаев необходимости в использовании буферизации вывода для избавления ошибок указанного типа просто не существует и всё с лёгкостью может быть исправлено правильным проектированием приложения)

Использование буферизации вывода для кэширования на стороне сервера

Вы уже видели базовый пример буферизации вывода, теперь следующий шаг, в котором буфер сохраняется в файл:

Простая и эффективная система кеширования PHP. Кэширования байткода в PHP

Ни для кого не секрет, что чем быстрее загружается сайт тем удобнее на нем будет пользователям. Если страницы загружаются быстро пользователи не будут уходить из вашего сайта и поисковики будут относиться к сайту лучше. Для многих современных сайтов узким местом становиться движок выполнения динамических скриптов PHP.

Веб-сервер Nginx при правильной настройке может отдавать просто огромное количество страниц мгновенно, чего нельзя сказать про PHP на генерацию страницы может уходить до нескольких секунд. Но PHP тоже можно ускорить с помощью кэширования. В этой статье мы рассмотрим как настраивается кэширование php, каким оно бывает и зачем вообще это нужно. Для примера будем использовать связку php-fpm и Nginx, но информация из статьи подойдет и для других вариантов установки.

Особенность интерпретируемых языков в том, что при каждом запуске скрипта интерпретатор должен скомпилировать программу и проверить ее на ошибки. Но мы можем обойти. Есть два основных вида кэширования:

  • Кэширование готовых страниц — страница генерируется php, а потом пользователю отдается готовая страница без обращения к php. Я расскажу как это сделать через fastcgi, но не рекомендую применять такой метод для wordpress или других движков, их лучше кэшировать с помощью специальных плагинов;
  • Кэширование байт кода и инструкций — а это уже интересно, кэшируется не вся страница, а только некоторые инструкции, и куски байт кода, которые не изменяются при вызовах скрипта. Перед тем как выполнять скрипт, интерпретатор должен преобразовать его в понятный для него формат, при кэшировании такое преобразование выполняется только первый запуск, а дальше берется версия из кэша;
  • Кэширование сессий — по умолчанию php сохраняет сессии пользователей в файлы и мы можем немного ускорить его работу, если будем сохранять сессии в оперативную память.

Кэширования байткода в PHP

Начиная с PHP 5.5 в интерпретатор языка была добавлена поддержка кэширования байткода из ZendFramework. В новых версиях этот кэш позволяет очень сильно увеличить производительность вашего ресурса, например, есть сведения, что на PHP 7 Wordpres и другие движки работают чуть ли не в два раза быстрее. Перед тем как настраивать кєширование opcode php, нужно установить его пакет:

sudo apt install php-opcache

Или для Red Hat дистрибутивов:

sudo yum install php-opcache

Затем, чтобы включить кэширование нужно добавить несколько строк в php.ini, можно также создать отдельный файл в /etc/php/conf.d/

zend_extension=opcache.so;
opcache.error_log=/var/log/php-fpm/opcache-error.log
opcache.enable=1;
opcache.memory_consumption=256;
opcache.interned_strings_buffer=8;
opcache.max_accelerated_files=4000;
opcache.reval > opcache.fast_shutdown=0;
opcache.enable_cli=0;
opcache.reval > opcache.val > opcache.max_file_size=0;
opcache.file_cache= /var/www/сайт/opcache;

Рассмотрим что означают эти строки, чтобы вы знали какие значения установить. Первая строка загружает расширение, здесь ничего менять не нужно.

  • opcache.error_log — указывает файл для записи лога ошибок, будет полезно при отладке;
  • opcache.log_verbosity_level — указывает насколько подробным должен быть лог файл, значение от 1 до 4;
  • opcache.enable — включает кэширование;
  • opcache.enable_cli — включает кэширование страниц php для консольной версии;
  • opcache.memory_consumption — количество оперативной памяти для хранения кэша;
  • opcache.max_accelerated_files — число скриптов/файлов, которые нужно кэшировать;
  • opcache.validate_timestamps — проверять время изменения данных в файле скрипта;
  • opcache.revalidate_freq — частота проверки для предыдущего параметра;
  • opcache.revalidate_path — установите в 0 чтобы выполнять проверку при include только первый раз;
  • opcache.enable_file_override — кэширует запросы к атрибутам файлов, например, существование и т д;
  • opcache.blacklist_filename — список файлов, которые не нужно кэшировать;
  • opcache.max_file_size — максимальный размер файла скрипта для кэширования, 0 — не ограниченно;
  • opcache.interned_strings_buffer — допустимое количество строк в буфере;
  • opcache.fast_shutdown — использовать быстрый способ освобождения памяти.

После сохранения всех настроек вам останется только перезапустить php или ваш веб-сервер:

systemctl restart php-fpm

Для того чтобы убедиться, что все работает вы можете захотеть посмотреть какие скрипты уже закэшированы. Для этого можно использовать скрипт opcache-status . Просто сохраните скрипт в директорию веб-сервера, а затем дайте ему права:

chmod 777 /var/www/сайт/opcode.php

Здесь можно видеть подробную статистику по кєширвоанию, настройки и количество занятой памяти.

Хранение сессий в memcached

По умолчанию php хранит сессии в файловой системе, в некоторых случаях, вы можете достаточно сильно ускорить работу php, если перенесете хранение сессий из файлов в оперативную память, например, memcached. Сначала нужно установить memcached и php библиотеку для работы с ней:

sudo apt install memcached php-memcached

Или для систем на базе Red Hat:

sudo yum install memcached php-memcached

Сначала нам нужно настроить memcached, откройте файл /etc/sysconfig/memcached и найдите строку CACHESIZE, здесь нужно указать объем оперативной памяти, которая выделяется под кэш:

session.save_handler = memcache
session.save_path = «tcp://localhost:11211»

Осталось перезапустить ваш php интерпретатор:

systemctl restart php-fpm

Если вы хотите проверить все ли правильно кэшируется и есть ли вообще что-либо в кэше, можно использовать phpmemcacheadmin .

Кэширование страниц fastcgi

Я не советую использовать кэширование fastgci для сайтов WordPress, потому что там есть специальные плагины, которые могут точно контролировать кэш, очищать его когда нужно и вовремя обновлять. Но во всех остальных случаях кэш fastcgi может очень сильно ускорить работу сайта. Настраивается он в конфиге, где вы включаете fastgci, например, в конфигурации веб-сервера Nginx. Минимально для настройки кэша fastgci достаточно добавить в блок server такие строки:

fastcgi_cache_path /var/nginx/cache levels=1:2 keys_zone=MYAPP:100m inactive=60m;
fastcgi_cache_key «$scheme$request_method$host$request_uri»;

Первая строка настраивает сразу несколько параметров, во первых, она добавляет путь к кэшу, можно использовать любой, только чтобы папка существовала и у веб-сервера были права для записи в нее. Директива levels указывает сколько подпапок будет. Следующая строка указывает что будет использоваться в качестве ключа для кэша. Ключ будет хэширован в md5.

Теперь нужно настроить блок обработки php:

\.php$ <
fastcgi_pass unix:/var/run/php7-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_cache MYAPP;
fastcgi_cache_valid 200 60m;
>

Здесь мы обращаемся к уже настроенной зоне памяти MYAPP, а также указываем время жизни кэша в один час. Для проверки кэширования можно посмотреть содержимое папки:

ls -lR /var/nginx/cache/

С помощью таких методов ваши страницы будут загружаться намного быстрее. Если вам понадобится отключить кєширование php для отдельных страниц, то сначала создаем переменную no_cache со значением 0:

Затем проверяем нужные параметры, и если соответствует, то устанавливаем значение в 1:

if ($request_method = POST)
<
set $no_cache 1;
>

И на завершение передаем значение этой переменной таким директивам, это отключит кэширование когда не нужно:

fastcgi_cache_bypass $no_cache;
fastcgi_no_cache $no_cache;

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

Сами кэши бываю двух видов — локальные и общие. Локальный это кеш, хранимый непосредственно на диске у клиента, создаваемый и управляемый его браузером. Общий — кэш прокси-сервера организации или провайдера и может состоять из одного или нескольких прокси-серверов. Локальный кеш присутствует, наверное в каждом браузере, общими пользуется значительная часть людей использующих Internet. И если малую часть сайтов сейчас оценивают по расходу трафика, то скорость загрузки — важный критерий, который должен учитываться при разработке Вашего web-проекта.

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

Кэшировать или нет?

Возможность сохранения в кэш страницы определяется динамичностью информации в источнике данных. Таким образом необходимость использования кэша определяется Вами, исходя из планируемого времени жизни страницы.

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

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

Общие принципы сохранения страниц в кэш

PHP-программа может управлять кэшированием результатов ее работы формируя дополнительные поля в заголовке HTTP ответа вызовом функции Header().

Несколько общих утверждений характерных не только для PHP-программ:

  • Страницы передаваемые по POST никогда не сохраняются в кэш.
  • Страницы запрашиваемые по GET и содержащие параметры (в URL присутствует «?») не сохраняются в кэш, если не указано обратное.

Таким образом в большинстве ситуаций дополнительных инструкций в программу добавлять не надо. Основные моменты на которые следует обратить внимание можно свести к двум:

  • запрет кэширования документов, кэшируемых по умолчанию
  • кэширование документов, не подлежащих кэшированию по умолчанию.

Запрет кэширования документов, кэшируемых по умолчанию

Эта задача возникает для PHP-скриптов вызываемых без параметров или являющимися индексами директорий, однако формирующих данные персонально под пользователя (например на основе cookies или user agent) или работающих на основе быстро изменяющихся данных. По спецификации HTTP/1.1 мы можем управлять следующими полями:

  • Expires — Задает дату истечения срока годности документа. Задание ее в прошлом определяет запрет кэш для данной страницы.
  • Cache-control: no-cache — Управление кэш. Значение no-cache определяет запрет кэш данной страницы. Для версии протокола HTTP/1.0 действует «Pragma: no-cache».
  • Last-Modified — Дата послднего изменения содержимого. Поле актуально только для статических страниц. Apache заменяет это поле значением поля Date для динамически генерируемых страниц, в том числе для страниц содержащих SSI.

На сайте www.php.net дается следующий код для запрета кеширования.

Header(«Expires: Mon, 26 Jul 1997 05:00:00 GMT»); // Date in the past header(«Last-Modified: » . gmdate(«D, d M Y H:i:s») . » GMT»); // always modified header(«Cache-Control: no-cache, must-revalidate»); // HTTP/1.1 header(«Pragma: no-cache»); // HTTP/1.0

Однако, я считаю, что данный заголовок избыточен. В большинстве случаев достаточно:

Чтобы пометить документ как «уже устаревший» следует установить Expires равным полю Date.

Header(«Expires: » . gmdate(«D, d M Y H:i:s») . » GMT»);

Ну и не следует забывать, что формы, запрошенные по POST также не подлежат кэшированию.

Кэширование документов, не подлежащих кэшированию по умолчанию

Обратная задача, может показаться на первый взгляд абсурдной. Однако и в этом существует потребность. Кроме простой минимизации трафика при разработке web-программы следует учитывать комфортность работы с ней пользователя. Например, некоторые страницы Вашего сервера формируются на основе статических данных большого объема. Возможность включения их в кэш существенно улучшит скорость работы сервера для пользователя и частично освободит Ваш от многочисленных повторных генераций такой страницы. Заголовок разрешающий сохранение на прокси-серверах:

Если страница учитывает информацию сохраненную в браузере пользователя (тип и версию браузера, ключи, авторизацию и т.д.) такую страницу нельзя сохранить на прокси, однако возможно ее сохранение в локальном кэш браузера:

Кэширование до истечения корректности

Описанные выше решения довольно прямолинейны, хотя и подходят для большинства задач. Но протокол HTTP/1.1 имеет средства для более тонкого управления кэш страниц, и существуют задачи требующие применения этих механизмов. Как пример — web-приложения работающие с данными большого объема и прогнозируемой динамичностью. Корректность данных может устанавливаться как по дате прогнозируемого обновления, так и по изменению содержания. Для этих случаев используются разные заголовки управления кэш.

Кэширование с прогнозируемым обновлением

Рассмотрим пример — прайс лист обновляемый по понедельникам. Вы заранее знаете, что содержание страницы можно хранить в кэш до наступления новой недели, что и следует указать в заголовке ответа обеспечивая нужное поведение страницы в кэш.

Основная задача — получить дату следующего понедельника в формате RFC-1123

$dt_tmp=getdate(date(«U»)); header(«Expires: » . gmdate(«D, d M Y H:i:s», date(«U»)-(86400*($dt_tmp[«wday»]-8))) . » GMT»); header(«Cache-control: public»);

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

Другой подход, применяемый при более оперативном обновлении информации и одновременной высокой посещаемости сервера (иначе кэширование не будет эффективным) состоит в использовании заголовка Cache-control: max-age=секунды, определяющий время по истечении которого документ считается устаревшим и имеющий больший приоритет при вычислении «свежести» документа.

Если Вы публикуете новости с интервалом в 30 минут:

Илон Маск рекомендует:  Что такое код mysql_fetch_lengths

Header(«Cache-control: public»);
header(«Cache-control: max-age=1800»);

Кэширование по содержанию

Еще более интеллектуальный вид управления предоставляет HTTP/1.1 на основе содержимого с помощью директив Vary. Я очень рекомендую применять его при формировании изображений или текстов большого объема, которые как показывает практика изменяются крайне редко. При этом у пользователя в случае возврата не будет происходить их повторной выгрузки, если содержание осталось прежним, и страница будет взята с Вашего сервера, если ее содержание изменилось.

Рассмотрим пример выдачи изображения из базы данных индентифицируемых по ID. Вызов страницы выглядит следующим образом:

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

Mysql_connect(«host», «user», «passwd»); $image=mysql(«db», «select pics,type from pictures where ); mysql_freeResult($image); mysql_close();

Для управления используется MD5 сумма содержимого изображения. Пока содержание не изменилось, сумма будет постояной. В случае изменения содержания в базе на сервере клиент выполнит запрос для повторного формирования содержания. Пока изображение постоянно содержимое будет отображаться из кэш.

Примечания для Russian Apache

И приятное (или неприятное) сообщение для пользователей Russian Apache. Так как сервер выдает старину по пользовательской кодировке он автоматически снабжает ВСЕ страницы (не только динамические) заголовками запрета кэширования.

Основной проблематикой кэширования является быстрота реакции на запросы к основным системам хранения и обработки входящей и исходящей структурированной информации.

Представьте, что необходимо осуществить быструю передачу информации, однако скорость доступа к данным крайне низкая. Или другая ситуация: скорость хорошая, но мало доступной памяти или ширина канала недостаточная, или процессорные и дисковые факторы мешают осуществить задачу. В этом случае кэширование – это единственный выход из ситуации.

Виды кэширования

Необходимо понимать, что работу с данными можно производить как на стороне клиента, так и на сервере. Притом, серверная обработка данных централизована и имеет ряд несомненных преимуществ (особенно для службы поддержки).

Существует несколько видов кэширования, предлагаем рассмотреть каждый вид, его особенности и рекомендации по применению:

1. Браузерное кэширование или клиентское кэширование
1.1 Кэширование файлов и картинок

Это первый уровень кэширования, который состоит в отдаче заголовка «expired» и заголовка «304 Not Modified» . Наиболее эффективным считается кэширование на 2 недели.

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

1.2 Кэширование https
1.3 Кэширование центра сертификации

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

1.4 Кэширование страниц

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

Как правило, кэш подразделяется по типу пользователей:

Данное разделение обусловлено уникальностью контента, для каждого авторизованного пользователя и общностью контента для гостевых пользователей. В большинстве сайтов не авторизованный пользователь не может изменять содержимое сайта, а значит и влиять на его содержимое.

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

2. Серверное кэширование
2.1 Кэширование страницы целиком

Пожалуй, любой когда-либо мечтал о сайте, работающем со скоростью «ping» или быстрее.
Но и у этого типа кэша есть свои минусы: например, невозможность кэшировать страницы для авторизованного пользователя, либо пользователя, содержимое страницы которого зависит от текущих переменных пользователя.

Используйте этот кэш, если серверу известны все статичные состояния внешних данных, такие как: uri, get (без дополнительных параметров), пользователь не авторизован — то есть, фактически, это идеальное состояние страницы для гостевых пользователей. Учитывайте тот факт, что при таком кэшировании архитектура сайта или приложения всегда должна однотипно обрабатывать входящие запросы и отдавать однотипные ответы. Такое состояние есть в любом приложении или сайте, его нужно лишь отследить и применить к нему кэш.

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

2.2 Кэширование результатов компиляции php-файлов

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

2.3 Кэширование отдельных блоков страницы

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

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

2.4 Кэширование php на основе неразделяемых ресурсов
2.5 Кэширование php на основе общих ресурсов
2.6 Кэширование mysql на основе query cache

Наверняка, вы регулярно сталкивались с ситуацией, когда необходимо отдать новые материалы, дата публикации которых уже разрешена текущим timestamp? Проще говоря,

Вставив прошедшую дату в мета-тэг Expires, вы сообщаете браузеру, что кэшированная копия странички всегда является устаревшей. Это значит, что браузер никогда не должен кэшировать страницу. Мета-тэг Pragma: no-cache довольно хорошо поддерживаемое соглашение, которому следует большинство web-браузеров. Обнаружив этот тэг, они обычно не кэшируют страницу (хотя никаких гарантий нет, это всего лишь соглашение).

Это хорошо звучит, но есть две проблемы, связанные с использованием мета-тэгов:

  1. Если тэг не существовал когда страница была запрошена браузером впервые, но появляется позже (например, вы модифицировали включаемый файл pageheader.php который является шапкой каждой web-страницы), браузер останется в блаженном неведении и воспользуется свей кэшированной копей оригинала.
  2. Прокси-серверы, кэширующие web-страницы, как например общий ISP , вообще не будет исследовать непосредственно содержимое HTML -документа. Вместо этого они полагаются только на web-сервер, с которого пришли документы, и протокол HTTP . Иными словами, web-браузер может считать, что не должен кэшировать страницу, но прокси-сервер между браузером и вашим web-сервером вероятно не знает этого – и продолжит отправлять клиенту ту же самую, уже устаревшую, страницу.

Лучший подход состоит в том, чтобы использовать непосредственно протокол HTTP с помощью функции PHP header() , эквивалентно приведённым выше двум мета-тэгам:

Мы можем пойти на один шаг вперёд, воспользовавшись заголовком Cache-Control совместимым с браузерами, поддерживающими HTTP 1.1:

Header(«Expires: Mon, 26 Jul 1997 05:00:00 GMT»); header(«Cache-Control: no-store, no-cache, must-reval );

Это гарантирует, что никакой web-браузер или промежуточный прокси-сервер не будет кэшировать страницу, таким образом посетители всегда получат самую последнюю версию контента. Фактически, первый заголовок должен быть самодостаточным, это лучший способ гарантировать, что страница не кэшируется. Заголовки Cache-Control и Pragma добавлены с целью «подстраховаться». Хотя они не работают во всех браузерах или прокси, они отловят некоторые случаи, в которых Expires не работает должным образом (например, если дата на компьютере клиента установлена неправильно).

Конечно, полный отказ от кэширования обеспечивает нас проблемами, которые мы обсуждали в начале этой главы. Сейчас мы рассмотрим решение этих проблем.

Internet Explorer и кэширование загрузки файлов

Если при обслуживании загрузки файла PHP -скриптом используются такие заголовки, как например Content-Disposition: attachment, filename=myFile.pdf или Content-Disposition: inline, filename=myFile.pdf у вас будут проблемы с Internet Explorer ’ом, если вы сообщите браузеру не кэшировать страницу.

Internet Explorer оперирует загрузкой довольно необычным образом, выполняя два запроса к web-сайту. Первый запрос загружает файл и сохраняет его в кэше, пока не будет создан второй запрос (без сохранения отклика). Этот запрос вызывает процесс передачи файла конечному пользователю в соответствии с типом файла (например, запускает Acrobat Reader , если файл является PDF -документом). Это значит, что если вы отправили заголовки, запрещающие браузеру кэшировать страницу, Internet Explorer удалит файл между первым и вторым запросом, в результате чего конечный пользователь ничего не получит. Если файл, который вы отдаёте PHP -скриптом, не изменяется, одним из простейших решений будет убрать «запрещающие кэширование» заголовки из скрипта.

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

Как я могу захватить данные на стороне сервера для кэширования?

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

Несколько слов о кэшировании при помощи шаблонов

Как мне управлять кэшированием на стороне клиента средствами PHP?

Пришло время посмотреть на механизм, который позволит нам контролировать кеш на стороне клиента средствами PHP . Этот подход будет работать только если вы используете PHP в связке с сервером Apache , поскольку мы будем использовать функцию getallheaders() , чтобы получить заголовки, передаваемые браузером. Эта функция работает только в Apache .

Новые имена функций

Если вы используете PHP 4.3.0 с Apache , HTTP-заголовки доступны функцией apache_request_headers() и apache_response_headers() . Функция getallheaders() стала псевдонимом для новой функции apache_request_headers() .

Механизмом для работы с кэшем web-браузера вновь является HTTP . Множество заголовков вовлечёны в инструктирование web-браузеров и прокси-серверов независимо кэшировать страницу, ситуация осложняется тем фактом, что некоторые из них доступны только с HTTP 1.1.

Проверка HTTP-заголовков в вашем браузере

Простым но очень удобным инструментом для проверки заголовков запросов и откликов является LiveHttpHeaders – аддон к браузеру Mozilla . Необходимо точно знать, какие заголовки посылает ваш скрипт, особенно когда вы имеете дело с заголовками кэширования HTTP .

Для простоты мы рассмотрим только заголовки кэширования HTTP 1.0, а именно Expires , Last-Modified и If-Modified-Since , а также статус-код HTTP 304 (Not Modified) .

Другие заголовки, доступные с HTTP 1.1, например Cache-Control и ETag , предназначены для обеспечения расширенного механизма, который может использоваться совместно с состоянием web-сессии, иными словами, версия данной страницы, отображаемой неавторизованному посетителю, может значительно отличаться от отображаемой авторизованному пользователю. Заголовки HTTP 1.1 изначально добавлялись для того, чтобы позволить кэшировать такие страницы.

Истечение срока жизни страницы

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

Пример 7. 6.php

Функция setExpires отправляет заголовок HTTP Expires с будущим временем, заданном в секундах. Вышеприведённый пример показывает текущее время по Гринвичу и выводит ссылку, которая вам позволяет перейти на страницу вновь. Используя кнопку Refresh вашего браузера, вы можете сообщить браузеру о желании обновить кэш. Используя ссылку, вы увидите, что время изменяется только раз в 10 секунд.

Даты и время в HTTP

Даты в HTTP всегда вычисляются относительного меридиана времени Гринвича (GMT). Функция PHP gmdate() точно такая же функция, как date() , за исключением того, что она автоматически компенсирует время по Гринвичу, основанное на системных часах и настройках региона вашего сервера.

Когда браузер сталкивается с заголовком Expires , он кэширует страницу. Все последующие запросы страницы, сделанные до указанного времени истечения срока жизни, используют версию страницы из кэша, никаких запросов к web-серверу при этом не происходит.

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

Время изменения страницы

Более практично использовать заголовки Last-Modified и If-Modified-Since , доступные в HTTP 1.0. Технически он известно как выполнение условного GET-запроса, вы возвращаете любой контент, основываясь на условии пришедшего заголовка запроса If-Modified-Since .

При использовании этого метода вы должны отправлять заголовок Last-Modified каждый раз, когда обращаются к вашему PHP-скрипту. При следующем запросе страницы браузером, он отправит заголовок If-Modified-Since , содержащий время, по которому ваш скрипт может определить, обновлялась ли страница со времени последнего запроса. Если это не так, ваш скрипт посылает код статуса HTTP 304 , чтобы указать, что страница не изменялась, не выводя при этом содержимого страницы.

Устанавливаем время модификации кэш-файла этой строкой: $lastModified = filemtime($cache_file);

Затем, используя время модификации кэш-файла, мы посылаем заголовок Last-Modified . Нам нужно посылать её для каждой предоставляемой страницы, чтобы вынудить браузер посылать нам заголовок If-Modified-Since с каждым запросом.

// Выдаём заголовок HTTP Last-Modified header(«Last-Modified: » . gmdate(«D, d M Y H:i:s», $lastModified) . » GMT»); \n \n»; echo » \n»; echo » \n»; echo » \n»; ?>

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

Будьте осторожны при тестировании любого кэширования, выполненного в таком стиле, если вы сделаете это неправильно, вы можете заставить ваших посетителей всегда иметь устаревшие копии вашего сайта.

Кэширование ваших страниц в 5 шагов

Кэширование ваших страниц может оказаться красивым и полезным механизмом, особенно, если они генерируются средствами PHP и делают множество SQL запросов. Как только вы примените кэширование, ваш сервер тут же снизит нагрузку и перестанет съедать много памяти на генерацию страниц — он просто будет загружать их из КЭШа. Я покажу вам, как PHP может кэшировать страницы и, в дальнейшем, вы сможете тратить на это минут 5.

Расмотрим технологию кэширования пошагам:

  1. В домашней директории создаем файлы .htaccess , start_cache.php , end_cache.php , а также папку с названием cache_files .
  2. Папке cache_files необходимо проставить атрибуты 777 .
  3. Внутри .htaccess файла пропишите следующие строки: php_value auto_prepend_file /home/username/public_html/start_cache.php php_value auto_append_file /home/username/public_html/end_cache.php Строку /home/username/public_html/ необходимо заменить на путь к вашей домашней директории.
  4. В скрипт start_cache.php помещаем следующий код: Не забывайте исправлять путь /home/username/public_html/ на путь к вашей домашней директории.
  5. А следующий код поместите в скрипт end_cache.php :

Все ваши страницы будут кэшироваться на 3600 секунд = 1 час. Этот параметр вы легко можете поменять в скрипте start_cache.php . Кэш страниц будет сохранен в папке cache_files .

Совершенно очевидно, что в данном случае атрибуты 777 являются определенным нарушением безопасности. В связи с чем, рекомендую вынести папку cahce_files за пределы public_html , например, поместить ее на один уровень выше. Это закроет доступ к находящимся в ней файлам пользователей вашего сайта, но никак не повлияет на работоспособность системы.

Также, у данного метода есть еще один серьезный недостаток: автор статьи складывает весь кэш в одну папку, что, при достаточном количестве страниц на вашем сайте, вызовет проблему, например, в системах Unix наблюдается достаточное замедление работоспособности при наличие в папке более чем 1000 файлов. В связи с чем, в алгоритм необходимо внести ряд изменений и раскладывать файлы по отдельным подпапкам внутри папки cache_files . Например, используя для этого первые 3-4 символа md5 КЭШа.

Для динамических ресурсов вполне возможно выбрать время кэширования в несколько (5-10) секунд или 1-2 минуты, что уже значительно снизит нагрузку на сервер, но не нанесет вреда интерактивности сайта.

Для страниц, для которых особо важна интерактивность, можно ввести исключения в .htaccess , что позволит именно им постоянно изменяться, а для остальных страниц можно применять кэширование.

Регенерация содержания на лету

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

Это делается следующим набором директив:

RewriteCond % !-s RewriteRule ^page\.html$ page.php

Здесь, запрос к page.html приводит к внутреннему запуску соответствующего page.php, если page.html все-ещё отсутствует или имеет нулевой размер. Фокус здесь в том что page.php это обычный PHP скрипт который в дополнение к собственному выводу, записывает свой вывод в файл page.html. Запустив это один раз, сервер передает данные page.html. Когда вебмастер хочет обновить содержание, он просто удаляет page.html (обычно с помощью cronjob).

Проблема с кэшированием страниц у Internet Explorer.

У IE при работе с заголовком «Vary» встречается одна неприятная ошибочка, связанная с кэшированием страниц. Проблема решается добавлением в.htaccess следующих строк:

В старые добрые времена, когда создание web-сайтов представляло из себя такое простое занятие, как набор нескольких HTML-страниц, отправка web-страниц в браузер была простой отправкой файла web-сервером. Посетители сайта могли видеть эти небольшие, исключительно текстовые странички, почти мгновенно (если не считать пользователей медленных модемов). Как только страница была загружена, браузер кэширует её где-нибудь на локальном компьютере, чтобы в случае повторного запроса страницы, можно было взять его локальную версию из кэша, послав лишь короткий запрос, чтобы убедиться, что страница на сервере не была изменена. Запросы обрабатывались быстро и как можно эффективней, и все были счастливы (кроме использующих модемы 9600 бод).

Появление динамических web-страниц изменило положение вещей в худшую сторону, эффективно «сломав» эту модель обслуживания web-страниц благодаря наличию двух проблем:

Когда сервером получен запрос динамической web-странички, производится некоторая промежуточная обработка, например синтаксический анализ (парсинг) скрипта движком PHP, которая должна быть завершена. Благодаря этому получаем задержку перед тем, как web-сервер начнёт отправку вывода в браузер. Для простого PHP-скрипта это не существенно, но для более сложного приложения движок PHP может выполнить много действий прежде чем страница будет готова для отправки. Эти дополнительные действия приводят к заметной задержке между запросами пользователей и реальным отображением страниц в их браузерах.

Типичный web-сервер, например Apache, использует время модификации файла чтобы правильно сообщить web-браузеру состояние кэша запрашиваемой странички. Для динамических web-страниц, фактически PHP-скрипт может изменяться только изредка, в то время как отображаемый им контент, возможно располагающийся в базе данных, изменяется часто. Web-сервер не имеет возможности знать о наличии изменений в базе данных, тем не менее он не отправляет дату последней модификации. Если клиент (браузер) не получает никакого признака того, как долго данные являются корректными, он предполагает, что в следующий раз необходимо запросить страничку по новой. Web-сервер всегда будет отвечать обновлённой версией странички, независимо от того, изменились ли данные. Чтобы избежать этого недостатка большинство web-разработчиков используют мета-тэги или HTTP-заголовки, чтобы сообщить браузеру никогда не использовать кэшированную версию странички. Однако это отрицает естественную способность web-браузера кэшировать web-страницы и обладает некоторыми существенными недостатками. Например, содержание динамической странички может изменяться раз в сутки, поэтому выгода, получаемая от наличия даже 24-часового кэширования странички браузером, очевидна.

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

Обратите внимание, что в главе «Кэширование» обсуждаются только решения, осуществляемые при помощи PHP. Они не должны быть перепутаны с решениями кэширования скриптов, работающими на основе оптимизации и кэширования откомпилированных PHP-скриптов. В эту группу можно включить Zend Accelerator, ionCube PHP Accelerator, Turck MMCache/eaccelerator, APC.

Как я предотвращаю кэширование страницы браузерами?

Прежде чем мы рассмотрим методы клиентского и серверного кэширования, в первую очередь мы должны понять, как вообще предотвратить кэширование страниц web-браузером (и прокси-серверами). Основной способ достижения этого использует мета-тэги HTML:

«Mon, 26 Jul 1997 05:00:00 GMT» />

Вставив прошедшую дату в мета-тэг Expires, вы сообщаете браузеру, что кэшированная копия странички всегда является устаревшей. Это значит, что браузер никогда не должен кэшировать страницу. Мета-тэг Pragma: no-cache довольно хорошо поддерживаемое соглашение, которому следует большинство web-браузеров. Обнаружив этот тэг, они обычно не кэшируют страницу (хотя никаких гарантий нет, это всего лишь соглашение).

Это хорошо звучит, но есть две проблемы, связанные с использованием мета-тэгов:

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

Прокси-серверы, кэширующие web-страницы, как например общий ISP, вообще не будет исследовать непосредственно содержимое HTML-документа. Вместо этого они полагаются только на web-сервер, с которого пришли документы, и протокол HTTP. Иными словами, web-браузер может считать, что не должен кэшировать страницу, но прокси-сервер между браузером и вашим web-сервером вероятно не знает этого – и продолжит отправлять клиенту ту же самую, уже устаревшую, страницу.

Лучший подход состоит в том, чтобы использовать непосредственно протокол HTTP с помощью функции PHP header, эквивалентно приведённым выше двум мета-тэгам:

header () ; header («Pragma: no-cache» ) ;

Мы можем пойти на один шаг вперёд, воспользовавшись заголовком Cache-Control, совместимым с браузерами, поддерживающими HTTP 1.1:

header ( «Expires: Mon, 26 Jul 1997 05:00:00 GMT» ) ; header ( «Cache-Control: no-store, no-cache, must-revalidate» ) ; header ( «Cache-Control: post-check=0, pre-check=0» , FALSE ) ; header («Pragma: no-cache» ) ;

Это гарантирует, что никакой web-браузер или промежуточный прокси-сервер не будет кэшировать страницу, таким образом посетители всегда получат самую последнюю версию контента. Фактически, первый заголовок должен быть самодостаточным, это лучший способ гарантировать, что страница не кэшируется. Заголовки Cache-Control и Pragma добавлены с целью «подстраховаться». Хотя они не работают во всех браузерах или прокси, они отловят некоторые случаи, в которых Expires не работает должным образом (например, если дата на компьютере клиента установлена неправильно).

Конечно, полный отказ от кэширования обеспечивает нас проблемами, которые мы обсуждали в начале этой главы. Сейчас мы рассмотрим решение этих проблем. Internet Explorer и кэширование загрузки файлов

Наше обсуждение о PDF, приведённое в главе 3, Альтернативные типы контента, объяснило, что проблемы могут возникать, когда вы имеете дело с кэшированием и загрузкой файлов. Если при обслуживании загрузки файла PHP-скриптом используются такие заголовки, как например Content-Disposition: attachment, filename=myFile.pd f или Content-Disposition: inline, filename=myFile.pdf , у вас будут проблемы с Internet Explorer’ом, если вы сообщите браузеру не кэшировать страницу.

Internet Explorer оперирует загрузкой довольно необычным образом, выполняя два запроса к web-сайту. Первый запрос загружает файл и сохраняет его в кэше, пока не будет создан второй запрос (без сохранения отклика). Этот запрос вызывает процесс передачи файла конечному пользователю в соответствии с типом файла (например, запускает Acrobat Reader, если файл является PDF-документом). Это значит, что если вы отправили заголовки, запрещающие браузеру кэшировать страницу, Internet Explorer удалит файл между первым и вторым запросом, в результате чего конечный пользователь ничего не получит. Если файл, который вы отдаёте PHP-скриптом, не изменяется, одним из простейших решений будет убрать «запрещающие кэширование» заголовки из скрипта.

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

Как я могу захватить данные на стороне сервера для кэширования?

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

Несколько слов о кэшировании при помощи шаблонов

Шаблонные движки типа Smarty часто говорят о кэшировании шаблонов. Обычно эти движки предлагают встроенный механизм для сохранения откомпилированной версии шаблона (т.е. генерируют из шаблона PHP-исходник), что предохраняет нас от необходимости парсить шаблон каждый раз, когда запрашивается страница. Это не нужно путать с кэшированием вывода, которое имеет отношение к кэшированию предоставляемого HTML (или другого вывода), который посылает PHP в браузер. Вы можете успешно использовать оба типа кэширования одновременно на одном и том же сайте.

Сейчас мы рассмотрим встроенный механизм кэширования на PHP, использующий буферизацию вывода, который может использоваться вами независимо от способа создания контента (с шаблонами или без шаблонов). Рассмотрим ситуацию в которой ваш скрипт отображает результат использую, к примеру, echo или print, чтобы выдать данные непосредственно в браузер. В таком случае вы можете использовать функции управления выводом PHP для хранения данных в буферной памяти, над которой ваш PHP-скрипт имеет и доступ, и контроль.

Вот простой пример:

Ob_start () ; // Выводим некоторый текст (который сохраняется в буфере); echo «1. Выводим это в буфер
» ; // Останавливаем буферизацию и очищаем буфер вывода ob_end_clean () ; // Выводим некоторый текст обычным образом echo «2. Нормальный вывод
» ; // Вывод содержимого буфера echo $buffer ;

Сам буфер хранит вывод как строку. Так, в вышеприведённом скрипте мы начинаем буферизацию с ob_start и используем echo, чтобы вывести что-либо. Затем мы используем ob_get_contents, чтобы выбрать данные, помещённые в буфер оператором echo, и сохранить их в строке. Функция ob_end_clean останавливает буферизацию вывода и уничтожает его содержимое, как альтернативу можно использовать ob_end_flush, чтобы вывести содержимое буфера.

Вышеописанный скрипт выведет:

2. Нормальный вывод 1. Выводим это в буфер

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

Заголовки HTTP и буферизация вывода

Буферизация вывода может помочь решить наиболее общую проблему, связанную с функцией header, не говоря уже о session_start и set_cookie. Обычно, если вы вызываете любую из этих функций после того, как начался вывод страницы, вы получите противное сообщение об ошибке. При включенной буферизации вывода единственным типом вывода, избегающим буферизации, являются HTTP-заголовки. Используя ob_start в самом начале выполнения вашего приложения, вы можете посылать заголовки в любой понравившейся точке программы, не сталкиваясь с обычными ошибками. Затем, как только вы будете уверены, что больше выводить HTTP-заголовки не потребуется, вы можете сразу же вывести содержимое страницы из буфера. (прим. переводчика: следует заметить что подобное использование данной функции крайне неоправдано. В большинстве случаев необходимости в использовании буферизации вывода для избавления ошибок указанного типа просто не существует и всё с лёгкостью может быть исправлено правильным проектированием приложения)

Использование буферизации вывода для кэширования на стороне сервера

Вы уже видели базовый пример буферизации вывода, теперь следующий шаг, в котором буфер сохраняется в файл:

PHP и кеширование

Вступление

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

Сами кэши бываю двух видов — локальные и общие. Локальный это кеш, хранимый непосредственно на диске у клиента, создаваемый и управляемый его браузером. Общий — кэш прокси-сервера организации или провайдера и может состоять из одного или нескольких прокси-серверов. Локальный кеш присутствует, наверное в каждом браузере, общими пользуется значительная часть людей использующих Internet. И если малую часть сайтов сейчас оценивают по расходу трафика, то скорость загрузки — важный критерий, который должен учитываться при разработке Вашего web-проекта.
Для динамических страниц, создаваемых в результате работы PHP-программы, казалось бы, кэширование вредно. Содержание страницы формируются по запросу пользователя на основе какого-либо источника данных. Однако, кэширование может быть полезным. Управляя им Вы можете сделать работу с Вашим сервером комфортнее для пользователя, разрешая загрузку из кэш определенных страниц, предотвращая тем самым их повторную выгрузку с Вашего сервера и экономя пользователю время и трафик.

Кэшировать или нет?

Возможность сохранения в кэш страницы определяется динамичностью информации в источнике данных. Таким образом необходимость использования кэша определяется Вами, исходя из планируемого времени жизни страницы.

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

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

Общие принципы сохранения страниц в кэш

PHP-программа может управлять кэшированием результатов ее работы формируя дополнительные поля в заголовке HTTP ответа вызовом функции Header().
Несколько общих утверждений характерных не только для PHP-программ:

  • Страницы передаваемые по POST никогда не сохраняются в кэш.
  • Страницы запрашиваемые по GET и содержащие параметры (в URL присутствует ‘?’) не сохраняются в кэш, если не указано обратное.

Таким образом в большинстве ситуаций дополнительных инструкций в программу добавлять не надо. Основные моменты на которые следует обратить внимание можно свести к двум:

  • запрет кэширования документов, кэшируемых по умолчанию
  • кэширование документов, не подлежащих кэшированию по умолчанию.

Запрет кэширования документов, кэшируемых по умолчанию

Эта задача возникает для PHP-скриптов вызываемых без параметров или являющимися индексами директорий, однако формирующих данные персонально под пользователя (например на основе cookies или user agent) или работающих на основе быстро изменяющихся данных. По спецификации HTTP/1.1 мы можем управлять следующими полями:

Expires
Задает дату истечения срока годности документа. Задание ее в прошлом определяет запрет кэш для данной страницы.

Cache-control: no-cache
Управление кэш. Значение no-cache определяет запрет кэш данной страницы. Для версии протокола HTTP/1.0 действует «Pragma: no-cache».

Last-Modified
Дата послднего изменения содержимого. Поле актуально только для статических страниц. Apache заменяет это поле значением поля Date для динамически генерируемых страниц, в том числе для страниц содержащих SSI.

На сайте www.php.net дается следующий код для запрета кеширования.

header(«Expires: Mon, 26 Jul 1997 05:00:00 GMT»); // Date in the past
header(«Last-Modified: » . gmdate(«D, d M Y H:i:s») . » GMT»); // always modified
header(«Cache-Control: no-cache, must-revalidate»); // HTTP/1.1
header(«Pragma: no-cache»); // HTTP/1.0

Однако, данный заголовок избыточен. В большинстве случаев достаточно:

header(«Expires: Thu, 01 Jan 1970 00:00:01 GMT»);

Чтобы пометить документ как «уже устаревший» следует установить Expires равным полю Date .
header(«Expires: » . gmdate(«D, d M Y H:i:s») . » GMT»);

Ну и не следует забывать, что формы, запрошенные по POST также не подлежат кэшированию.

Кэширование документов, не подлежащих кэшированию по умолчанию

Обратная задача, может показаться на первый взгляд абсурдной. Однако и в этом существует потребность. Кроме простой минимизации трафика при разработке web-программы следует учитывать комфортность работы с ней пользователя. Например, некоторые страницы Вашего сервера формируются на основе статических данных большого объема. Возможность включения их в кэш существенно улучшит скорость работы сервера для пользователя и частично освободит Ваш от многочисленных повторных генераций такой страницы. Заголовок разрешающий сохранение на прокси-серверах:

header(«Cache-control: public»);
Если страница учитывает информацию сохраненную в браузере пользователя (тип и версию браузера, ключи, авторизацию и т. д.) такую страницу нельзя сохранить на прокси, однако возможно ее сохранение в локальном кэш браузера:

Кэширование до истечения корректности

Описанные выше решения довольно прямолинейны, хотя и подходят для большинства задач. Но протокол HTTP/1.1 имеет средства для более тонкого управления кэш страниц, и существуют задачи требующие применения этих механизмов. Как пример — веб-приложения, работающие с данными большого объема и прогнозируемой динамичностью. Корректность данных может устанавливаться как по дате прогнозируемого обновления, так и по изменению содержания. Для этих случаев используются разные заголовки управления кэшем.

Кэширование с прогнозируемым обновлением

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

$dt_tmp=getdate(date(«U»));
header(«Expires: » . gmdate(«D, d M Y H:i:s», date(«U»)-(86400*($dt_tmp[«wday»]-8))) . » GMT»);
header(«Cache-control: public»);

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

Другой подход, применяемый при более оперативном обновлении информации и одновременной высокой посещаемости сервера (иначе кэширование не будет эффективным) состоит в использовании заголовка Cache-control: max-age=секунды, определяющий время по истечении которого документ считается устаревшим и имеющий больший приоритет при вычислении «свежести» документа.

Если Вы публикуете новости с интервалом в 30 минут:

header(«Cache-control: public»);
header(«Cache-control: max-age=1800»);

Кэширование по содержанию

Еще более интеллектуальный вид управления предоставляет HTTP/1.1 на основе содержимого с помощью директив Vary. Я очень рекомендую применять его при формировании изображений или текстов большого объема, которые как показывает практика изменяются крайне редко. При этом у пользователя в случае возврата не будет происходить их повторной выгрузки, если содержание осталось прежним, и страница будет взята с Вашего сервера, если ее содержание изменилось.

Рассмотрим пример выдачи изображения из базы данных индентифицируемых по ID. Вызов страницы выглядит следующим образом:

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

mysql_connect(«host», «user», «passwd»);
$image=mysql(«db», «select pics,type from pictures where > Header(«Cache-Control: public, must-revalidate»);
Header(«Vary: Content-ID»);
Header(«Content-ID: «.md5(mysql_result($image, 0, «pics»)));
Header(«Content-type: «.mysql_result($image, 0, «type»));
echo mysql_result($image, 0, «pics»);
mysql_freeResult($image);
mysql_close();

Для управления используется MD5 сумма содержимого изображения. Пока содержание не изменилось, сумма будет постояной. В случае изменения содержания в базе на сервере клиент выполнит запрос для повторного формирования содержания. Пока изображение постоянно содержимое будет отображаться из кэш.

Примечания для Russian Apache

И приятное (или неприятное) сообщение для пользователей Russian Apache. Так как сервер выдает старину по пользовательской кодировке он автоматически снабжает все страницы (не только динамические) заголовками запрета кэширования.

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL