|
||||||||||||||
|
||||||||||||||
|
|
|
|
Обычно для маленьких PHP-приложений вполне можно игнорировать существование этих проблем, однако с увеличением сложности и повышением трафика Вашего сайта Вы можете столкнуться с проблемами. Тем не менее, обе эти проблемы могут быть решены, первая путём кэширования на стороне сервера, вторая путём управления кэшированием на стороне клиента из вашего приложения. Подход, который вы будете использовать для решения проблем, будет зависеть от вашей области применения, но в этой главе мы увидим, как вы можете решить обе проблемы используя PHP и некоторые классы библиотеки PEAR.
Обратите внимание, что в главе «Кэширование» обсуждаются только решения, осуществляемые при помощи PHP. Они не должны быть перепутаны с решениями кэширования скриптов, работающими на основе оптимизации и кэширования откомпилированных PHP-скриптов. В эту группу можно включить Zend Accelerator, ionCube PHP Accelerator и Turck MMCache, последний является единственным на сегодняшний день акселератором готовым для использования в PHP, установленном под Windows.
Прежде чем мы рассмотрим методы клиентского и серверного кэширования, в первую очередь мы должны понять, как вообще предотвратить кэширование страниц web-браузером (и прокси-серверами). Основной способ достижения этого использует мета-тэги HTML:
<meta http-equiv="Expires" content="Mon, 26 Jul 1997 05:00:00 GMT" /> |
Вставив прошедшую дату в мета-тэг Expires, вы сообщаете браузеру, что кэшированная копия странички всегда является устаревшей. Это значит, что браузер никогда не должен кэшировать страницу. Мета-тэг Pragma: no-cache довольно хорошо поддерживаемое соглашение, которому следует большинство web-браузеров. Обнаружив этот тэг, они обычно не кэшируют страницу (хотя никаких гарантий нет, это всего лишь соглашение).
Это хорошо звучит, но есть две проблемы, связанные с использованием мета-тэгов:
Лучший подход состоит в том, чтобы использовать непосредственно протокол HTTP с помощью функции PHP header, эквивалентно приведённым выше двум мета-тэгам:
<?php |
Мы можем пойти на один шаг вперёд, воспользовавшись заголовком Cache-Control совместимым с браузерами, поддерживающими HTTP 1.1:
<?php |
Это гарантирует, что никакой web-браузер или промежуточный прокси-сервер не будет кэшировать страницу, таким образом посетители всегда получат самую последнюю версию контента. Фактически, первый заголовок должен быть самодостаточным, это лучший способ гарантировать, что страница не кэшируется. Заголовки Cache-Control и Pragma добавлены с целью «подстраховаться». Хотя они не работают во всех браузерах или прокси, они отловят некоторые случаи, в которых Expires не работает должным образом (например, если дата на компьютере клиента установлена неправильно).
Конечно, полный отказ от кэширования обеспечивает нас проблемами, которые мы обсуждали в начале этой главы. Сейчас мы рассмотрим решение этих проблем.
Как я могу захватить данные на стороне сервера для кэширования?
Пришло время взглянуть на то, как мы можем уменьшить задержку при помощи кэширования вывода на стороне сервера. Общий подход начинает предоставлять страницу как обычно, выполняя запросы к базе данных и так далее на PHP. Тем не менее, перед отправкой результата в браузер, мы захватываем его и сохраняем готовую страницу, например, в файле. При следующем запросе, PHP-скрипт сначала проверяет наличие кэшированной версии страницы. Если она существует, скрипт отправляет в браузер версию из кэша, исключая таким образом задержку на повторное создание страницы.
Пример 5.1. 1.php<?php |
Сам буфер хранит вывод как строку. Так, в вышеприведённом скрипте мы начинаем буферизацию с ob_start и используем echo, чтобы вывести что-либо. Затем мы используем ob_get_contents, чтобы выбрать данные, помещённые в буфер оператором echo, и сохранить их в строке. Функция ob_end_clean останавливает буферизацию вывода и уничтожает его содержимое, как альтернативу можно использовать ob_end_flush, чтобы вывести содержимое буфера.
Вышеописанный скрипт выведет:
2. Нормальный вывод 1. Выводим это в буфер |
Другими словами, мы захватили вывод первого echo, затем послали его браузеру после второго echo. Как видно из этого простого примера, буферизация вывода является очень мощным инструментом для формирования вашего сайта, она обеспечивает решение для кэширования, как мы скоро увидим, и является отличным способом скрыть ошибки от посетителей вашего сайта (смотрите главу 10, Обработка ошибок). Она также обеспечивает альтернативную возможность для переадресации браузера в ситуациях типа аутентификации пользователя.
Пример 5.2. 2.php<?php <?php |
Сначала вышеописанный скрипт проверяет наличие существования версии странички в кэше, и, если она имеется, скрипт читает и выводит её. В противном случае, он использует буферизацию вывода для создания версии страницы в кэше. Она сохраняется как файл, после использования ob_end_flush для отображения страницы пользователю.
Файл 2.cache содержит точную копию HTML, которую предоставляет скрипт: |
Пример 5.3. 3.php (начало)<?php function writeCache($content, $filename) {$fp = fopen('./cache/' . $filename, 'w'); fwrite($fp, $content); fclose($fp); } / * Проверка кэш-файлов * @param string filename – имя проверяемого кэш-файла * @param int expiry – максимальный «возраст» файла в секундах * @return mixed содержимое кэша или false */ function readCache($filename, $expiry) { if (file_exists('./cache/' . $filename)) { if ((time() - $expiry)>filemtime('./cache/' . $filename)) return FALSE; $cache = file('./cache/' . $filename); return implode('', $cache); } return FALSE; } ?> |
Первые две определённые нами функции, writeCache и readCache, используются соответственно для создания кэш-файлов и проверки их существования. Функция writeCache получает данные для кэширования в первом аргументе, и имя файла, используемое при создании кэш-файла. Функция readCache получает имя кэш-файла в первом параметре, вместе со временем в секундах, после которого кэш-файл должен считаться устаревшим. Если она сочтёт кэш-файл допустимым, скрипт вернёт его содержимое, в противном случае он вернёт FALSE, чтобы показать, что-либо кэш-файла не существует, либо он устарел.
В этом примере я использовал процедурный подход. Однако я не советую делать это на практике, поскольку это закончится очень грязным кодом (смотри последующие решения с лучшей альтернативой) и, вероятно, вызовет проблемы с блокировкой файла (например, что случится, когда кто-то обращается к кэшу в момент его обновления?).
Давайте продолжим этот пример. После того, как запущена буферизация вывода, начинается обработка. Сначала скрипт вызывает readCache, чтобы узнать, существует ли файл 3_header.cache, он содержит шапку страницы – заголовок HTML и начало тела. Мы используем функцию date PHP чтобы вывести время, когда страница фактически была сгенерирована, таким образом вы увидите различные кэш-файлы в работе, когда страница будет отображена.
Пример 5.4. 3.php (продолжение)<?php Время создания шапки: <?php echo date('H:i:s'); ?> <br /><?php |
Что же случается когда кэш-файл не найден? Выводится некоторый контент и присваивается переменной при помощи ob_get_contents, после чего буфер очищается функцией ob_clean. Это позволяет нам перехватывать вывод по частям и сопоставлять их с индивидуальными кэш-файлами при помощи writeCache. Заголовок страницы теперь хранится как файл, который может быть использован без нашего вмешательства в пересборку страницы. Давайте вернёмся на секунду к началу условного оператора. Когда мы вызывали readCache, мы передали ей время жизни кэша в 604800 секунд (одна неделя), readCache использует время модификации кэш-файла, чтобы определить, является ли кэш-файл всё ещё допустимым.
Для содержимого (тела) страницы мы по прежнему будем использовать тот же процесс. Однако на сей раз при вызове readCache мы будем использовать время жизни кэша в пять секунд, кэш-файл будет модифицироваться каждый раз, когда он «старше» 5 секунд:
Пример 5.5. 3.php (продолжение)<?php |
Нижний колонтитул эффективно изменять так же, как заголовок. После этого буферизация вывода останавливается и отображается содержимое трёх переменных, содержащих данные страницы:
Пример 5.6. 3.php (окончание)<?php Время создания нижнего колонтитула: <?php echo date('H:i:s'); ?> <br /></> </html> <?php |
Конечный результат выглядит примерно так:
Время создания шапки: 17:10:42 Время создания тела: 18:07:40 Время создания нижнего колонтитула: 17:10:42 |
Заголовок и нижний колонтитул обновляются еженедельно, в время как тело модифицируется, когда оно старее 5 секунд.
Рисунок 5.1. Блок-схема блочной буферизации выводаКак мне реализовать простую систему кэширования на стороне сервера?
Теперь, когда мы понимаем идеи буферизации вывода, пришло время рассмотреть, как мы можем использовать этот процесс в действии таким образом, чтобы его было легко поддерживать. Чтобы сделать это, мы воспользуемся небольшой помощью от PEAR::CacheLite (версия 1.1 использовалась в приведённых здесь примерах).
Как я уже говорил, в интересах удобства последующей поддержки кода и получения надежного кеширующего механизма, разумно будет возложить ответственность за кеш-логику на те классы, которым вы доверяете. Cache_Lite представляет собой мощную, но простую в использовании, библиотеку для кеширования, которая берет на себя такие задачи, как временная блокировка кеш-файлов, их создание и проверка, управление буфером вывода и непосредственное кеширование результа работы функции или метода класса. Основной же причиной выбора этой библиотеки является относительно простая интеграция Cache_Lite в уже существующее приложение, которая требует лишь незначительных изменений в коде.
Cache_Lite состоит из трех основных классов. Первым является базовый класс Cache_Lite, который отвечает только за создание и чтение кеш-файлов и не занимается буферизацией вывода. Данный класс можно использовать в одиночку в тех случаях, когда нет необходиомсти использовать буферизацию вывода, как например при сохранении результата разбора шаблона PHP скриптом. Приведенные здесь примеры не используют класс Cache_Lite напрямую и демонстрируют применение остальных двух классов. Cache_Lite_Function используется для вызова функции или метода класса и последующего кеширования результатов работы. Это может оказаться полезным, например для кеширования результата запроса к MySQL. Класс Cache_Lite_Output использует PHP функции контроля за выводом для перехвата данных, сгенерированных скриптом, и сохранения их в кеш-файлах. Это позволяет выполнять те же задачи, что и предыдущее решение.
Вот пример, который покажет вам, как вы могли бы использовать Cache_Lite, чтобы выполнить задачу, которую мы рассмотрели в предыдущей части. Чтобы рассмотреть любое применение Cache_Lite, мы должны его сначала настроить - создать массив параметров - который определит поведение класса. Ниже мы рассмотрим их подробнее, а пока обратите внимание на то, что ваш скрипт должен иметь права на чтение и запись в каталог cahceDir.
**Пример 5.7. ##4.php## (начало)** <?php // Подключаем класс вывода PEAR::Cache_Lite require_once 'Cache/Lite/Output.php'; // Определяем настройки для Cache_Lite $options = array( 'cacheDir' => './cache/', 'writeControl' => 'true', 'readControl' => 'true', 'readControlType' => 'md5' ); // Создаем объект класса Cache_Lite_Output $cache = new Cache_Lite_Output($options); ?> |
Для каждой части вывода вашего скрипта, которую вы хотите кэшировать, необходимо установить время жизни кэша в секундах. это время определяет, как долго нужно брать данные из кэше. По истечении этого времени, данные в файле будут обновлены. Далее, мы вызываем метод start(), доступный только в классе Cahce_Lite_Output, который включает буферизацию вывода. В метод мы передаем 2 параметра: первый - идентификатор файла с кэшем, второй - группа (тип кэша). Параметр "группа" позволяет объединять несколько шаблонов, это позволяет производить групповые действия, например, удалить все файлы кэша в группе. Как только вывод нужной нам части закончен, мы должны вызвать метод stop(). Этот метод остановит буферизацию и сохранит содержимое буфера в файл.
Кэширование основного тела вывода (body) и нижней части (footer) происходит аналогично кэшированию заголовка. Обратите внимание, мы снова выставляем время жизни кэша, для каждой следующей части.
**Пример 5.9. ##4.php## (продолжение)** <?php $cache->setLifeTime(5); if (!$cache->start('body', 'Dynamic')) { echo 'Время создания тела: ' . date('H:i:s') . ' |
Когда вы вызовете для просмотра эту страницу, Cache_Lite создаст в каталоге для кэширования следующие файлы:
./cache/cache_Static_header ./cache/cache_Dynamic_body ./cache/cache_Static_footer |
Если запросить этуже страницу позже, код, приведенный выше, покажет вам содержимое этих файлов, конечно в том случае, если время их жизни еще не истекло.
Защищайте файлы с кэшем.
Удостоверьтесь, что каталог, в котором вы храните файлы кэша, не является публично доступным. иначе посетители вашего сайта смогут сделать большее, чем вы хотели бы им позволить.
**Пример 5.10. ##4.php## (окончание)** <?php // Задаем настройки для Cache_Lite $options = array( 'cacheDir' => './cache/', 'writeControl' => TRUE, 'readControl' => TRUE, 'readControlType' => 'md5' ); // Создаем объект Cache_Lite_Output $cache = new Cache_Lite_Output($options); ?> |
В текущем версии класса (1.1) доступны следующие настройки:
cacheDir - Это каталог, в которрый будут помещаться файлы кэша. Значение по умолчанию - каталог, где выполняется скрипт.
caching - эта опция включает или выключает возможности Cache_Lite. Например, если у вас очень много запросов к Cache_Lite, а в процессе отладки вы захотите выключить кэширование, установить в FALSE. Значение по умолчанию - TRUE.
lifetime - Этот параметр содержит в себе заданный по умолчанию отрезок времени жизни кэша (в секундах). Изменить значение можно вызвав метод setLifeTime(). Значение по умолчанию 3600 (один час).
fileNameProtection - Если данная опция включена, Chache_Lite будет использовать MD5 кодирование для генерации имени файла с кэшем. Это позволяет вам использовать в названии файлов кэша и групп любые символы, даже запрещенные файловой системой. Этот параметр должен быть включен, когда вы используете Cache_Lite_Function. Значение по умолчанию - TRUE (включено).
fileLocking - Этот параметр включает механизмы блокирования файла с кэшем, на время записи в него данных. Значение по умолчанию - TRUE (включено).
writeControl - Проверяет, что файл кэша был записан правильно сразу после окончания записи. В случае ошибки бросает PEAR:Error. Эта возможность позволяет вашему скрипту перезаписать файл кэша еще раз, но замедляет его работу. Значение по умолчанию TRUE (включено).
readControl - Перед чтением файла с кэшем проверяет его на искажения. Cache_Lite размещает в файле значение длинны файла, которое можно использовать для контроля его целостности. Также имеется альтернативный механизм проверки целостности файла. Он включается параметром readControlType. Эти механизмы несколько замедляют скорость, но помогают гарантировать, что ваши пользователи увидят неиспорченную страницу. значение по умолчанию TRUE (включено).
readControlType - Этот параметр определяет тип механизма чтения файлов кэша. Доступные механизмы: цикличная проверка избыточности ('crc32' - значение по умолчанию) - использует функцию crc32PHP, 'MD5' хэш - используется функция md5PHP, или простую проверку длинны - 'strlen'. Обратите внимание, что этот механизм не предназначен для защиты файлов кэша от вызова их напрямую посторонними пользователями. Это всего лишь способ определить - испорчен файл или нет.
pearErrorMode - включает принятый в PEAR способ возврата ошибок. Значение по умолчанию CACHE_LITE_PEAR_RETURN - возвращает объект /#c#?PEAR::Error.
memoryCaching - Если данный параметр включен, каждый раз, как вы вызываете запись кэша в файл, он записывается в массив Cache_Lite. saveMemoryCachingState и getMemoryCachingState используются для доступа к кэшу, сохраненному в памяти между запросами. Преимущество подобного метода состоит в том, что содержимое кэша может быть сохранено в едином файле, сокращая число циклов чтения/записи на диск. Кэш восстанавливается прямо в массив, к которому ваш скрипт имеет доступ. В наших примерах мы будем использовать обычный механизм Cache_Lite, но вам стоит поэкспериментировать с этим параметром в дальнейшем, если у вас очень большой сайт. По умолчанию TRUE (выключено).
onlyMemoryCaching - Если вы включили этот параметр - будет использоваться только механизм кэширования в памяти. По умолчанию TRUE (выключено).
memoryCachingLimit - Этот парметр определяет предел количества файлов кэша, которые могут быть сохранены в массиве в памяти. чем больше число файлов - тем больше памяти будет расходоваться. Поэтому определить ограничение - это очень хорошая идея. Конечно это не влияет на размер файла кэша, поскольку один или два массивных файла не создадут никаких проблем. значение по умолчанию - 1000.
$cache->remove('body', 'Dynamic'); |
Метод clean() позволяет удалить все файлы в нашем каталоге, если вызвать его без параметров. Если в качестве параметра передать название группы, то будут удалены все файлы, входящие в данную группу. Если бы мы хотели удалить заголовок и нижнюю часть, то нужно подать команты аналогичные этим:
$cache->clean('Static'); |
Методы remove() и clean() нужно, очевидно, вызывать в ответ на события, в пределах приложения. Например, у вас есть форум, наверняка следует удалить файл кэша, если какой-либо пользователь отправит новое сообщение. Несмотря на то, что это решение выглядит красиво, оно может повлечь за собой изменение кода. Если у вас есть главный скрипт, который подключается к каждой странице приложения, которую может просмотреть посетитель, вы можете просто наблюдать за поступающими событиями, например, за переменной $_GET['newPost'], удаляя требуемые файлы кэша. Это позволит вам создать ентрализованный механизм управления кэшем. вы могли бы даже включить этот код в php.ini (см. описание настройки auto_prepend_file).
$countries = $stationInfo->listCountries(); |
$country = $stationInfo->searchByCountry($_GET['country']); |
В обоих случаях, эти вызовы соответствуют запросу данных по сети. Используя Cache_Lite, мы могли бы кэшировать данные, возвращаемые сервисом, и могли бы использовать их многократно. Это позволило бы избежать дополнительных ненужных сетевых соединений, и значительно увеличило бы скорость выполнения. Обратите внимание, что сдесь мы рассматриваем только код, касающийся нашей темы. В начале мы подключаем Cache_Lite_Function:
**Пример 5.11. ##5.php## (начало)** // Include PEAR::Cache_Lite_Function require_once 'Cache/Lite/Function.php'; |
Далее мы инициализируем сласс Cache_Lite_Function с определенными параметрами:
**Пример 5.12. ##5.php## (продолжение)** // Задаем параметры для for Cache_Lite_Function // ВНИМАНИЕ: fileNameProtection = TRUE! $options = array( 'cacheDir' => './cache/', 'fileNameProtection' => TRUE, 'writeControl' => TRUE, 'readControl' => TRUE, 'readControlType' => 'strlen', 'defaultGroup' => 'SOAP' ); // Создаем объект класса Cache_Lite_Function $cache = new Cache_Lite_Function($options); |
Важно, что параметр fileNameProtection установлен в TRUE. Это хначение принято по умолчанию, однако я специально выставил его вручную, чтобы подчеркнуть значимость. Если этот параметр установить в FALSE, получится недопустимое имя файла, так что кэширования не будет.
Далее, мы делаем запрос к нашему клиенту SOAP:
**Пример 5.13. ##5.php## (продолжение)** $countries = $cache->call('stationInfo->listCountries'); |
И:
**Пример 5.14. ##5.php## (окончание)** $country = $cache->call('stationInfo->searchByCountry', $_GET['country']); |
Если запрос делается впервые, Cache_Lite_Function хранит результаты в виде сериализованного массива в файле кэша (вы не должны об этом волноваться), и этот файл будет использоваться для будущих запросов, пока не истечет время его жизни. setLifeTime может использоваться, чтобы определить как долго будет жить файл кэша, перед обновлением информации в нем. Занчение по умолчанию - 1 час (3600 секунд).
Вообще, Cach_Lite обеспечивает единую, простую в использовании систему, чтобы решить любые вопросы, связанные с кэшированием. Поскольку следующий уровень - это сайты с особенно большим трафиком, вам стоит исследовать PEAR::Cache, это большой брат Cache_Lite. Он также предусматривает расширение возможностей кэширования, например, кэширование в общей памяти, как альтернативу кэширование в файл, или помощь Msession PHP extension, храня данные в сбалансированной сессии, которая является особенно полезной в сбалансированных WEB серверах. Дргие материалы о PEAR::Cache я приведу в конце этой статьи. Cache_Lite, однако, предлагает более чем достаточные возможности, и отвечает потребностям большинства сайтов.
После того, как мы рассмотрели варианты отмены кэширования на стороне клиента, и каким образом можно организовать кэширование на стороне клиента, пришло время посмотреть на механизм, который позволит нам контролировать кеш на стороне клиента средствами 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 изначально добавлялись для того, чтобы позволить кэшировать такие страницы.
**Пример 5.15. ##6.php##** <?php /** * Посылает заголовок Expires HTTP 1.0. * @param int количество секунд до времени истечения срока жизни */ function setExpires($expires){ header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $expires) . 'GMT'); } // Устанавливаем заголовок времени истечения срока жизни Expires setExpires(10); // Отображаем echo 'Эта страница самоуничтожится через 10 секунд<br />'; echo 'Сейчас ' . gmdate('H:i:s') . ' GMT<br />'; echo '<a href="' . $_SERVER['PHP_SELF'] . '">Посмотреть вновь </a><br />'; ?> |
Функция setExpires отправляет заголовок HTTPExpires с будущим временем, заданном в секундах. Вышеприведённый пример показывает текущее время по Гринвичу и выводит ссылку, которая вам позволяет перейти на страницу вновь. Используя кнопку Refresh вашего браузера, вы можете сообщить браузеру о желании обновить кэш. Используя ссылку, вы увидите, что время изменяется только раз в 10 секунд.
Даты и время в HTTP
Даты в HTTP всегда вычисляются относительного меридиана времени Гринвича (GMT). Функция PHP gmdate точно такая же функция, как date, за исключением того, что она автоматически компенсирует время по Гринвичу, основанное на системных часах и настройках региона вашего сервера.
Когда браузер сталкивается с заголовком Expires, он кэширует страницу. Все последующие запросы страницы, сделанные до указанного времени истечения срока жизни, используют версию страницы из кэша, никаких запросов к web-серверу при этом не происходит.
Заголовок Expires преимущественно прост в реализации, но в большинстве случаев, если вы не высокоорганизованный человек, вы не можете знать точно, когда данная страница вашего сайта обновлена. Поскольку браузер войдёт в контакт с сервером только после того, как страница устареет, нет ни одного способа сообщить браузеру, что страница, находящаяся в его кэше, устарела. Вы также теряете некоторую часть трафика к вашему web-сайту, поскольку браузер не обращается к серверу при запросе страницы из кэша.
**Пример 5.16. ##7.php## (начало)** <?php // Подключаем PEAR::Cache_Lite require_once 'Cache/Lite.php'; // Определяем настройки Cache_Lite $options = array( 'cacheDir' => './cache/' ); // Инициализируем Cache_Lite $cache = new Cache_Lite($options); // Некоторые фиктивные данные для хранения $id = 'MyCache'; // Инициализируем кэш, если страница запрошена впервые if (!$cache->get($id)) { $cache->save('Dummy', $id); } // Рандомизатор… $random = array(0, 1, 1); shuffle($random); // Произвольное обновление кэша if ($random[0] == 0) { $cache->save('Dummy', $id); } // Получаем время последней модификации кэш-файла $lastModified = filemtime($cache->_file); // Выдаём заголовок HTTP Last-Modified header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT'); // Получаем заголовки запроса клиента – только для Apache $request = getallheaders(); if (isset($request['If-Modified-Since'])) { // Разделяем If-Modified-Since (Netscape <= $modifiedSince) { // Разгружаем канал передачи данных! header('HTTP/1.1 304 Not Modified'); exit(); } echo 'Сейчас ' . gmdate('H:i:s') . ' по Гринвичу<br />'; echo '<a href="' . $_SERVER['PHP_SELF'] . '">Обновить</a><br />'; ?> |
Не забудьте пользоваться ссылкой «Обновить» при запуске этого примера (нажатие Refresh’а обычно очищает кэш вашего браузера). Если вы кликните по ссылке неоднократно, в конечном итоге кэш будет изменён, ваш браузер удалит версию из кэша и сохранит в нём новую страницу, предоставленную PHP.
В вышеприведённом примере мы использовали PEAR::Cache_Lite для создания произвольно модифицируемого кэш-файла. Мы устанавливаем время модификации кэш-файла этой строкой:
<?php $lastModified = filemtime($cache->_file); ?> |
Говоря техническим языком, это хак, поскольку переменная $_file класса PEAR::Cache_Lite должна быть приватной. Тем не менее мы вынуждены её использовать, чтобы получить имя кэш-файла и узнать его время модификации.
Затем, используя время модификации кэш-файла, мы посылаем заголовок Last-Modified. Нам нужно посылать её для каждой предоставляемой страницы, чтобы вынудить браузер посылать нам заголовок If-Modified-Since с каждым запросом.
<?php // Выдаём заголовок HTTP Last-Modified header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT'); ?> |
Использование функции getallheaders обеспечивает нам получение от PHP всех входящих заголовков в виде массива. Затем мы должны проверить, что заголовок If-Modified-Since действительно существует, если он есть, мы должны обработать специальный случай старых версий Mozilla (нижа 6й версии), который добавлял в конец (отклоняясь от спецификации) дополнительное поле к заголовку If-Modified-Since. Используя функцию PHP strtotime, мы получаем таймштамп даты, переданной нам браузером. Если такого заголовка нет, мы присваиваем таймштампу ноль, вынуждая таким образом PHP отдать посетителю последнюю версию страницы.
<?php // Получаем заголовки запроса клиента – только для Apache $request = getallheaders(); if (isset($request['If-Modified-Since'])) { // Разделяем If-Modified-Since (Netscape |
Наконец, мы проверяем, был ли модифицирован кэш с тех пор как посетитель получал эту страницу в последний раз. Если это не так, мы просто посылаем в заголовке отклик Not Modified и прекращаем выполнение скрипта, не нагружая канал передачи данных и экономя процессорное время, инструктируя браузер отобразить кэшированную версию страницы.
<?php // Сравниваем время последней модификации контента с кэшем клиента if ($lastModified <= $modifiedSince) { // Разгружаем канал передачи данных! header('HTTP/1.1 304 Not Modified'); exit(); } ?> |
Если вы объедините подход времени последнего изменения со значением времени, являющимся уже доступным в вашем приложении (например, время самой последней новостной статьи, или время устаревания из системы серверного кэширования, которое мы видели в последнем решении), вы сможете воспользоваться преимуществами кэша web-браузера и разгрузите канал передачи данных, по возможности сэкономив информационный трафик с вашего сайта и улучшив его воспринятую производительность.
Будьте осторожны при тестировании любого кэширования, выполненного в таком стиле, если вы сделаете это неправильно, вы можете заставить ваших посетителей всегда иметь устаревшие копии вашего сайта.
seo & website usability | inet | os faq | hardware faq | memory | video | cpu | hdd | mainboard faq | printer & scaner | modem | mobiles | hackzone |
Windows 10 | Registry Windows 10 | Windows7: Общие настройки | Windows7: Реестр | Windows7: Реестр faq | Windows7: Настроки сети | Windows7: Безопасность | Windows7: Брандмауэр | Windows7: Режим совместимости | Windows7: Пароль администратора | | | | Память | SDRAM | DDR2 | DDR3 | Quad Band Memory (QBM) | SRAM | FeRAM | Словарь терминов | Video | nVIDIA faq | ATI faq | Интегрированное видео faq | TV tuners faq | Терминология | Форматы графических файлов | Работа с цифровым видео(faq) | Кодеки faq | DVD faq | DigitalVideo faq | Video faq (Архив) | CPU | HDD & Flash faq | Как уберечь винчестер | HDD faq | Cable faq | SCSI адаптеры & faq | SSD | Mainboard faq | Printer & Scaner | Благотворительность
|
На главную | Cookie policy | Sitemap