Давайте попробуем что-то другое на Nettuts + сегодня. Любой дизайнер, который работал с большими CSS-файлами, согласится, что его основным недостатком является невозможность использования переменных. В этой статье мы узнаем, как реализовать переменные с помощью PHP и мода переписывания URL Apache.
Предисловие
Эта техника несколько проста. Мы попросим Apache перенаправить любую таблицу стилей на определенный скрипт PHP. Этот скрипт откроет таблицу стилей и прочитает ее построчно, чтобы найти и заменить любые пользовательские переменные. Наконец, проанализированный контент будет отображаться как чистый CSS; браузеры не заметят разницы. Чтобы закрыть это руководство, мы также увидим, как кэшировать обработанный результат, чтобы избежать ненужного использования процессора.
Обратите внимание, что ожидаются некоторые базовые знания PHP (OOP), Apache и HTTP.

Требования:
- Apache с включенным модом Rewrite
- PHP 5
Шаг 1 — Построить проект
Давайте сначала создадим нашу простую структуру проекта. Добавьте файл index.html в корень вашего проекта.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
<!DOCTYPE html PUBLIC «-//W3C//DTD XHTML 1.0 Transitional//EN» «http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd»>
<html xmlns=»http://www.w3.org/1999/xhtml»>
<head>
<meta http-equiv=»Content-Type» content=»text/html; charset=utf-8″ />
<title>Variables in CSS Files… It’s possible!</title>
<link href=»css/styles.css» rel=»stylesheet» type=»text/css» />
</head>
<body>
<h1>Variables in Stylesheets</h1>
<p>It’s possible!</p>
<p>With PHP and Apache URL Rewrite Mod</p>
</body>
</html>
|
Теперь создайте файл CSS со следующими переменными и поместите его в папку «css»
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
$font: arial, sans-serif;
$main-color: #3D7169;
h1 {
font: 200% $font;
color: $main-color;
}
p {
background: $secondary-color;
color: $main-color;
font-family: $font;
padding: 10px;
}
|
Наконец, создайте пустой файл PHP с именем enhanced_css.php и пустой файл .htaccess . Этот последний файл переопределяет конфигурацию сервера по умолчанию и применяется к папке и ее подпапкам.
Теперь наш проект должен выглядеть так:

Шаг 2 — Перенаправление файлов CSS в скрипт PHP
Мы хотим перенаправить любой URL с расширением CSS на наш скрипт PHP. Сервер Apache позволяет нам сделать это, используя мод URL Rewrite.
Во-первых, убедитесь, что модули «rewrite_module» активны на вашем сервере. Перейдите и найдите файл httpd.conf в вашей папке Apache. Отредактируйте его и найдите эту строку:
|
1
|
LoadModule rewrite_module modules/mod_rewrite.so
|
При необходимости раскомментируйте его, удалив префикс «#», и перезапустите Apache, чтобы убедиться, что параметры конфигурации активны.

