Разгони свой сайт. Статическое сжатие css- и js- файлов на лету!
В сегодняшней статье я расскажу Вам об одном из способ ускорения загрузки Ваших сайтов, а именно мы будем сжимать css и js файлы. Хочу обратить Ваше внимание, что все это будете происходить автоматически, без Вашего участия, а оригинал файла всегда будет не изменным!
Алгоритм работы скрипта будет следующий:
- Объединяем все css- или js-файлы в один
- Создаем сжатую версию этого файла (причем только тогда, когда в оригинале были произведены какие-либо изменения)
- Реализовываем автоматическую отдачу сервером актуальной версии файла (в сжатом виде, если это поддерживает браузер)
- Если браузер закэшировал этот файл, то берем из кэша браузера
Итак, с целями и задачами разобрались. Теперь переходим непосредственно к созданию скрипта, который будет выполнять все эти действия. Для этого в корне сайта, создадим новый php-файл и назовем его mini.php.
Так как один скрипт должен обрабатывать и js- и css-файлы, то ему нужно передавать помимо самих файлов еще и тип этих файлов. Выглядеть это будет примерно так:
<link href="mini.php?type=css&files=reset,standart,style" rel="stylesheet" type="text/css">
или так:
<script type="text/javascript" src="mini.php?type=javascript&files=script,ajax"></script>
Теперь в нашем скрипте будут доступны такие переменные:
$_GET['type'] — будет содержать тип файлов
$_GET['files'] — через запятую, список файлов, которые нужно объединить и сжать
Следующим шагом необходимо задать директории для хранения кэша, js- и css-файлов:
// Папка для хранения кэша $cachedir = dirname(__FILE__) . '/cache'; // Папка где расположены css-файлы $cssdir = dirname(__FILE__) . '/styles'; // Папка где расположены js-файлы $jsdir = dirname(__FILE__) . '/js';
Относительно этих директорий и необходимо указывать путь к файлам при подключении.
Далее проверим были ли переданы файлы скрипту:
//Если файлы не переданы выводим 404 ошибку if (!isset($_GET['files']) or strlen($_GET['files']) == 0) { header('Status: 404 Not Found'); exit(); }
Теперь проверим существует ли директория для хранения кэша и если её нет, то создаем её:
if (!file_exists($cachedir)) { mkdir($cachedir); }
Следующим шагом определяем абсолютный путь к папке с файлами в зависимости от типа файлов:
$type = $_GET['type']; switch ($type) { case 'css': $base = realpath($cssdir); break; case 'javascript': $base = realpath($jsdir) break; default: header ("HTTP/1.0 503 Not Implemented"); exit; }
Так как мы передаем файлы без расширения, то необходимо написать функцию, которая будет автоматически добавлять к каждому файлу расширение в зависимости от его типа:
function addExtension($file,$type) { if ($type == 'javascript' && substr($file, -3) !== '.js'){ $file .= '.js'; } elseif($type == 'css' && substr($path, -4) !== '.css') { $file .= '.css'; } return $file; }
Используя эту функцию, сформируем массив файлов:
//Получаем массив файлов $files = explode(',', $_GET['files']); $types = array_fill(0,sizeof($files),$type); // Добавляем разширение для переданных файлов $files = array_map('addExtension',$files,$types);
Далее проверяем существуют ли переданные файлы на сервере и получаем время последнего редактирования
$lastmodified = 0; foreach ($files as $file){ $path = realpath($base.'/'.$file); if (!file_exists($path)) { header ("HTTP/1.0 404 Not Found"); exit; } $lastmodified = max($lastmodified, filemtime($path)); }
Создаем хэш файлов и отправляем браузеру Etag заголовок:
$md5 = md5($_GET['files']); $hash = $lastmodified . '-'.$md5; header ("Etag: \"" . $hash . "\""); if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) == '"' . $hash . '"') { header ("HTTP/1.0 304 Not Modified"); header ('Content-Length: 0'); exit; }
Теперь мы подошли к самому интересному. Нам необходимо объединить все файлы в один, вырезать из него все комментарии, переносы строк, табуляцию. Если браузер пользователя поддерживает GJIP-сжатие, то сжать его и автоматически отдать его в браузер.
Итак, определяем метод сжатия:
$gzip = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip'); $deflate = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate'); $encoding = $gzip ? 'gzip' : ($deflate ? 'deflate' : 'none'); if (!strstr($_SERVER['HTTP_USER_AGENT'], 'Opera') && preg_match('/^Mozilla\/4\.0 \(compatible; MSIE ([0-9]\.[0-9])/i', $_SERVER['HTTP_USER_AGENT'], $matches)) { $version = floatval($matches[1]); if ($version < 6) $encoding = 'none'; if ($version == 6 && !strstr($_SERVER['HTTP_USER_AGENT'], 'EV1')) $encoding = 'none'; }
Если браузер поддерживает сжатие и существует сжатый файл на сервере, то берем файл из кэша:
$cachefile = 'cache-' .$md5. '.' .(($type === 'css') ? 'css': 'js') . (($encoding != 'none')? '.' . $encoding : ''); if (file_exists($cachedir.'/'.$cachefile) && $lastmodified < filemtime($cachedir.'/'.$cachefile)) { if ($encoding != 'none') { header ("Content-Encoding: " . $encoding); } header ("Content-Type: text/".$type); header ("Content-Length: " . filesize($cachedir.'/'.$cachefile)); readfile($cachedir.'/'.$cachefile); exit; }
Если сжатый файл не существует на сервере, то формируем его, сжимаем, записываем на сервер и отдаем браузеру:
$contents = ''; foreach ($files as $file){ $path = realpath($base.'/'.$file); $contents .= file_get_contents($path); } // Удаляем комментарии, переносы строк, табуляцию, тройные пробелы if($type === 'css'){ $contents = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $contents); $contents = str_replace(array("\r", "\n", "\t", ' '), '', $contents); } else{ require('jsmin.php'); $contents = JSMin::minify($contents); } // выводим Content-Type файла header ("Content-Type: text/".$type); if (isset($encoding) && $encoding != 'none') { // Выводим сжатый файл $contents = gzencode($contents, 9, $gzip ? FORCE_GZIP : FORCE_DEFLATE); header ("Content-Encoding: " . $encoding); header ('Content-Length: ' . strlen($contents)); echo $contents; } else { // Выводим объединенный файл header ('Content-Length: ' . strlen($contents)); echo $contents; } // Записываем в кэш file_put_contents($cachedir.'/'.$cachefile, $contents);
Ну вот собственно и все! Скачать исходники можно здесь!
-
Комментарии (19)
- Сайт