jaylib
5/11/2009 - 7:49 PM

base64 Plugin for CSS Cacheer with support for IE6+

base64 Plugin for CSS Cacheer with support for IE6+

<?php
/******************************************************************************
 Prevent direct access
 ******************************************************************************/
if (!defined('CSS_CACHEER')) { header('Location:/'); }

/******************************************************************************
 Grab the modified CSS file
 ******************************************************************************/
$css = file_get_contents($relative_file);
$prepend_css = ""; // prepend to css before caching

// Pre-process for importers
foreach($plugins as $plugin)
{
	$css = $plugin->pre_process($css);
}

// Process for heavy lifting
foreach($plugins as $plugin)
{
	$css = $plugin->process($css);
}

// Post-process for formatters
foreach($plugins as $plugin)
{
	$css = $plugin->post_process($css);
}

/******************************************************************************/
$header  = '/* Processed and cached by Shaun Inman\'s CSS Cacheer';
$header .= ' (with '.str_replace('Plugin', '', preg_replace('#,([^,]+)$#', " &$1", join(', ', array_keys($plugins)))).' enabled)';
$header .= ' on '.gmdate('r').' <http://shauninman.com/search/?q=cacheer> */'."\r\n";
$css = $prepend_css.$header.$css;

/******************************************************************************
 Make sure the target directory exists
 ******************************************************************************/
if ($cached_file != $cached_dir && !is_dir($cached_dir))
{
	$path = $cssc_cache_dir;
	$dirs = explode('/', $relative_dir);
	foreach ($dirs as $dir)
	{
		$path .= '/'.$dir;
		mkdir($path, 0777);
	}
}

/******************************************************************************
 Cache parsed CSS
 ******************************************************************************/
$css_handle = fopen($cached_file, 'w');
fwrite($css_handle, $css);
fclose($css_handle);
chmod($cached_file, 0777);
touch($cached_file, $requested_mod_time);
<?php
error_reporting(0);
/******************************************************************************
 Used to prevent direct access to CSS Cacheer files
 ******************************************************************************/
define('CSS_CACHEER', true);

/******************************************************************************
 Received request from mod_rewrite
 ******************************************************************************/
// absolute path to requested file, eg. /css/nested/sample.css
$requested_file	= isset($_GET['cssc_request']) ? $_GET['cssc_request'] : '';
// absolute path to directory containing requested file, eg. /css/nested
$requested_dir	= preg_replace('#/[^/]*$#', '', $requested_file);
// absolute path to css directory, eg. /css
$css_dir = preg_replace('#/[^/]*$#', '', (isset($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_URL']);

/******************************************************************************
 Limit processing to existing css files within this and nested directories
 ******************************************************************************/
if 
(
	substr($requested_file, -4) != '.css' ||
	substr($requested_file, 0, strlen($css_dir)) != $css_dir ||
	!file_exists(substr($requested_file, strlen($css_dir) + 1))
)
{
	echo '/* Invalid Request */';
	exit();
}

/******************************************************************************
 Load plugins
 ******************************************************************************/
include('css-cacheer/plugin.php');
$flags = array();
$plugins = array();
$plugin_path = 'css-cacheer/plugins';
if (is_dir($plugin_path))
{
	if ($dir_handle = opendir($plugin_path)) 
	{
		while (($plugin_file = readdir($dir_handle)) !== false) 
		{
			if (substr($plugin_file, 0, 1) == '.' || substr($plugin_file, 0, 1) == '-')
			{ 
				continue; 
			}
			include($plugin_path.'/'.$plugin_file);
			if (isset($plugin_class) && class_exists($plugin_class))
			{
				$plugins[$plugin_class] = new $plugin_class($flags);
				$flags = array_merge($flags, $plugins[$plugin_class]->flags);
			}
		}
		closedir($dir_handle);
	}
}

/******************************************************************************
 Create hash of query string to allow variables to be cached
 ******************************************************************************/
$isIE = eregi("MSIE", $_SERVER['HTTP_USER_AGENT']);
$isIEOnVista = $isIE && eregi("Windows NT 6.0", $_SERVER['HTTP_USER_AGENT']);
$recache = isset($_GET['recache']);
$args = $flags;
ksort($args);
$checksum = md5(serialize($args.$isIE.)); // Gives two different checksums for IE and non-IE Browsers

/******************************************************************************
 Determine relative and cache paths
 ******************************************************************************/
$cssc_cache_dir = 'css-cacheer/cache/';
// path to requested file, relative to css directory, eg. nested/sample.css
$relative_file = substr($requested_file, strlen($css_dir) + 1);
// path to directory containing requested file, relative to css directory, eg. nested
$relative_dir = (strpos($relative_file, '/') === false) ? '' : preg_replace("/\/[^\/]*$/", '', $relative_file);
// path to cache of requested file, relative to css directory, eg. css-cacheer/cache/nested/sample.css
$cached_file = $cssc_cache_dir.preg_replace('#(.+)(\.css)$#i', "$1-{$checksum}$2", $relative_file);

// path to directory containing cache of requested CSS file, relative from the directory containing cache.php, eg. cache/nested
$cached_dir = $cssc_cache_dir.$relative_dir;

/******************************************************************************
 Delete file cache
 ******************************************************************************/
if ($recache && file_exists($cached_file))
{
	unlink($cached_file);
}

/******************************************************************************
 Get modified time for requested file and if available, its cache
 ******************************************************************************/
$requested_mod_time	= filemtime($relative_file);
$cached_mod_time	= (int) @filemtime($cached_file);
// cache may not exist, silence error with @

/******************************************************************************
 Recreate the cache if stale or nonexistent
 ******************************************************************************/
if ($cached_mod_time < $requested_mod_time)
{
	include_once('css-cacheer/index.php');	
}
/******************************************************************************
 Or send 304 header if appropriate
 ******************************************************************************/
else if 
(
	isset($_SERVER['HTTP_IF_MODIFIED_SINCE'], $_SERVER['SERVER_PROTOCOL']) && 
	$requested_mod_time <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])
)
{
	header("{$_SERVER['SERVER_PROTOCOL']} 304 Not Modified");
	exit();
}

/******************************************************************************
 Send cached file to browser, Due to security restrictions on Windows Vista, a
 MHTML document has to have the MIME type 'text/plain'.
 ******************************************************************************/

if ($isIEOnVista) 
{
	header('Content-type: text/plain');
} 
else 
{
	header('Content-type: text/css');
}
header("Vary: User-Agent, Accept");
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $requested_mod_time).' GMT');
@include($cached_file);
<?php
/******************************************************************************
 Prevent direct access
 ******************************************************************************/