Теперь отредактируйте ваш файл .htaccess и добавьте следующие строки.
|
1
2
|
RewriteEngine on
RewriteRule ^(.*\.css)$ enhanced_css.php?css=$0
|
Сохрани это. Как упоминалось ранее, строки выше просят Apache перехватить все URL-адреса с расширением .css и перенаправить их на «extended_css.php». Исходный путь к файлу CSS передается как параметр ‘css’.
Например :
/css/styles.css
Будет перенаправлен на:
enhanced_css.php? CSS = / CSS / styles.css
Замечания:
Некоторые хостинговые решения не позволяют переопределять их настройки пользователем. Если это так, ссылки на таблицы стилей в коде HTML должны быть заменены вручную.
В таких случаях вам придется заменить:
|
1
|
<link href=»css/styles.css» rel=»stylesheet» type=»text/css» />
|
с:
|
1
|
<link href=»enhanced_css?css=css/styles.css» rel=»stylesheet» type=»text/css» />
|
Шаг 3 — Разбор CSS-файла с помощью PHP
Поскольку CSS-файлы перенаправляются нашим PHP-скриптом, давайте создадим класс с именем «Enhancedcss», чтобы читать их, находить и заменять переменные, а затем отображать содержимое в виде чистого CSS. Наш класс будет создан путем передачи $_GET['css'] в конструктор. Помните перенаправление .htaccess . $_GET содержит путь к текущей таблице стилей.
|
1
2
3
4
|
if (isset($_GET[‘css’])) {
$css = new EnhancedCss($_GET[‘css’]);
$css->display();
}
|
Основная реализация класса состоит из четырех методов. Позже мы добавим метод кэширования.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
class EnhancedCss {
public $values;
public $cssFile;
public function __construct($cssFile) {
// check if the css file exists
}
private function parse() {
// open the css file and throw every line to
// findAndReplaceVars method
}
private function findAndReplaceVars($line) {
// find the variable definitions, store the values,
// replace the variable by their defined values.
}
public function display() {
// display the new parsed content
}
}
|
Конструктор
Здесь нет ничего сексуального. Мы проверяем, существует ли запрошенный файл CSS. Если нет, скрипт возвращает ошибку 404 http. Путь к файлу CSS хранится в свойстве $this->cssFile для $this->cssFile вычисления имени файла кэша.
|
1
2
3
4
5
6
7
|
public function __construct($cssFile) {
if (!file_exists($cssFile)) {
header(‘HTTP/1.0 404 Not Found’);
exit;
}
$this->cssFile = $cssFile;
}
|
Метод разбора
Этот метод открывает файл CSS и читает его построчно.
|
1
2
3
4
5
6
7
8
|
private function parse() {
$content = »;
$lines = file($this->cssFile);
foreach($lines as $line) {
$content .= $this->findAndReplaceVars($line);
}
return $content;
}
|
Функция файла используется здесь. Это может быть полезно, потому что он открывает файл и возвращает содержимое в виде массива строк. Каждая строка выбрасывается в findAndReplaceVars который обрабатывает переменные. Проанализированный контент затем возвращается.
Метод FindAndReplace
Этот метод является основной рабочей лошадкой нашего класса. Он находит определения переменных, сохраняет их значения в массиве. Когда переменная найдена, и если ее значение существует, она заменяется значением.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
private function findAndReplaceVars($line) {
preg_match_all(‘/\s*\\$([A-Za-z1-9_\-]+)(\s*:\s*(.*?);)?\s*/’, $line, $vars);
$found = $vars[0];
$varNames = $vars[1];
$varValues = $vars[3];
$count = count($found);
for($i = 0; $i < $count; $i++) {
$varName = trim($varNames[$i]);
$varValue = trim($varValues[$i]);
if ($varValue) {
$this->values[$varName] = $this->findAndReplaceVars($varValue);
} else if (isset($this->values[$varName])) {
$line = preg_replace(‘/\\$’.$varName.'(\W|\z)/’, $this->values[$varName].’\\1′, $line);
}
}
$line = str_replace($found, », $line);
return $line;
}
|
Здесь много кода. Давайте рассмотрим это подробно.
|
1
2
|
private function findAndReplaceVars($line) {
preg_match_all(‘/\s*\\$([A-Za-z1-9_\-]+)(\s*:\s*(.*?);)?\s*/’, $line, $vars);
|
Здесь мы применяем регулярное выражение к текущей строке. Это выражение соответствует и извлекает шаблоны, такие как $variable:$value; (и некоторые варианты) или $variable в текущей строке. Я не пойду дальше здесь. Регулярные выражения — сложная тема, которая заслуживает отдельного руководства. Функция Preg_match_all возвращает все совпадения.
Например, третья строка файла CSS нашего проекта —
$ main-color: # 3D7169; $ вторичный цвет: # 000;
— вернет этот массив:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
$vars => Array
(
[0] => Array
(
[0] => $main-color: #3D7169;
[1] => $secondary-color: #000;
)
[1] => Array
(
[0] => main-color
[1] => secondary-color
)
[2] => Array
(
[0] => : #3D7169;
[1] => : #000;
)
[3] => Array
(
[0] => #3D7169
[1] => #000
)
)
|
Мы предполагаем, что $vars[0] содержит полное совпадение, $vars[1] содержит имена переменных, а $vars[3] содержит значения. Давайте организуем массив, чтобы он был понятнее.
|
1
2
3
|
$found = $vars[0];
$varNames = $vars[1];
$varValues = $vars[3];
|
Теперь это кристалл.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
$found => Array
(
[0] => $main-color: #3D7169;
[1] => $secondary-color: #000;
)
$varNames => Array
(
[0] => main-color
[1] => secondary-color
)
$varValues => Array
(
[0] => #3D7169
[1] => #000
)
|
Мы посчитаем, сколько переменных было найдено в текущей строке.
|
1
|
$count = count($found);
|
Таким образом, мы можем циклически проходить через каждую запись нашего массива переменных. Чтобы прояснить ситуацию, мы установили несколько новых переменных для обработки имени и значения.
|
1
2
3
4
5
6
|
for($i = 0; $i < $count; $i++) {
$varName = trim($varNames[$i]);
$varValue = trim($varValues[$i]);
// …
}
|
Определения переменных
Если $varValue не пусто, мы сталкиваемся с определением переменной. Поэтому мы должны сохранить это значение в свойстве $this->values .
|
1
2
3
|
if ($varValue) {
$this->values[$varName] = $this->findAndReplaceVars($varValue);
} else if …
|
Обратите внимание, что мы передаем значение переменной в методе findAndReplaceVars снова. Таким образом, будут обрабатываться и другие потенциальные переменные.
Значения хранятся в массиве $this->values с именем переменной в качестве ключа. В конце скрипта массив $this->values выглядит следующим образом.
|
1
2
3
4
5
6
|
Array
(
[font] => arial, sans-serif
[main-color] => #3D7169
[secondary-color] => #000
)
|
Переменные Приложения
Если $varValue пусто, мы сталкиваемся с переменным приложением. Мы проверяем, существует ли эта переменная в массиве значений. Если это так, мы заменяем имя переменной ее значением.
|
1
2
3
|
} else if (isset($this->values[$varName])) {
$line = preg_replace(‘/\\$’.$varName.'(\W|\z)/’, $this->values[$varName].’\\1′, $line);
}
|
Эта замена может показаться слишком сложной. На самом деле нет, эта замена заботится о замене $variable только если за ней следует не символ (\ W) или конец строки (\ z).
Наконец, мы удаляем все совпадения, чтобы таблица стилей была чистой и действительной. Обработанная строка возвращается.
|
1
2
3
|
$line = str_replace($found, », $line);
return $line;
}
|
Метод отображения
Этот метод отображает проанализированную таблицу стилей. Для отображения в браузере в виде содержимого CSS в качестве заголовка text/css тип содержимого text/css .
|
1
2
3
4
|
public function display() {
header(‘Content-type: text/css’);
echo $this->parse();
}
|
Шаг 4 — Кэшируйте результат
На данный момент все работает отлично. Однако эта операция может быть очень трудоемкой при использовании с большими веб-сайтами.
В конце концов, нам не нужно анализировать CSS-файлы каждый раз, когда это требуется браузеру. Процесс должен быть запущен только в первый раз для создания кэша, или если исходный файл CSS был изменен с момента последней операции кэширования. В противном случае ранее визуализированный результат может быть использован повторно. Итак, давайте добавим решение для кэширования в наш скрипт.
Добавьте новую папку с именем cache в проект. При необходимости дайте этой папке право на запись, применив chmod 777 . Теперь наш проект должен выглядеть так:

Метод кеширования
Новый метод должен быть добавлен. Его функция будет заключаться в:
- прочитайте файл кеша, если он существует.
- создавать и хранить результаты.
- обновить существующий файл кэша, если файл CSS был изменен.
Вся логика обрабатывается методом ниже:
|
1
2
3
4
5
6
7
8
9
|
private function cache($content = false) {
$cacheFile = «cache/».urlencode($this->cssFile);
if (file_exists($cacheFile) && filemtime($cacheFile) > filemtime($this->cssFile)) {
return file_get_contents($cacheFile);
} else if ($content) {
file_put_contents($cacheFile, $content);
}
return $content;
}
|
Давайте объясним этот код. Имя файла кэша вычисляется из исходного имени файла CSS, ранее сохраненного в свойстве $this->cssFile . Наконец, мы используем функцию urlencode .
|
1
|
$cacheFile = «cache/».urlencode($this->cssFile);
|
Таким образом, CSS-файл как
/css/styles.css
будет кэшироваться как
/cache/css%2Fstyles.css
Нам нужно проверить, существует ли уже файл кеша ( file_exists ) и, если да, проверить, не находится ли дата его создания до даты изменения ( filemtime ) файла CSS.
|
1
2
|
if (file_exists($cacheFile) && filemtime($cacheFile) > filemtime($this->cssFile)) {
return file_get_contents($cacheFile);
|
В противном случае мы создаем / воссоздаем файл кэша.
|
1
2
3
|
} else if ($content) {
file_put_contents($cacheFile, $content);
}
|
Теперь остальная часть класса должна иметь дело с этим новым методом. Два метода должны быть изменены.
|
1
2
3
4
5
6
7
8
9
|
private function parse() {
if (!$content = $this->cache()) {
$lines = file($this->cssFile);
foreach($lines as $line) {
$content .= $this->findAndReplaceVars($line);
}
}
return $content;
}
|
Метод синтаксического анализа теперь проверяет кэш, прежде чем запускать весь процесс. Если нет доступного кэша, файл CSS анализируется, в противном случае возвращается кэшированное содержимое.
|
1
2
3
4
|
public function display() {
header(«Content-type: text/css»);
echo $this->cache($this->parse());
}
|
Наконец, метод отображает правильное содержимое (новое или кэшированное), предоставляемое методом кэширования.
Кэширование браузера
По соображениям безопасности (сеансы, динамическое содержимое) браузеры не сохраняют результаты PHP в своем кэше. Реальный файл CSS был бы
кешируется но не результат нашего скрипта. Мы должны иметь дело с браузером, чтобы эмулировать поведение реального файла CSS. Давайте добавим несколько строк в конструктор.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
public function __construct($cssFile) {
if (!file_exists($cssFile)) {
header(‘HTTP/1.0 404 Not Found’);
exit;
}
// Deals with the Browser cache
$modified = filemtime($cssFile);
header(‘Last-Modified: ‘.gmdate(«D, d MYH:i:s», $modified).’ GMT’);
if(isset($_SERVER[‘HTTP_IF_MODIFIED_SINCE’])) {
if (strtotime($_SERVER[‘HTTP_IF_MODIFIED_SINCE’]) == $modified) {
header(‘HTTP/1.1 304 Not Modified’);
exit();
}
}
$this->cssFile = $cssFile;
}
|
Мы копируем дату последнего изменения исходного CSS-файла в наш результат в заголовке.
Как правило, браузеры и серверы обмениваются заголовками перед передачей данных.
Когда браузер имеет копию страницы в своем кэше, он отправляет запрос HTTP_IF_MODIFIED_SINCE на сервер
с датой, указанной ранее в header('Last-Modified', ...) в день, когда он был кэширован. Если даты совпадают, содержание
обновлен и не требует перезагрузки; поэтому мы отправляем ответ 304 Not Modified и завершаем работу сценария.
Более коротким способом было бы просто добавить header('Cache-Control: max-age=3600'); в файл. Содержание будет кэшировано
1 час (3600 секунд) любым браузером.

Вывод
Выполнено! Теперь вы можете проверить одну из ваших таблиц стилей на вашем сервере. Например, http: //localhost/myproject/css/styles.css
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
$font: arial, sans-serif;
$main-color: #3D7169;
h1 {
font: 200% $font;
color: $main-color;
}
p {
background: $secondary-color;
color: $main-color;
font-family: $font;
padding: 10px;
}
|
Становится:
|
01
02
03
04
05
06
07
08
09
10
|
h1 {
font: 200% arial, sans-serif;
color: #3D7169;
}
p {
background: #000;
color: #3D7169;
font-family: arial, sans-serif;
padding: 10px;
}
|
Я надеюсь, вам понравился этот урок. о чем ты думаешь?