Статьи

Как добавить переменные в ваши файлы CSS

Давайте попробуем что-то другое на Nettuts + сегодня. Любой дизайнер, который работал с большими CSS-файлами, согласится, что его основным недостатком является невозможность использования переменных. В этой статье мы узнаем, как реализовать переменные с помощью PHP и мода переписывания URL Apache.


Эта техника несколько проста. Мы попросим Apache перенаправить любую таблицу стилей на определенный скрипт PHP. Этот скрипт откроет таблицу стилей и прочитает ее построчно, чтобы найти и заменить любые пользовательские переменные. Наконец, проанализированный контент будет отображаться как чистый CSS; браузеры не заметят разницы. Чтобы закрыть это руководство, мы также увидим, как кэшировать обработанный результат, чтобы избежать ненужного использования процессора.

Обратите внимание, что ожидаются некоторые базовые знания PHP (OOP), Apache и HTTP.

  • Apache с включенным модом Rewrite
  • PHP 5

Давайте сначала создадим нашу простую структуру проекта. Добавьте файл 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 . Этот последний файл переопределяет конфигурацию сервера по умолчанию и применяется к папке и ее подпапкам.

Теперь наш проект должен выглядеть так:


Мы хотим перенаправить любой 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’.

Например :

Будет перенаправлен на:

Некоторые хостинговые решения не позволяют переопределять их настройки пользователем. Если это так, ссылки на таблицы стилей в коде 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» />

Поскольку 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 который обрабатывает переменные. Проанализированный контент затем возвращается.

Этот метод является основной рабочей лошадкой нашего класса. Он находит определения переменных, сохраняет их значения в массиве. Когда переменная найдена, и если ее значение существует, она заменяется значением.

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 нашего проекта —

— вернет этот массив:

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();
}

На данный момент все работает отлично. Однако эта операция может быть очень трудоемкой при использовании с большими веб-сайтами.

В конце концов, нам не нужно анализировать 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-файл как

будет кэшироваться как

Нам нужно проверить, существует ли уже файл кеша ( 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;
}

Я надеюсь, вам понравился этот урок. о чем ты думаешь?