if (!defined('CSS_CACHEER')) { header('Location:/'); }

$plugin_class = 'Base64Plugin';
class Base64Plugin extends CacheerPlugin
{
	function Base64Plugin()
	{
		if (isset($_SERVER['HTTP_USER_AGENT']))
		{
			$ua = $this->parse_user_agent($_SERVER['HTTP_USER_AGENT']);
			// Safari (WebKit), Firefox & Opera are known to support data: urls so embed base64-encoded images
			if
			(
				($ua['browser'] == 'applewebkit' && $ua['version'] >= 125) || // Safari and ilk
				($ua['browser'] == 'firefox') || // Firefox et al
				($ua['browser'] == 'opera' && $ua['version'] >= 7.2) || // quell vociferous Opera evangelists
				($ua['browser'] == 'ie' && $ua['version'] >= 6.0)
			)
			{
				$this->flags['Base64'] = true;
			}
		}
	}
	
	function post_process($css)
	{
			global $prepend_css;
			$ua = $this->parse_user_agent($_SERVER['HTTP_USER_AGENT']);
			if (isset($this->flags['Base64']))
		{
			global $requested_dir;
			$root_dir = $_SERVER['DOCUMENT_ROOT'];

			$images = array();
			if (preg_match_all('#url\(([^\)]+)\)#i', $css, $matches))
			{
				foreach($matches[1] as $relative_img)
				{
					if (!preg_match('#\.(gif|jpg|png)$#', $relative_img, $ext))
					{
						continue;
					}

					$images[$relative_img] = $ext[1];
				}
			}
				if ($ua['browser']!="ie") {
				foreach($images as $relative_img => $img_ext)
				{
					$up = substr_count($relative_img, '../');
					$absolute_img = $root_dir.preg_replace('#([^/]+/){'.$up.'}(\.\./){'.$up.'}#', '', $requested_dir.'/'.$relative_img);

					if (file_exists($absolute_img))
					{
						$img_raw = file_get_contents($absolute_img);
						$img_data = 'data:image/'.$img_ext.';base64,'.base64_encode($img_raw);
						$css = str_replace("url({$relative_img})", "url({$img_data})", $css);
					}
				}
			}
			else if ($ua['browser']=="ie") {
				
				$full_request_uri = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; // absolute url for the requested css file
				$delimiter = "DELIMITER_STRING"; // Delimiter for each base64 encoded image declaration
				$prepend_css .="/*\n".'Content-Type: multipart/related; boundary="'.$delimiter.'"'."\n\n"; // set the required Content-Type for mhtml
				foreach($images as $relative_img => $img_ext)
				{
					
					$up = substr_count($relative_img, '../');
					$absolute_img = $root_dir.preg_replace('#([^/]+/){'.$up.'}(\.\./){'.$up.'}#', '', $requested_dir.'/'.$relative_img);
					
					if (file_exists($absolute_img))
					{
						$img_raw = file_get_contents($absolute_img);
						$img_data = trim(base64_encode($img_raw));
						
						// Content-Location: ....
						$content_location = str_replace(array("/", "."), "", $relative_img);
						
						// url(mhtlm:http://......)
						$mhtml_uri = "mhtml:".$full_request_uri."!".$content_location;
						
						$css = str_replace("url({$relative_img})", "url({$mhtml_uri})", $css);
						
						// insert base64 encoded images into the css-file
						$prepend_css .= "--".$delimiter."\n";
						$prepend_css .= "Content-Location:".$content_location."\n";
						$prepend_css .= "Content-Transfer-Encoding:base64\n\n";
						$prepend_css .= $img_data."\n\n";					
					}
				}
			 			
				$prepend_css .= "*/\n\n"; // Close comment for the mhtml part

	}

	}
		return $css;

	}
	
	// really simple (read: imperfect) rendering engine detection
	function parse_user_agent($user_agent)
	{
		$ua['browser']	= '';
		$ua['version']	= 0;

		if (preg_match('/(firefox|opera|applewebkit)(?: \(|\/|[^\/]*\/| )v?([0-9.]*)/i', $user_agent, $m))
		{
			$ua['browser']	= strtolower($m[1]);
			$ua['version']	= $m[2];
		}
		else if (preg_match('/MSIE ?([0-9.]*)/i', $user_agent, $v) && !preg_match('/(bot|(?<!mytotal)search|seeker)/i', $user_agent))
		{
			$ua['browser']	= 'ie';
			$ua['version']	= $v[1];
		}
		return $ua;
	}
}