Tworzenie wydajnych i przygotowanych na dużą popularność serwisów internetowych nie jest zadaniem łatwym. Przekonują się o tym webmasterzy, dla których sukces witryny okazuje się paradoksalnie przeszkodą w jego dalszym rozwoju. Nagły wzrost transferu i generowanego obciążenia serwera przekładają się bowiem wprost na dodatkowe koszty. Skutecznym rozwiązaniem okazuje się wówczas wykorzystanie mechanizmu cache.
Idea zastosowania pamięci podręcznej – bo
tak często określa się cache – znajduje swoje
zastosowanie w informatyce od wielu lat:
w pracy procesora, dysku twardego czy przeglądarki
internetowej.
Wraz z rozwojem sieci, także strony internetowe
stają się coraz bardziej rozbudowanymi
aplikacjami – komunikującymi się z bazami danych,
korzystającymi z systemów szablonów i oferującymi
bogaty interfejs, łączący w sobie technologie
XHTML, CSS i JavaScript. Na każdej z tych warstw
możliwe jest zastosowanie mechanizmu cache
– co w artykule zostanie pokazane na przykładzie
witryny napisanej w języku PHP.
Co ważne, zasada działania jest wspólna także
dla innych technologii, różnice występują jedynie
w implementacji. Zastosowanie przedstawionych
rozwiązań pozwoli na znaczne zaoszczędzenie
zasobów serwera, a także zmniejszenie generowanego
transferu, nawet o kilkadziesiąt procent.
Ograniczenie transferu
Najczęstszym powodem, dla którego sięga się
po mechanizm cache, jest chęć ograniczenia generowanego
przez witrynę transferu. Mimo że dostawcy
usług hostingowych oferują coraz większe
pakiety, prowadzenie popularnego serwisu wciąż
wiąże się z koniecznością dodatkowych opłat z tytułu
przekroczenia limitów transferu. Najprostszym
i najłatwiejszym do wprowadzenia sposobem na
ograniczenie kosztów jest wprowadzenie kompresji
kodu HTML, CSS czy JS.
Wszystkie popularne przeglądarki (Internet
Explorer, Firefox czy Opera) obsługują kompresję
w ramach protokołu HTTP, gdzie wykorzystywane
są dwa algorytmy: gzip (GNU zip) oraz deflate.
Oznacza to tyle, że po przesłaniu do przeglądarki
odwiedzającego skompresowane w ten sposób
pliki zostaną automatycznie rozpakowane, co
odbywa się w tle, nie angażując w to działanie
użytkownika.
Przeglądarka, wysyłając do serwera żądanie
GET (pobierz), wykorzystuje nagłówek \”Accept-
Encoding\”, w którym informuje o obsługiwanym
przez siebie algorytmie kompresji. Serwer na tej
podstawie może skompresować wysyłane do
przeglądarki dane, co przyspiesza czas całej operacji
i zmniejsza liczbę koniecznych do przesłania
informacji. Jakie pliki więc warto kompresować?
Doskonale nadają się do tego właśnie .html, .js
i .css, których objętość można bez strat zmniejszyć
nawet o 80%. Kompresowanie plików .jpg czy .zip
nie ma z kolei sensu, gdyż są one już upakowane.
W przypadku silnika strony napisanego w języku
PHP, aby wykorzystać gzip, wystarczy dodać kilka
linii kodu, które muszą się znaleźć przed wysłaniem
jakichkolwiek danych do przeglądarki użytkownika:
if (!ini_get (\'zlib.output_compression\')) {
if (substr_count ($_SERVER[\'HTTP_ACCEPT_ENCODING\'], \'gzip\')) {
ini_set (\'zlib.output_compression_level\', 1);
ob_start (\'ob_gzhandler\');
}
}
W pierwszej linii sprawdzamy, czy kompresja
nie jest już uaktywniona, gdyż można ją włączyć
globalnie dla całego serwera, poprzez stosowny
wpis w konfiguracji PHP. Dalszy kod wykonujemy
jedynie w przypadku, gdy automatyczna kompresja
jest na serwerze wyłączona.
W linii nr 2 sprawdzamy, czy przeglądarka użytkownika
obsługuje kompresję gzip – wychwytując,
czy nazwa tego algorytmu występuje w nagłówku
\”Accept-Encoding\” – jeśli tak, wywoływane są
dwie funkcje. Za pomocą ini_set() ustawiamy wartość
parametru \”{stala}zlib.output_compression_level{/stala}\”,
przypisując mu liczbę z przedziału 1.9. Im mniejsza
cyfra, tym słabsza jakość kompresji – jednak co za
tym idzie, także mniejsze obciążenie procesora.
Różnice w objętości spakowanych danych między
zastosowaniem 1 a 9 nie są spore, dlatego rozsądne
jest wybranie niskiej wartości. Wywołanie
ob_start() z parametrem \'{stala}ob_gzhandler{/stala}\’ włącza
kompresję gzip.
Nieco więcej zachodu wymaga dodanie kompresji
gzip w przypadku wysyłania plików z kodem
CSS i JavaScript. Pojawiają się bowiem dwa problemy,
które trzeba rozwiązać. Pierwszym jest fakt,
że pliki .css czy .js nie są interpretowane przez PHP,
co jednak można łatwo rozwiązać poprzez zmianę
konfiguracji w pliku .htaccess. Wystarczą do tego
2 linie kodu:
AddType application/x-httpd-php .css .js
php_value auto_prepend_file gzip.php
Pierwsza informuje serwer, że pliki .css i .js
będą parsowane przez interpretator PHP. W kolejnej
linijce wykorzystujemy mechanizm \”{stala}auto_prepend_file{/stala}\”, który powoduje przy wywołaniu
dołączenie pliku gzip.php, w którym zaimplementujemy
mechanizm kompresji. Dzięki temu nie
będziemy musieli nic zmieniać w plikach .js czy .css
– pozostaną one nietknięte.
Plik {stala}.htaccess{/stala} umieszczamy oczywiście w katalogu,
gdzie znajdują się pliki .js oraz .css – nie
w katalogu głównym konta czy np. bezpośrednio
w public_html. Ważne też, aby w katalogu nie
znajdowały się inne pliki .php.
Drugi problem pojawia się w momencie
dodania kompresji w pliku {stala}gzip.php{/stala}. Pliki .css
i .js domyślnie są bowiem cache\’owane przez
większość przeglądarek – tak aby przy każdym
odświeżeniu strony nie były ponownie pobierane
z serwera. Zmiany dokonane w .htaccess oraz
dodanie kompresji jak w przypadku plików .php
powodują modyfikację wysyłanych nagłówków,
usuwając z nich m.in. \”Last-modified\”, przez co nie
są one cache\’owane.
Aby nie musieć wybierać między kompresją
a cache\’owaniem plików w przeglądarce,
należy ręcznie zapewnić przesłanie odpowiednich
nagłówków. Jako że pliki .css i .js są plikami
statycznymi, nie ma sensu kompresować ich przy
każdym wywołaniu – plik {stala}gzip.php{/stala} powinien to
uwzględniać. Rozpocznijmy więc kodowanie:
$file = end (explode (\'/\', $_SERVER[\'REQUEST_URI\']));
$file_last_modification = filemtime ($file);
$gm_file_last_modification = gmdate (\"D, d M Y H:i:s T\", $file_last_modification);
// is file modified
if ($_SERVER[\'HTTP_IF_MODIFIED_SINCE\'] == $gm_file_last_modification) {
header (\"HTTP/1.0 304 Not Modified\");
header (\'ETag: \"\' . md5 ($file . $file_last_modification) . \'\"\');
exit;
}
Na początku przygotowujemy trzy zmienne,
zawierające kolejno: nazwę wywoływanego pliku
oraz datę jego ostatniej modyfikacji w dwóch
formatach: UNIX-owym znaczniku czasu oraz GMT
(Greenwich Mean Time).
Kolejnym krokiem jest sprawdzenie, czy przeglądarka
użytkownika nie ma już w swojej pamięci
wywoływanego pliku – porównujemy w tym celu
datę jego modyfikacji. W przypadku, gdy klient
ma już żądany plik, wysyłamy do przeglądarki
nagłówek 304 \”Not Modified\” i przerywamy dalsze
wywoływanie skryptu. Wcześniej wysyłany jest
jeszcze nagłówek \”Etag\” (entity tag), który jest
sumą kontrolną, pozwalającą określić, czy plik został
zmieniony – generujemy ją za pomocą funkcji
hashującej md5, podając jako parametr nazwę
pliku i datę modyfikacji.
Dalsza część kodu będzie wykonywana, jeśli
przeglądarka nie ma w swoim buforze żądanego
pliku CSS/JS:
(end (explode (\'.\', $file)) == \'css\') ? header (\'Content-type: text/css; charset:UTF-8\') : header(\'Content-type: text/javascript; charset: UTF-8\');
header (\'Last-Modified: \' . $gm_file_last_modification);
header (\'ETag: \"\' . md5 ($file . $file_last_modification) . \'\"\');
W pierwszej linii w zależności od rozszerzenia
żądanego pliku (css lub js) wysyłamy do przeglądarki
nagłówek informujący o typie MIME:
text/css lub text/javascript. Podane jest również
zastosowane kodowanie, możemy je zmienić na
np. iso8859-2, jeśli takie jest wykorzystywane,
a w kodzie znajdują się polskie znaki. Następne
dwa nagłówki określają datę modyfikacji pliku i entity tag. Musimy je podać, aby wcześniejsze
sprawdzenie modyfikacji miało sens: podana tutaj
data zostanie nam przesłana przez przeglądarkę
przy następnym odświeżeniu.
Czas teraz na dodanie obsługi kompresji gzip.
Z racji na to, że pliki css i js nie są generowane
dynamicznie, a ich kompresja przy każdorazowym
żądaniu nie ma sensu – zastosujemy inne rozwiązanie.
Polega ono na zapisywaniu spakowanych
plików w osobnym katalogu. Kod prezentuje się
następująco:
if (substr_count ($_SERVER[\'HTTP_ACCEPT_ENCODING\'], \'gzip\')) {
$filegz = \'gzipped/\' . $file . \'.gz\';
$makeNewGz = false;
if (file_exists ($filegz)) {
$gzip_last_modification = filemtime ($filegz);
if ($gzip_last_modification + 10 < $file_last_modification) {
$makeNewGz = true;
}
} else {
$makeNewGz = true;
}
if ($makeNewGz === true) {
$handle = gzopen ($filegz, \'w9\');
gzwrite ($handle, file_get_contents ($file));
gzclose ($handle);
chmod ($filegz, 0666);
}
header (\'Content-Encoding: gzip\');
header (\'Vary: Accept-Encoding\');
echo file_get_contents ($filegz);
exit;
}
Najpierw następuje sprawdzenie, czy przeglądarka
obsługuje gzip. Jeśli nie obsługuje, żadne
linie kodu nie będą wykonywane, a w dalszym
kroku przesłany będzie żądany plik (gzip.php
kończy swoje działanie - był on jedynie dodawany
na początku wywołania .css lub .js).
Następnie przygotowujemy dwie zmienne:
$filegz zawiera ścieżkę skompresowanego pliku
(umieszczamy go w katalogu gzipped), musimy
więc wcześniej zadbać o jego stworzenie i nadanie
mu odpowiednich praw do zapisu (na serwerach
UNIX najczęściej komendą: chmod 777 gzipped).
Zmienną {stala}$makeNewGz{/stala} ustawiamy początkowo
jako false - przechowywać w niej będziemy
informację, czy konieczne jest dokonanie kompresji
żądnego pliku, co ma miejsce tylko w dwóch
przypadkach: jeśli nie ma jeszcze skompresowanego
pliku lub jest on starszy niż jego nieskompresowana
wersja. Dzięki takiemu rozwiązaniu,
w przypadku aktualizacji np. pliku stylów, serwer
automatycznie wykryje zmiany, skompresuje go
i poinformuje przeglądarkę, że zawartość została
zmieniona.
Jeśli {stala}$makeNewGz{/stala} zostało ustawione na \"true\",
musimy przeprowadzić kompresję, czego dokonujemy
za pomocą funkcji {stala}gzopen(){/stala} oraz {stala}gzwrite(){/stala}.
Jako jeden z argumentów tej pierwszej funkcji
podajemy stopień kompresji: \"w9\". Tutaj również
wybieramy go z przedziału 1.9, warto jednak dać
najwyższą wartość - kompresja będzie najsilniejsza,
a z racji faktu, że wykonywana jest ona tylko
jednorazowo po zmianie pliku, nie musimy się
martwić o obciążenie procesora.
Pozostało nam już tylko poinformowanie
przeglądarki, że wysyłana wartość jest kodowana
algorytmem gzip, oraz oczywiście wysłanie
spakowanego pliku, do czego wykorzystywana jest
funkcja {stala}file_get_contents(){/stala}. Istotne jest również
wywołanie exit; co zakończy wykonywanie
skryptu, a co za tym idzie - nie zostanie dołączony
nieskompresowany plik.
Rozwiązanie na pierwszy rzut oka wydawać się
może skomplikowane, jednak można je wdrożyć
w działającym serwisie internetowym praktycznie
w minutę. Wystarczy przekopiować pliki {stala}.htaccess{/stala}
oraz {stala}gzip.php{/stala}, a następnie stworzyć katalog
\"gzipped\" z prawami do zapisu przez wykonywany
skrypt. Nie są konieczne modyfikacje istniejących
plików .js i .css.
Cache \"kosztownych\" operacji PHP
Przedstawione do tej pory metody dotyczyły
kompresji danych przesyłanych z serwera
do przeglądarki użytkownika. Pozwala to na
zmniejszenie ilości wygenerowanego transferu,
jednak nie wpływa na spadek obciążenia serwera.
Wręcz przeciwnie, jest ono nawet większe,
gdyż w przypadku kompresji stron dynamicznych
procesor musi wykonać dodatkowo mechanizm
upakowania danych. Jak więc oszczędzić nieco
zasobów serwera?
Tworząc typową aplikację PHP, można
zastosować cache na trzech płaszczyznach:
buforowaniu wyników najbardziej zasobożernych
operacji, systemie szablonów oraz zapytań do
bazy danych. W tym pierwszym przypadku trzeba
samodzielnie obsłużyć mechanizm cache, co
zapewni przygotowana specjalnie do tego celu
klasa. Z kolei popularne systemy szablonów oraz
systemy abstrakcji baz danych mają zazwyczaj
wbudowany mechanizm cache, wówczas
wystarczy więc jedynie go włączyć i odpowiednio
zastosować.
Załóżmy, że w naszym serwisie mamy kilka
fragmentów kodu, które szczególnie obciążają
system - mogą to być np. skomplikowane obliczenia,
operacje na dysku czy pobieranie danych
z zewnątrz. Żeby odciążyć serwer, najbardziej
zasobożerne operacje powinny być cache\'owane.
Ponadto programista powinien mieć możliwość
kontroli, jak długo zapisywane mają być w pamięci
(której funkcję będzie pełnił dysk twardy)
wyniki działania konkretnych linii kodu. Aby lepiej
zrozumieć zasadę działania całego mechanizmu,
zacznijmy od kodu, w którym cache będzie wykorzystywany:
$cache = new cache;
$cache -> setFile (\'sample_data\');
if ($cache -> isFresh (10)) {
$result = $cache -> load ();
} else {
$result = pow (256, 512);
$cache -> save ($result);
}
echo \'Liczba 256 podniesiona do potęgi 512
jest równa \'{stala}.$result;{/stala}
Jak widać, wprowadzenie cache do aplikacji
nie jest skomplikowane. Rozpoczynamy od
stworzenia instancji klasy cache, a następnie
definiujemy nazwę pliku, w którym zapisywane
będą dane. Kolejny krok to sprawdzenie,
czy cache jest aktualny - w tym przypadku
sprawdzamy, czy został wygenerowany w ciągu
ostatnich 10 minut - jeśli tak, wówczas dane są
ładowane do zmiennej {stala}$result{/stala}. Jeśli zawartość
pliku \'sample_data\' jest nieaktualna, wówczas do
$result przypisujemy wynik działania zasobożernej
operacji. W przykładzie jest to 512-ta potęga
liczby 256, co oczywiście nie ma dużego sensu
(jej obliczenie przy dzisiejszej mocy procesorów
nie stanowi dla komputera problemu). Kolejnym
krokiem jest zapisanie wyniku działania do pliku,
w którym przechowywany jest cache.
Zastosowanie tego typu kodu sprawi, że
dokonywanie obliczenia działania matematycznego
będzie miało miejsce co najwyżej raz na 10
minut - niezależnie, czy skrypt zostanie wywołany
w tym czasie 1, 10 czy 10 000 razy, co pokazuje
możliwości tego rozwiązania. Przyjrzyjmy się teraz,
jak zbudowana jest sama klasa cache:
class cache {
public $file;
public function setFile ($name) {
$this -> file = \'/home/user/cache/\' . $name . \'.cache\';
}
public function isFresh ($minutes = 10) {
if (!file_exists ($this -> file))
return false;
if (filemtime ($this -> file) >= time () - ($minutes * 60)) {
return true;
} else {
$this -> clear ($this -> file);
return false;
}
}
}
Klasa ma jedynie właściwość $file, w której
przechowywana jest nazwa pliku, ustawiana za
pomocą metody {stala}setFile(){/stala}. Pliki będą miały rozszerzenie
.cache i będą się znajdować w katalogu /
home/user/cache/ - ścieżkę trzeba oczywiście
dostosować do swojego serwisu.
Kolejną metodą jest {stala}isFresh(){/stala}, która sprawdza
aktualność cache, przyjmując jako argument liczbę
minut. Jeśli plik z wynikiem działania zasobożernej
operacji nie istnieje lub jest starszy niż podana
wartość czasu - zwracane jest false, dzięki czemu
wiemy, że konieczne jest wykonanie operacji
i zapisanie jej do cache.
Do zapisywania i ładowania cache służą odpowiednio
metody {stala}save(){/stala} i {stala}load(){/stala}:
public function save ($data) {
$handle = fopen ($this->file, \'w\');
fwrite ($handle, serialize ($data));
fclose ($handle);
chmod ($this -> file, 0666);
}
public function load () {
return unserialize (file_get_contents ($this -> file));
}
Funkcje te realizują prosty odczyt i zapis pliku,
połączony z (de)serializacją danych. {stala}Serialize(){/stala}
zwraca wartość typu string, będącą strumieniem
bajtów, mogącym odpowiadać zmiennej, tablicy
czy obiektowi, dzięki czemu możemy cache\'ować
dowolne elementy języka PHP.
Cache zapytań do bazy danych
Połączenia z bazą danych i nadmierna
liczba zapytań to często wąskie gardło aplikacji
internetowych. Na szczęście i tutaj z pomocą
przychodzi cache. Jego wprowadzenie jest najłatwiejsze
w przypadku korzystania z którejś z klas,
zapewniających warstwę abstrakcyjną, nakładaną
na natywne funkcje obsługujące połączenie
z bazą danych w PHP. Podobnie jak w przypadku
systemu szablonów, rozwiązań realizujących tę
funkcjonalność jest wiele i nie sposób ich wszystkich
opisać.
Od wersji 5.1 w PHP domyślnie dostępne jest
rozszerzenie PDO (PHP Data Objects), które zapewnia
obiektową obsługę połączenia i dokonywania
zapytań w bazie danych. Z pewnością warto z PDO
korzystać, zwłaszcza że istnieją rozwiązania, rozszerzające
je o możliwość cache\'owania danych.
Jednym z nich jest klasa PCO (PDO Cache Object
queries, http://art.php.pl/Projekt/31), napisana
przez Marka Pałczyńskiego na konkurs organizowany
przez społeczność serwisu php.pl. Jedną z funkcji
tej nakładki na PDO jest właśnie obsługa cache,
która realizowana jest w bardzo prosty sposób:
$model = new DataMapper (\'news\');
$news = $model -> cache() -> findAll();
Wystarczy użyć metody cache(), by dane nie
były pobierane z bazy danych, a z buforu, o ile
taki istnieje. Co więcej, nie musimy martwić się
o aktualność cache, gdyż będzie on automatycznie
czyszczony w przypadku dodania, modyfikacji lub
usunięcia danych w tabeli. Ponadto, aby cache
zadziałał, nie może być ustawiony tryb debugowania
({stala}DEBUG=0{/stala}) oraz podana musi być prawidłowa
ścieżka do folderu, przechowywana w stałej
CACHE_DIR.
Inną nakładką na PDO, rozszerzającą jej możliwości,
jest biblioteka Open Power Driver (http://www.openpb.net/opd.php), również polskiego
autora Tomasza Jędrzejewskiego. Cache realizowany
jest tam w nieco inny sposób (ustawiamy jego
ważność), bardzo dobrze opisany w dokumentacji.
Czy warto? Tak!
Zastosowanie opisanych technik pozwala na
znaczne odciążenie serwera, a także oszczędność
generowanego przez serwis transferu. Zważywszy
na niewielkie nakłady czasu, konieczne do wdrożenia
rozwiązań cache i kompresji gzip, sposoby
te są w zasięgu praktycznie każdego webmastera.
Włożona w te działania praca z pewnością się
opłaci - użytkownikom witryna załaduje się szybciej,
a koszty ponoszone na opłatę dodatkowego
transferu spadną.
Akceleratory PHP
PHP jest językiem interpretowanym, co oznacza, że przy każdym wywołaniu skryptu jego kod jest kompilowany
do kodu bajtowego, a dopiero potem wykonywany. Operacja kompilacji zajmuje oczywiście
czas i zasoby serwera, dlatego i tutaj możliwe jest zastosowanie cache.
Obecnie istnieją cztery liczące się rozwiązania, które przechowują w pamięci operacyjnej lub na dysku
kod bajtowy skryptów PHP, tym samym znacznie przyspieszając ich uruchamianie. Najbardziej znany
jest Zend Optimizer (http://zend.com/products/zend_optimizer), będący rozwiązaniem komercyjnym.
Wydajnością nie ustępują mu jednak rozwiązania otwarte, takie jak eAccelerator (http://www.eaccelerator.net) czy XCache (http://xcache.lighttpd.net). Dostępne jest również rozszerzenie PHP o nazwie APC
(Alternative PHP Cache), które oferuje podobną funkcjonalność.
Instalacja tego typu aplikacji wymaga praw administracyjnych na serwerze, jednak przeważnie firmy
hostingowe decydują się na korzystanie z jednego z tych rozwiązań. Stąd istnieje duże prawdopodobieństwo,
że prowadzony przez nas serwis internetowy jest już w ten sposób cache’owany i jego kod
bajtowy przechowywany jest w pamięci serwera.
Do zdefiniowana zostały jeszcze dwie metody,
które również mogą okazać się niezwykle przydatne
do wykorzystania w kodzie aplikacji:
public function exists () {
return file_exists ($this -> file);
}
public function clear ($file) {
unlink ($file);
}
Nie są to skomplikowane funkcje. Pierwsza
z nich sprawdza, czy plik z cache istnieje, a druga
kasuje tenże plik. Gdzie to wykorzystać?
Załóżmy, że serwis ma bazę artykułów, które
każdy może skomentować. Aby zaoszczędzić
zasoby, chcemy wprowadzić mechanizm cache-
\'owania tychże komentarzy. Jak jednak ustawić
liczbę minut, przez które będą one buforowane?
Z pomocą przychodzą właśnie dwie powyższe
metody, których wykorzystanie może wyglądać
następująco:
$cache = new cache;
$cache -> setFile (\'comments_\'+$articleId);
if ($cache -> exists ()) {
$comments = $cache -> load ();
} else {
$comments = get_comments ($articleId);
$cache -> save ($comments);
}
Takie rozwiązanie ma sens wtedy, kiedy przy
dodaniu komentarza wykonamy kod:
$cache = new cachea;
$cache -> clear (\'comments_\'+$articleId);
Jak widać, zastosowanie {stala}exists(){/stala} zamiast
{stala}isFresh(){/stala} pozwoliło na optymalne użycie cache. Pobieranie
listy komentarzy ({stala}get_comments(){/stala}) będzie
wykonywane jedynie wówczas, kiedy od momentu
ostatniej odsłony został dopisany przez użytkownika
nowy komentarz.
Napisanie klasy cache i jej wykorzystanie nie
jest skomplikowane, a niesie za sobą olbrzymie
możliwości, pozwalając programiście na znaczną
optymalizację kodu przy niedużym wysiłku. Wprowadzenie
tej metody nawet w już działającym
serwisie nie jest zadaniem czasochłonnym.
Cache systemu szablonów
Rozwiązań pozwalających na użycie szablonów
w budowie serwisu internetowego jest sporo
i niemożnością jest opisanie sposobu uruchomienia
cache we wszystkich z nich. Te systemy, które
obsługę buforowania mają wbudowaną, posiadają
również dokumentację na temat sposobu jego
wykorzystania. W przypadku popularnego skryptu
Smarty, włączenie cache sprowadza się do ustawienia
zmiennej {stala}$caching{/stala}, która przyjmuje wartość
0, 1 lub 2:
$smarty = new Smarty;
$smarty -> caching = 1;
$smarty -> display (\'page.tpl\');
Wartość 0 oznacza wyłączenie cache, 1 - włączenie,
a 2 - włączenie z możliwością ustawienia
czasu życia cache:
$smarty = new Smarty;
$smarty -> caching = 2;
$smarty -> cache_lifetime = 300;
$smarty -> display (\'page.tpl\');
Czas aktualizacji cache podajemy w sekundach
i przypisujemy do właściwości {stala}$cache_lifetime{/stala}.
Smarty udostępniają również szereg funkcji,
których działanie jest podobne jak w opisywanej
wcześniej klasie cache: {stala}is_cached(){/stala} czy {stala}clear_cache(){/stala}.
Systemy szablonów pozwalają ponadto na
cache\'owanie jedynie wybranych fragmentów
stron, z wyłączeniem np. formularza logowania czy
sekcji, które są personalizowane (osobne ustawienia
dla każdego z użytkowników).
W przypadku wykorzystania dowolnego systemu
szablonów warto sprawdzić jego możliwości
w zakresie cache\'owania. W niektórych przypadkach
można tak przyśpieszyć aplikację, że będzie
wykonywała się prawie tak szybko jak statyczne
pliki .html.