如何在CSS文件里加入变量

前言

这个技巧说来很简单。我们让Apache把任何stylesheet重定向到一个指定的PHP脚本。该脚本会一行行读取stylesheet的内容,找到并替换任何用户自定义的变量,最终会重新生成一个CSS样式表。浏览器不会发觉到有什么地方不同。在后面,我们会讨论如何缓存生成的最终结果避免加大CPU的负载。

请注意,这个教程需要读者懂得一些基本的PHPOOP)、ApacheHTTP知识。


系统要求:
  • ApacheRewrite mod 打开
  • PHP 5

 



第一步 创建项目工程

首先,建立一个简单的项目结构,在项目的根目录下创建一个index.html文件。

  1. <!DOCTYPE html PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN” ”http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
  2. <html xmlns=“http://www.w3.org/1999/xhtml”>
  3. <head>
  4. <meta http-equiv=“Content-Type” content=“text/html; charset=utf-8″ />
  5. <title>Variables in CSS Files… It’s possible!</title>
  6. <link href=“css/styles.css” rel=“stylesheet” type=“text/css” />
  7. </head>
  8. <body>
  9. <h1>Variables in Stylesheets</h1>
  10. <p>It’s possible!</p>
  11. <p>With PHP and Apache URL Rewrite Mod</p>
  12. </body>
  13. </html>

接着,创建一个CSS文件带有以下变量,保存在css目录下。

  1. $fontarialsans-serif;
  2. $main-color#3D7169; $secondary-color#000;
  3. h1 {
  4. font: 200% $font;
  5. color: $main-color;
  6. }
  7. p {
  8. background: $secondary-color;
  9. color: $main-color;
  10. font-family: $font;
  11. padding10px;
  12. }

最后,创建一个名为enhanced_css.php的空PHP文件和一个空.htaccess文件.htaccess 文件会覆盖服务器缺省的配置,并应用到各个子目录下去。

现在我们的项目看上去如下图所示:



第二步 重定向CSS文件到一个PHP脚本

我们希望把任何一个带有CSS后缀名的URL地址重定向到指定的PHP脚本。利用ApacheURL Rewrie模块可以实现这点。首先,确认rewrite_module模块已经打开。在Apache安装目录下找到httpd.conf文件。打开它,搜索下面这行代码:

  1. LoadModule rewrite_module modules/mod_rewrite.so

如果在前面有“#”号,把前面的“#”号去除,重新启动Apache以使设置生效。



接着,编辑.htaccess文件加入下面这些内容:

  1. RewriteEngine on
  2. RewriteRule ^(.*/.css)$ enhanced_css.php?css=$0

然后保存。如前面提到过的,上面这些内容就是让Apache捕捉所以带有.css后缀名的URL地址并重定向到enhanced_css.php。原CSS文件作为一个css参数对象传入。

例如:/css/styles.css 会被重定向到nhanced_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” />

第三步 通过PHP来分析CSS文件

因为CSS文件都被重定向到PHP脚本,我们要创建一个名为Enhancedcss的类来读取这些文件的内容,找到并替换变量,再生成纯CSS。通过传递$_GET['css']给构造函数来实例化这个类。记住.htaccess重定向访问请求后,通过$_GET可以获取当前stylesheet文件的路径。

  1. if (isset($_GET['css'])) {
  2. $cssnew EnhancedCss($_GET['css']);
  3. $css->display();
  4. }

基本实现由四个方法组成,最后再加上一个把结果缓存的方法。

  1. class EnhancedCss {
  2. public $values;
  3. public $cssFile;
  4. public function __construct($cssFile) {
  5. // check if the css file exists
  6. }
  7. private function parse() {
  8. // open the css file and throw every line to
  9. // findAndReplaceVars method
  10. }
  11. private function findAndReplaceVars($line) {
  12. // find the variable definitions, store the values,
  13. // replace the variable by their defined values.
  14. }
  15. public function display() {
  16. // display the new parsed content
  17. }
  18. }
构造函数

我们检查是否有CSS文件存在。如果没有,脚本返回一个404 http错误。CSS文件的路径保存在$this->cssFile属性里,后面要用来生成缓存文件的名字。

  1. public function __construct($cssFile) {
  2. if (!file_exists($cssFile)) {
  3. header(‘HTTP/1.0 404 Not Found’);
  4. exit;
  5. }
  6. $this->cssFile = $cssFile;
  7. }
分析方法

该方法打开CSS文件并一行行读取里面的内容。

  1. private function parse() {
  2. $content;
  3. $lines = file($this->cssFile);
  4. foreach($lines as $line) {
  5. $content .= $this->findAndReplaceVars($line);
  6. }
  7. return $content;
  8. }

