~ overflow ~

Archive for June 21st, 2010

CSS Packer Reloaded

by z3n on Jun.21, 2010, under Coding, Tips & Hints

Sometime ago i posted a simple css packer which basically wipes out the whitespaces,  new lines and so on. The javascript packer was also included. Although none of those were 100% my creations i’ve improved the way it works by adding a couple of things:

- Caching based on timestamp on files (aka automatic cache expiry on update)

- Packing multiple files into a single one (ex. if you have 100s of jquery extensions you don’t need to pack them individually, packing everything into a single file is faster and smaller)

- Flexible layout for packing IE specific files

Source:

function css_specifics($fn) {
	global $_GET;
	if (isset($_GET['q'])) {
		return str_replace(".css",".".trim(str_replace(array("..","/"),"",$_GET['q'])).".css",$fn);
	} else {
		return $fn;
	}
}

base_defines(array(
	"css_packed" => _is_sadm ? "packed-adm.css" : "packed.css",												// css worked file output
	"css_include_path" => "css/",																											// css base include path
	"css_cache_path" => "css/cache/",																									// css cache path
	"css_compression" => -1,																													// css compression method, -1 = disabled
	"css_copyright" => "/* (c) z3n - www.overflow.biz - rodrigo.orph@gmail.com */\n\n", 	// css copyright notice, will appear on packed verisons
	"css_debug" => 0,
	"css_gzip" => 1
));

$css="";
$_use_cache=0;
$css_packed=css_specifics(css_packed);

$l=(file_exists(css_cache_path.$css_packed)) ? filemtime(css_cache_path.$css_packed) : 0;

if (isset($css_includes) && (!empty($css_includes))) {
	if ($l > 0) { // check last mod first
		$_use_cache=1;
		if (css_debug)
			echo "collected includes: ".var_export($css_includes,true)."\n";
		foreach ($css_includes as $v) {
			$fn=_fn_fix($v{0} == "/" || strpos($v,":\\") !== false || $v{0} == "\\" ? $v : css_include_path.$v);
			if (css_debug)
				echo "checking lastmod for: ".$v."\n";
			if (foo((substr($v,0,5) == "http:") ?
					getlastmod($fn)
				:
					filemtime($fn)
			) > $l) {
				$_use_cache=0;
				break;
			}
		}
	}

	if (!$_use_cache) { // re/pack
		if (css_debug)
			echo "building new cache\n";
		// load all js into one
		foreach ($css_includes as $v)
			$css.=substr($v,0,5) == "http:" ?
					file_get_contents($v)."\n"
				:
					_fs($v{0} == "/" || strpos($v,":\\") !== false || $v{0} == "\\" ? $v : css_include_path.$v)."\n"
			;

		// @Packager.RemoveLine -> nothing

		if (css_debug)
			echo "replacing..\n";
		// perform basic replaces
		$css=str_replace(array(
				"##SITE_FULL_URI##"
			),array(
				site_full_uri
		),$css);

		// compress
		switch (css_compression) {
			case "0": // css tidy - http://csstidy.sourceforge.net/
				require_once("classes/csstidy/class.csstidy.php");

				$x=new csstidy();
				$x->set_cfg('preserve_css',true);
				$x->set_cfg('remove_last_;',true);
				$x->set_cfg('merge_selectors',1);
				$x->set_cfg('optimise_shorthands',1);
				$x->set_cfg('silent',true);
				$x->set_cfg('compress_colors',true);
				$x->set_cfg('sort_selectors',false);
				$x->set_cfg('sort_properties',false);
				$x->set_cfg('discard_invalid_properties',false);
				$x->set_cfg('timestamp',false);
				$x->load_template("highest_compression");

				$x->parse(strip_comments($css));
				$css=$x->print->plain();

				break;
			case "1": // minify - http://www.lateralcode.com/css-minifier/
				$css = preg_replace( '#\s+#', ' ', $css );
				$css = preg_replace( '#/\*.*?\*/#s', '', $css );
				$css = str_replace( '; ', ';', $css );
				$css = str_replace( ': ', ':', $css );
				$css = str_replace( ' {', '{', $css );
				$css = str_replace( '{ ', '{', $css );
				$css = str_replace( ', ', ',', $css );
				$css = str_replace( '} ', '}', $css );
				$css = str_replace( ';}', '}', $css );
				break;
			default: // no compression/invalid
				// nothing!
				break;
		}

		$css=css_copyright.$css; // add copyright notice

		if (css_debug)
			echo "writing cache..\n";

		_fw(css_cache_path.$css_packed,$css,"w"); // save cache
	} else { // use cache
		if (css_debug)
			echo "loading from cache\n";
		$css=_fs(css_cache_path.$css_packed);
	}

	if (css_debug)
		echo "output\n - - - \n".$css;

	_opt_headers(gmdate(_dt_lm,filemtime(css_cache_path.$css_packed))." GMT"); // check/send 304

	header('Content-Type: text/css');

	if (css_gzip) // compress
		ob_start("ob_gzhandler");

	print $css; // final output
}

die;

Usage:

Configure files to be added by:

$css_includes=array(
    "file1.css",
    "file2.css",
    "fileN.css",
    "http://www.remote.com/style.css"
);

Do the link by:

<link rel="stylesheet" href="/lib/css.php" type="text/css">
<!--[if IE]><link rel="stylesheet" href="/lib/css.php?q=ie" type="text/css"><![endif]-->

Assuming you have the source into /lib/css.php and, on this case, you have an exception for all IEs.

Notes:

css_include_path = source of css (uncompressed, commented)
css_cache_path = path to save the compressed css
css_compression = method of compression , -1 no compression , 0 = css tidy, 1 = minify
css_debug = enables step by step debug
css_gzip = forces php to output as gzip

Required:

CSS Tidy

You may also need some functions of my framework, i will post them when i compile a decent common file.

Leave a Comment :, , , more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!