CSS post-processing in PHP

Filed under: Php tips — vincent @ 13:08

While implementing the layout of this site in PHP, I found the tutorial on A List Apart, and wished to implement it here. However, I wanted to be able to change the column widths without having to recalculate manually the different dimensions.

I’ve then written a small PHP which post-process the CSS, and put a .htacces file in the CSS folder in order to ‘replace’ the original CSS by the postprocessed CSS.

Here is an extract from my CSS (you can find the full one here):

/** First define some values.
We put a special pattern at the beginning of the line to
identify it easily in PHP:
@!@ LC=180	# left column width
...
*/
...
/* The use it: */
  width: 180px;		/* @:@ width: ${LC}px; */

Let me explain the syntax choosen:

  • Lines beginning with @!@ are declarations. They will be processed, but the line will be output as-is.
  • Lines including @!@ are calculations. They will be calculated, and replaced by the result
  • Other lines are left untouched

Doing this, one I’m satisfied with the result, I can just take the output of the script, and my previous ’style.css’, and commented out my .htaccess. My CSS will again be a static CSS. If I uncommned the .htaccess, it will be dynamic again. In essence, the compiled version is also the source.

The PHP processing this is quite simple. I haven’t implemented a full expression evaluator, nor an efficient one: this is intended to be used for development, not for production:

<?php
$filename=$_REQUEST['file'];
// todo: check for path,...
if (preg_match("/[^a-zA-Z0-9_.]/",$file)) {
	die("Bad char in file name");
}
header('Content-type: text/css');
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past
$vars=array();
$warns=array();
function cb_def($m) {
	global $vars;
	$vars[$m[1]]=$m[2];
	return "$m[0]"; // doesn't modify the line!
}
function calc($e) {
	global $vars;
	if (isset($vars[$e])) return $vars[$e];
	if (preg_match('/^(d+)$/',$e,$m)) return $m[1];  // constant
	if (preg_match('/^(.*)([+-])(.*?)$/',$e,$m)) { // add, substr
		$x1=calc($m[1]);
		$x2=calc($m[3]);
		return($m[2]=='+'?$x1+$x2:$x1+$x2);
	}
	if (preg_match('/^(.*)(*)(.*?)$/',$e,$m)) { // multiplication
		return calc($m[1])*calc($m[3]);
	}
	global $warns;
	global $lineNo;
	$warns[]="Can't evaluate '$e' at line $lineNo";
	return 0;
}
$handle=fopen($file,"r");
if (!$handle) {
	print "/* CSS $filename doesn't exists */";
	exit;
}
$lineNo=0;
while(!feof($handle)) {
	$line=fgets($handle);
	$lineNo++;
	$re="/^s*@!@s*([a-zA-Z0-9_]+)s*=s*([0-9]+)s*(#.*)?$/";
	$line=preg_replace_callback($re,'cb_def',$line);
	if (preg_match('!/*s*@:@(.*)${(.*?)}(.*)*/(.*)$!',$line,$m)) {
		$line="$m[1]".calc($m[2])."$m[3]$m[0]";
	}
	print "$line";
}
fclose($handle);
if (count($warns)) {
	print("/* Warnings:n".implode("n",$warns)."n*/n");
}
?>

The last block of the puzzle is the .htacesss file

RewriteEngine on
Options +FollowSymLinks
RewriteRule style.css /wp/wp-content/themes/cipher/processCss.php?file=style.css

Now, I can change the declarations in my CSS file, and just refresh the page using that CSS. After much tweaking, once I’m satisfied, I open my CSS URL, copy the text, past it in my CSS file, and remove the .htacess. I’m back to a static CSS.

No Comments

No comments yet.

RSS feed for comments on this post. TrackBack URI

Sorry, the comment form is closed at this time.