这里用到了file 函数。该函数很有用,因为它打开一个文件并返回一个包含了每一行内容的数组对象。程序遍历每行内容,通过findAndReplaceVars这个方法来处理包含的变量。最后返回处理后的内容。

FindAndReplace方法

这个方法是最主要的部分。它找到定义好的变量后,把对应的值保存到一个数组里。当找到一个变量后,如果它对应的值已经存在,就用它的值替换已有值。

  1. private function findAndReplaceVars($line) {
  2. preg_match_all(‘//s*//$([A-Za-z1-9_/-]+)(/s*:/s*(.*?);)?/s*/’$line$vars);
  3. $found$vars[0];
  4. $varNames$vars[1];
  5. $varValues$vars[3];
  6. $countcount($found);
  7. for($i = 0; $i$count$i++) {
  8. $varName = trim($varNames[$i]);
  9. $varValue = trim($varValues[$i]);
  10. if ($varValue) {
  11. $this->values[$varName] = $this->findAndReplaceVars($varValue);
  12. else if (isset($this->values[$varName])) {
  13. $line = preg_replace(‘///$’.$varName.‘(/W|/z)/’$this->values[$varName].‘//1′$line);
  14. }
  15. }
  16. $linestr_replace($found$line);
  17. return $line;
  18. }

这里有很多代码,我们仔细一段段分析。

  1. private function findAndReplaceVars($line) {
  2. preg_match_all(‘//s*//$([A-Za-z1-9_/-]+)(/s*:/s*(.*?);)?/s*/’$line$vars);

这里,我们应用了正则表达式来处理当前行。这个表达式匹配当前行里所有像$variable:$value$variable的变量。在本教程里,我们不对正则表达式做深入的讨论。

例如:CSS文件里第三行

$main-color: #3D7169; $secondary-color: #000;

将返回以下这个数组对象:

  1. $vars => Array
  2. (
  3. [0] => Array
  4. (
  5. [0] => $main-color: #3D7169;
  6. [1] => $secondary-color: #000;
  7. )
  8. [1] => Array
  9. (
  10. [0] => main-color
  11. [1] => secondary-color
  12. )
  13. [2] => Array
  14. (
  15. [0] =>  : #3D7169;
  16. [1] =>  : #000;
  17. )
  18. [3] => Array
  19. (
  20. [0] =>  #3D7169
  21. [1] =>  #000
  22. )
  23. )

我们假设$vars[0]包含了全部匹配的变量,$vars[1]包含了所有变量的名称,$vars[3]包含了所有变量的值。让我们重新对它们做一下调整,

  1. $found$vars[0];
  2. $varNames$vars[1];
  3. $varValues$vars[3];

现在清晰多了。

  1. $found => Array
  2. (
  3. [0] => $main-color: #3D7169;
  4. [1] => $secondary-color: #000;
  5. )
  6. $varNames => Array
  7. (
  8. [0] => main-color
  9. [1] => secondary-color
  10. )
  11. $varValues => Array
  12. (
  13. [0] =>  #3D7169
  14. [1] =>  #000
  15. )

统计当前行找到多少个变量

  1. $countcount($found);

这样,我们就能遍历变量数组。为了更清晰一些,我们设置一些新的变量来处理名称和数值。

  1. for($i = 0; $i$count$i++) {
  2. $varName = trim($varNames[$i]);
  3. $varValue = trim($varValues[$i]);
  4. // …
  5. }
变量定义

如果$varValue值不为空,将面对一个定义变量的问题。因此把这个值保存到$this->values属性。

  1. if ($varValue) {
  2. $this->values[$varName] = $this->findAndReplaceVars($varValue);
  3. else if

注意,我们要再把变量值作为参数传递给findAndReplaceVars方法。这样的话,其他潜在的变量也会被处理。保存在$this->values数组的变量值用变量名称作为键值。最后,$this->values数组看上去像这样:

  1. Array
  2. (
  3. [font] => arial, sans-serif
  4. [main-color] => #3D7169
  5. [secondary-color] => #000
  6. )
变量应用

如果$var的值为空,则面对一个应用变量的问题。我们检查是否在变量值数组里存在该变量。如果存在,把替换变量替换为对应的值。

  1. else if (isset($this->values[$varName])) {
  2. $line = preg_replace(‘///$’.$varName.‘(/W|/z)/’$this->values[$varName].‘//1′$line);
  3. }

替换过程看上去异常复杂。实际上不是这样,替换过程仅仅替换$variable如果它后面跟着一个非字符(/W)或行结束符(/z)

最后,我们去除所有匹配的变量已使stylesheet干净和正确,最后返回处理后的内容。

  1. $linestr_replace($found$line);
  2. return $line;
  3. }
Display方法

该方法显示处理过的stylesheet。为了让浏览器识别CSS内容,把header设置为text/css content type.

  1. public function display() {
  2. header(‘Content-type: text/css’);
  3. echo $this->parse();
  4. }

第四步 缓存结果

到了这一点,所有事情都已很完美了。然而,当应用于大型网站时,该操作会很消耗CPU资源。

毕竟,当浏览器需要CSS文件的时候,我们不用每次都去处理它。只要第一次运行或CSS文件有修改时,生成一个缓存。

创建一个名为cache的目录。如有需要,执行chmod 777命令把该目录的权限设置为可写。现在我们的项目应该如下面所示:



Cache方法

加入一个新方法:

  • read the cache file if it exists.
  • create and store the rendered results.
  • update existing cache file if the CSS file has been modified.

所有逻辑按照下面方法处理:

  1. private function cache($content = false) {
  2. $cacheFile“cache/”.urlencode($this->cssFile);
  3. if (file_exists($cacheFile) && filemtime($cacheFile) > filemtime($this->cssFile)) {
  4. return file_get_contents($cacheFile);
  5. else if ($content) {
  6. file_put_contents($cacheFile$content);
  7. }
  8. return $content;
  9. }

我们解释一下这段代码。缓存文件名称是通过之前保存在$this->cssFile属性里面的CSS文件名计算出来的。最后,要用到urlencode函数。

  1. $cacheFile“cache/”.urlencode($this->cssFile);

我们需要检查是否缓存文件已经存在(file_exists)了。如果存在,继续检查缓存文件的创建时间是否比CSS文件的修改时间(filemtime)早。

  1. if (file_exists($cacheFile) && filemtime($cacheFile) > filemtime($this->cssFile)) {
  2. return file_get_contents($cacheFile);

否则,系统要新生成一个缓存文件。

  1. else if ($content) {
  2. file_put_contents($cacheFile$content);
  3. }

最后,要处理这个新方法。有两个方法需要做修改。

  1. private function parse() {
  2. if (!$content$this->cache()) {
  3. $lines = file($this->cssFile);
  4. foreach($lines as $line) {
  5. $content .= $this->findAndReplaceVars($line);
  6. }
  7. }
  8. return $content;
  9. }

Parse方法现在在运行前要检查缓存。如果没有有效的缓存,CSS文件就要被处理,否则直接返回缓存的内容。

  1. public function display() {
  2. header(“Content-type: text/css”);
  3. echo $this->cache($this->parse());
  4. }

最后,display方法显示了正确的内容(新的或缓存的)。

浏览器缓存

出于安全原因(sessions,动态内容)浏览器不能把PHP脚本产生的结果缓存到它的缓存里去。一个真实的CSS文件可能被缓存但不是我们代码所产生的结果。我们不得不让浏览器去模拟真实CSS文件的行为。我们在构造函数里加入一些行。

  1. public function __construct($cssFile) {
  2. if (!file_exists($cssFile)) {
  3. header(‘HTTP/1.0 404 Not Found’);
  4. exit;
  5. }
  6. // Deals with the Browser cache
  7. $modifiedfilemtime($cssFile);
  8. header(‘Last-Modified: ’.gmdate(“D, d M Y H:i:s”$modified).‘ GMT’);
  9. if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
  10. if (strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $modified) {
  11. header(‘HTTP/1.1 304 Not Modified’);
  12. exit();
  13. }
  14. }
  15. $this->cssFile = $cssFile;
  16. }

我们把原CSS文件的最后修改时间复制到header。基本上在传递数据前,headers被浏览器和服务器之间交换。当浏览器在它的缓存里有一页拷贝,它会发送一个HTTP_IF_MODIFIED_SINCE请求给服务器,带着之前由header(’Last-Modified’, …)保存的缓存日期。如果日期相匹配,内容是新的不需要重载。因此我们发送一个304不需要修改的响应,退出脚本。

有个简单的方法,在文件里加header(’Cache-Control: max-age=3600′)。内容会被任意一个浏览器缓存一个小时3600秒)。


结论

搞定!你现在可以在你的服务器上测试一下。例如:http://localhost/myproject/css/styles.css

  1. $fontarialsans-serif;
  2. $main-color#3D7169; $secondary-color#000;
  3. h1 {
  4. font: 200% $font;
  5. color: $main-color;
  6. }
  7. p {
  8. background: $secondary-color;
  9. color: $main-color;
  10. font-family: $font;
  11. padding10px;
  12. }

变成为

  1. h1 {
  2. font: 200% arialsans-serif;
  3. color#3D7169;
  4. }
  5. p {
  6. background#000;
  7. color#3D7169;
  8. font-familyarialsans-serif;
  9. padding10px;
  10. }

转自:http://blog.toeach.net/2009/03/19/how-to-add-variables-to-your-css-files/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值