Кэширование в Смарти

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

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

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

Мы рассмотрим два вида кэширования в смарти:

  1. Простое кэширование страницы
  2. Множественное кэширование


Просто кэш

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

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

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

Напишем для этого функцию get_mtime, которая будет возвращать количество секунд (с дробной частью), прошедших с полуночи 1 января года (так называемая эпоха Unix), и остальной код, чтобы показать кэширование в действии:

index.php

<?
	// возвращает метку времени
	function get_mtime() {
		$mtime = microtime();
		$mtime = explode(' ', $mtime);
		$mtime = $mtime[1] + $mtime[0];
		return $mtime;
	}
	// включаем и настраиваем Smarty
	include 'smarty/Smarty.class.php';
	$smarty = new Smarty;
	$smarty->compile_dir  = 'smarty/templates_c';
	$smarty->template_dir = 'templates';
	// включаем кэширование
	$smarty->cache_dir    = 'smarty/cache';
	$smarty->caching      = true;
	// формируем массив для нагрузки
	$my_array = Array();
	for ($i = 0; $i < 100000; $i++) {
		$my_array[] = $i;
	}
	// засекаем время
	$time1 = get_mtime();
	// формируем и выводим страницу
	$smarty->assign('my_array', $my_array);
	$smarty->fetch('index.html');
	// вычисляем и выводим разницу во времени
	$time2 = get_mtime();
	$totaltime = $time2 - $time1;
	echo '<br /><br />Time, sec: '.$totaltime;
?>

index.html

<html>
<head>
    <title>Page Title</title>
</head>
<body>
	{foreach from=$my_array item=item}
		{$item}
	{/foreach}
</body>
</html>

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

$smarty->caching = true;

И указал, где лежит директория, куда сваливать кешированные страницы (не забудьте ее создать):

$smarty->cache_dir = ‘smarty/cache';

Итак, посмотрим, получилось ли у нас ускорить вывод. При первом вызове этого скрипта время у меня было такое:

1.90849995613

Нажал в браузере F5. Получилось вот такое:

0.0234868526459

Еще раз нажал:

0.0197670459747

И еще раз:

0.0251159667969

И т.д.

Т.е. видим, что время сократилось где-то в 100 раз. Что тут произошло? При первом вызове скрипта смарти проинтерпретировал шаблон и сохранил результат в папку smarty/cache. При втором и последующих вызовах смарти не стал интерпретировать его заново, а просто взял кэшированную копию и вывел.

Здесь я использовал fetch вместо display, чтобы не выводить в окно браузера кучу чисел от 0 до 99999.

Если мы закомментируем строчку

$smarty->caching = true;

или вызовем

$smarty->clear_all_cache();

то кэширование работать не будет, т.к. в первом случае мы его просто выключим, а во втором - удалим все кэшированные копии.

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

if (!$smarty->is_cached('index.html')) {
	// формируем массив для нагрузки
	$my_array = Array();
	for ($i = 0; $i < 100000; $i++) {
		$my_array[] = $i;
	}
	$smarty->assign('my_array', $my_array);
}

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

Множественное кэширование

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

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

index.html

<html>
<head>
    <title>Page Title</title>
</head>
<body>
	{$body}
</body>
</html>

Т.е. тут содержимое контента зависит от переменной $body. Теперь удалим все файлы в папке smarty/cache и запустим вот этот скрипт:

<?
	// включаем и настраиваем Smarty
	include 'smarty/Smarty.class.php';
	$smarty = new Smarty;
	$smarty->compile_dir  = 'smarty/templates_c';
	$smarty->template_dir = 'templates';
	// включаем кэширование
	$smarty->cache_dir    = 'smarty/cache';
	$smarty->caching      = true;
	$smarty->assign('body', 'page1');
	// формируем и выводим страницу
	$smarty->display('index.html');
?>

Скрипт вывел page1 как и положено. Теперь поменяем в этом скрипте значение переменной body на page2:

$smarty->assign(‘body’, ‘page2′);

И запустим. Вывод скрипта неверен: page1. Должно быть page2.

Дело в том, что смарти выводит сохраненную копию страницы из кэша. И его в этом случае не волнует, что содержимое контента поменялось. Он выводит то, что сохранил раньше. Если сейчас посмотреть в папку smarty/cache, то там лежит один файл с расширением html. Теперь научим Smarty сохранять и выводить кэшированные страницы в зависимости от содержимого контента:

<?
	// включаем и настраиваем Smarty
	include 'smarty/Smarty.class.php';
	$smarty = new Smarty;
	$smarty->compile_dir  = 'smarty/templates_c';
	$smarty->template_dir = 'templates';
	// включаем кэширование
	$smarty->cache_dir    = 'smarty/cache';
	$smarty->caching      = true;
	$body = 'page1';
	// формируем и выводим страницу
	$smarty->assign('body', $body);
	$smarty->display('index.html', md5($body));
?>

