Кеширование обычными средствами

    Сегодня решил сделать то, что собирался уже давно — поставить таки кеширование скриптов у себя на сайте. Все скрипты перед отдачей пользователю собираются в один файл и сжимаются GZIP'ом — все вроде по-уму, но есть проблемка… Браузер отчаянно не хотел кешировать этот выходной скрипт.

    Опытным путем было установлено, что PHP устанавливает хидеры запрета кеширования при использовании функции session_start();

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

    1. session_start(); // где-то вверху начинается сессия
    2.  
    3.  
    4. function cmsCache_control($file, $time) {
    5.   
    6.   $etag = md5_file($file);
    7.   $expr = 60 * 60 * 24 * 7;
    8.   
    9.   header("ETAG: " . $etag);
    10.   header("LAST-MODIFIED: " . gmdate("D, d M Y H:i:s", $time) . " GMT");
    11.   header("CACHE-CONTROL: ");
    12.   header("PRAGMA: ");
    13.   header("EXPIRES: ");
    14.   
    15.   if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"])) {
    16.     
    17.     $if_modified_since = preg_replace("/;.*$/", "", $_SERVER["HTTP_IF_MODIFIED_SINCE"]);
    18.     
    19.     if(trim($_SERVER["HTTP_IF_NONE_MATCH"]) == $etag && $if_modified_since == gmdate("D, d M Y H:i:s", $time). " GMT") {
    20.       
    21.       header("HTTP/1.0 304 Not modified");
    22.       header("CACHE-CONTROL: MAX-AGE={$expr}, MUST-REVALIDATE");
    23.       exit;
    24.       
    25.     }
    26.     
    27.   }
    28.   
    29. }
    30.  
    31.  
    32. cmsCache_control($_SERVER[SCRIPT_FILENAME], filemtime($_SERVER[SCRIPT_FILENAME]));
    33. //дальше обычный вывод того, что кешируем
    * This source code was highlighted with Source Code Highlighter.


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

    Единственное дополнение — вместо filemtime() можно также использовать другую произвольную дату, в своем скрипте я использовал дату изменения самого свежего файла.

    P.S. Приведенный фрагмент мною был модифицирован, чтобы учитывать сессии, но сам код взят отсюда: http://ru2.php.net/manual/ru/function.header.php#85146, а также http://foxweb.net.ru/texts/43.htm (комментарии товарища antishock).
    Поделиться публикацией

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 41

      –1
      А зачем одновременно ETAG и LAST-MODIFIED?
        +3
        Для однозначной идентификации файла. Береженого бог бережет ;)

        «You can use HTTP's etags and last modified dates to ensure that you're not sending the browser data it already has cached»
        +9
        обьясните мне пожалуйста, почему все говорят «хидер»? ведь header он читается «хедер» или «хэдер», но никак не «хидер». извините за занудство, просто сто раз слышал, ухо режет
          +1
          Да как-то устоялось уже так… Правильное произношение —
            +2
            А тут можно послушать www.google.com/dictionary?langpair=en|ru&q=header
              0
              Ну так как я везде учил только немецкий, то я говорю хеадер и все понимают, так что вообще не вижу проблемы.
                –1
                чудесная логика. если бы вы учили только, скажем, иврит, вы бы, наверное, говорили «рдх» и тоже ожидали бы, что вас все поймут ;-)

                «хидеры» и «хеадеры» — это, в общем, показательно: если человек не может запомнить (или даже принципиально не хочет запоминать) элементарные правила произношения слов, которые он волей-неволей должен часто употреблять, требовать от него глубокого знания, допустим, programming patterns (которые тоже хорошо бы применять в повседневной жизни) довольно наивно.
                  0
                  Логика отличная — не заморачиваться по пустякам.
                    0
                    программирование — это внимание к мелочам. на эту тему можно почитать Дейкстру, например. главное — не перепутайте с «Дийсктрой» и «Дайкстрой» ;-)
                      +1
                      Уважаемый, Вы придираетесь. У нас страна никак не может определиться, надо ли писать «веб» или «вэб». Программирование — это внимание к мелочам, да, а не пустые разговоры «на тему».
                        0
                        да определилась страна: посмотрите рез-ты в Яндексе — 102 млн к 6 ;-)
                          0
                          А слово «результаты» целиком написать религия не позволяет? Ко всякой фигне придираетесь, а слово не написать как следует… ;)

                          Это конечно стёб, думаю тему написания слова «header» можно закрыть.
                        0
                        То есть ты считаешь, что на качество программирования влияет то как ты произносишь слово header?
                          +1
                          я считаю, что на качество программирования влияет: а) способность признавать свои ошибки, а не защищать их с пеной у рта б) стремление в общем случае делать так, как правильно, а не так, как «хочется».
                            0
                            А повыше почитать не судьба? Я уже и на яндекс слазал, и в гугл, и транскрипцию написал, а также послушал как произносится, что еще нужно сделать? ;)

                            Сдается мне, что высот в программировании Вы еще не добились, раз размениваетесь на такие мелочи. Лучше бы по существу что-нибудь написали, честное слово, вместо того, чтоб «эфир» засорять.
                              0
                              так я не вам и пишу, что за эгоцентризм? ;-)
                                0
                                по существу — окей, раз просите.

                                при каждом запросе обсчитывать md5-хэш файла — дорогое удовольствие; заголовок (давайте будем так, чтобы без холиваров) «cache-control» в случае, если кэш срабатывает, выдается дважды — зачем?

                                в целом решение нормальное такое. но для кэширования статики (особенно раз она у вас генерируется автоматически) обычно просто используют большое значение Expires, а ссылка на файл автоматически переименовывается при каждом изменении (с подстановкой того же filemtime(), при этом через mod_rewrite делаете так, чтобы ссылки вида /css/index.1223427651.css и /css/index.1223425897.css вели на один и тот же файл). смысл — экономите пользователю запрос на сервер. а ваше решение подходит больше для кэширования собственно HTML-страниц, которые время от времени могут поменяться. на жестко динамические страницы такое кэширование ставить смысла вообще нет.
                                  0
                                  В первый раз «CACHE-CONTROL» сбрасывается, это отменяет то, что выдается после вызова session_start(); второй заголовок отменяет первый, потому что

                                  «The optional replace parameter indicates whether the header should replace a previous similar header, or add a second header of the same type. By default it will replace, but if you pass in FALSE as the second argument you can force multiple headers of the same type.» © ru2.php.net/header в описании параметров.

                                  Насчет таких суровых ссылок — ну не знаю, мне такой вариант не очень нравится, хотя он и вполне жизнеспособен.

                                  Для чисто динамических страниц применять кеширование в таком виде — это надо конечно додуматься :)
                                0
                                Угу замечательно, только это относится ко всем профессиям и вообще не ноносится к произношению слова хеадер.
                                  0
                                  ой, как ваш комментарий подтверждает оба моих пункта ;-)
                                    0
                                    Ваш диагноз пользователями хабра поставлен. Ответы невпопад — видимо главная фишка вашей конторы, если конечно вас куда-то взяли работать.
                        0
                        Немцы, кстати, произносят английские названия не обнемечивая, т.е. «сервер» произносится как «сёрвер», или точнее, как «server» и т.п.
                        0
                        ай хороший человек, как понимаю!
                        +2
                        Поправьте, пожалуйста, тэги. Нужно через запятую.
                          0
                          ой-ой… my bad, исправил
                          0
                          Пустые хедеры крешат некоторые нерадивые прокси сервера :)
                          haproxy, например, вылетал. Вроде пофиксили.
                            0
                            Тут есть нюансы, которые тоже следует учесть на всякий случай:

                            — Если PHP через CGI будет работать а не через модуль Apache, HTTP_IF_MODIFIED_SINCE там не будет.
                            — header(«LAST-MODIFIED: ». gmdate(«D, d M Y H:i:s», $time). " GMT"); Не очень хорошо подсовывать GMT жёстко, многие серваки с UTC работают, ошибко имхо.

                            Да и стоит подумать о HTTP/1.1, актуально всёж.
                            Но это только советы, если универсальность нужна)
                            • НЛО прилетело и опубликовало эту надпись здесь
                                0
                                Привычка по аналогии с серверными переменными аля HTTP_IF_MODIFIED_SINCE или DOCUMENT_ROOT в таком же стиле писать заголовки…

                                Кстати в данной статье это оправдано — внимание привлекают.
                                0
                                у меня обычно обратная проблема — при изменении скрипта часто приходится подгружать его браузером напрямую, так как «внедрённый» он грузится из кэша, сколько ты ему не жми refresh
                                  0
                                  Тут попроще :) Ставьте заголовки:

                                  header("Expires: Tue, 1 Jan 2000 00:00:00 GM"); // любая дата из прошлого
                                  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // текущая дата
                                  header("Cache-Control: no-store, no-cache, must-revalidate");
                                  header("Pragma: no-cache");
                                  0
                                  А это не тоже самое, что давно с успехом делает либа Minify? правда она еще много чего умеет помимо
                                    0
                                    Да, видел, но так и не дошли руки скачать ее…

                                    Но в любом случае перед тем, как пользоваться фреймворком, неплохо бы разбираться, как он что делает. Хотя бы в общих чертах. Чтобы не было холивара, я поясню — при использовании jQuery не надо лазать в код по поводу и без, но для общего развития полезно иметь представление об ее внутреннем устройстве. Так же и с классом mysql например. Надо сначала немного поработать с БД через встроенные функции, а уж потом, имея знания, переходить на фреймворк. Но никто не отменял правило, что хорош тот фреймворк, который так задокументирован и сделан, что избавляет от необходимости себя изучать.

                                    Расскажите вкратце про либу :)
                                  0
                                  > Опытным путем было установлено, что PHP устанавливает хидеры запрета кеширования при использовании функции session_start();

                                  А как вы думаете, зачем разработчики это сделали, а? Неужели только для того, чтобы вам напакостить?

                                  когда используется сессия, речь обычно идет о страницах, вроде личного профиля, или например просмотра личной почты, то есть private страницах. Такие страницы *не должны* кешироваться промужуточными прокси-серверами (чтобы не попасть другому, неавторизованному, пользователя, ставится, cahce-control:private), и браузером (так как генерируются как правило динамически), поэтому PHP и ставит соответствующие заголовки. А вам вдруг в голову приперло все это сломать и сделать по-своему. Но-но. Мучайтесь потом сами с глюками кеширования у клиентов, удачи вам.
                                    0
                                    А Вам в голову не приходило, что сессия стартует глобально для всего проекта? И надо лишь несколько страниц кешировать. Умные все стали…
                                      0
                                      А может там она не очень-то и нужна… а то стали сессии по любому поводу открывать?))
                                        0
                                        Мне проще сессию запускать в каждом скрипте, и блокировать ее в 2х нужных. Чем наоборот :) Если вы имели ввиду, что может ее вообще не стартовать — ради 2 скриптов писать грабли с запуском или не-запуском сессии… вобщем мне проще прозрачно контролировать кеширование вручную :)
                                    0
                                    Ой, мой сайт :) Правда ссылка не туда.
                                      0
                                      Косяк :) поправил.

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

                                    Самое читаемое