Теперь если удалить все файлы из папки smarty/cache, запустить скрипт, поменять значение параменной $body на page2 и опять запустить, то все будет выведено правильно. При этом в папке smarty/cache будет лежать два файла - два кэшированных варианта одного и того же шаблона.

Здесь мы создали уникальный идентификатор страницы с помощью md5 и передали его методу display. Теперь display будет искать кешированную копию для определенного контента, а если не найдет - создаст еще одну копию.

Зачастую нужно различать кэшированные копии не только в зависимости от контента, но и в зависимости от глобальных переменных сессии или запросов GET и POST. Например нам нужно, чтобы кэш обновлялся, если что-то изменилось в сессии или что-то было передано в REQUEST через форму. Тогда я рекомендую пользоваться вот такой функцией для формирования идентификатора кэшированной копии, который потом можно будет передать в display или fetch вторым параметром:

function get_cache_id() {
	$request = $_REQUEST;
	unset($request[session_name()]);
	return md5(serialize($request).serialize($_SESSION));
}

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

<?
	function get_cache_id() {
		$request = $_REQUEST;
		unset($request[session_name()]);
		return md5(serialize($request).serialize($_SESSION));
	}
	// включаем и настраиваем Smarty
	include 'smarty/Smarty.class.php';
	$smarty = new Smarty;
	$smarty->compile_dir  = 'smarty/templates_c';
	$smarty->template_dir = 'templates';
	// включаем кэширование
	$smarty->cache_dir    = 'smarty/cache';
	$smarty->caching      = true;
	$cache_id = get_cache_id();
	if (!$smarty->is_cached('index.html', $cache_id)) {
		$body = 'page1';
		// еще что-то делаем для формирования страницы...
		$smarty->assign('body', $body);
	}
	// формируем и выводим страницу
	$smarty->display('index.html', $cache_id);
?>

Мы можем удалить все кэшированные копии с конкретным cache_id:

$smarty->clear_cache(null, $cache_id);

Время кэширования

Время, в течение которого действителен кэш, можно установить с помощью cache_lifetime перед вызовом display или fetch:

$smarty->cache_lifetime = 300; // в секундах

Это означает, что по прошествии 300 секунд с момента создания кэша, последний будет обновлен.

У установки этого параметра есть свои особенности: его нельзя устанавливать повторно для одного и того же шаблона. Т.е. следующее не прокатит:

$smarty->cache_lifetime = 300;
$smarty->cache_lifetime = 500;
$smarty->display(‘index.html’); // время хранения кэша - 300 секунд

А вот так можно:

$smarty->cache_lifetime = 300;
$smarty->display(‘index1.html’); // время хранения кэша - 300 секунд

$smarty->cache_lifetime = 500;
$smarty->display(‘index2.html’); // время хранения кэша - 500 секунд

Но тут разные шаблоны.

Резюме

Сегодня мы рассмотрели кэширование в смарти - весьма полезная вещь для увеличения производительности сайта. Но с ней нужно быть осторожным. Перед серьезным использованием этой фичи рекомендую изучить ее детально в документации по Smarty на сайте [ссылка].

До встречи! :)

Статьи по теме Smarty:
Смарти. Введение
Смарти: продолжаем осваивать
Пишем плагин для Smarty





Читайте также:



13 Ответов на “Кэширование в Смарти”

  1. Werewolf

    Познавательно! Все очень понятно и грамотно. Спасибо автору!

  2. novice

    Стараюсь :)

  3. Давно смарти хочу асилить, да никак руки не доходят, а автор молодец выложил путёвые заметки

  4. znikus

    автозамена это очень хорошо, но когда в коде все слова smarty заменились на <a href=”https://i-novice.net/smarti-vvedenie/”>smarty</a> - читать немного сложновато :о)

  5. novice

    Непредвиденное поведение плагина :) Спасибо. Исправил.

  6. pumi

    А можно ли изменить время кэширования
    в самом файле шаблона
    допустим что-бы
    {include file=”menu.tpl”}
    всегда кэшироавлся.

  7. PuMi

    Я хотел сказать, что в центре сайта есть облако тэгов, как сделать так что-бы оно всегда бралось из кэша?

  8. PuMi

    Нашел ответ :):
    [ссылка]
    [ссылка]

  9. anon

    хоть сделал четко копи пасте, но вот:

    Fatal error: Smarty error: the $cache_dir ‘smarty/cache’ does not exist, or is not a directory. in Z:\home\localhost\www\Mytest\PHP+MySQL\Smarty\2\Smarty\Smarty.class.php on line 1092

  10. novice

    2 anon: Это значит, что директория cache не найдена в том месте, где Вы сказали Smarty найти ее. В Вашем случае нужно вот так:

    $smarty->cache_dir = ‘Mytest/PHP+MySQL/Smarty/2/Smarty/cache';

    И при этом необходимо создать директорию cache в этом месте.

  11. Спасибо пригодилось…

  12. Alexander

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

  13. Благодарю, про кеширование разъяснил.

    Подписался!


© Copyright. . I-Novice. All Rights Reserved. Terms | Site Map