Location: PHPKode > scripts > GzOutput > gzoutput/GzOutput.class4.php
<?php
/**
 * GzOutput class,
 *	simple class with some static method to create gz output without ob_gzhandler
 * ______________________________________________________________
 * @example
 * 	GzOutput::create('body{padding:0;}', 'text/css');			// css file
 *      	GzOutput::createNew('var something = "somevalue";', 'text/javascript');	// JS file without caching
 * 	GzOutput::createFromList(array('file.css', 'file2.css'), 'text/css');	// css file
 *	GzOutput::createNewFromList(array('file.css', 'file2.css'), 'text/css');	// css file without caching
 * --------------------------------------------------------------
 * @example using external "bootstrap.php" file (css example)
 *
 *	<link rel="stylesheet" href="css/bootstrap.css.php?base|tables|extra" media="screen" type="text/css" />
 *	
 * 	<?php // css/bootstrap.css.php
 *	require '../classes/GzOutput.class.php';
 *	GzOutput::createFromFile(__FILE__, 'text/css');
 *	// single css file with: [css/base.css, css/tables.css, css/extra.css]
 *	 ?>
 * Please read over createFromFile method to know more
 * --------------------------------------------------------------
 * @example using external configuration file (for createFromConfig static method)
 *	<?php
 *	// You can add or remove every "type" configuration but please don't change GzOutputClassFile and GlobalCacheFolder key names
 *	$config = array(
 *		
 *		// * relative path from this file
 *		// (i.e. 'javascript' if this file folder has a "javascript" folder, '../javascript/' if folder is before than this one)
 *		
 *		'GzOutputClassFile'	=> 'php/classes/GzOutput.class.php',	// relative GzOutput class file folder
 *		'GlobalCacheFolder'	=> 'cache',			// relative cache folder*
 *		
 *		'JavaScriptInformations'=> array(				// JavaScript configuration
 *			'Folder'		=> 'javascript',			// relative JavaScript folder*
 *			'Extension'	=> 'js',				// JavaScript extension
 *			'Type'		=> 'text/javascript',		// JavaScript Content-Typ,
 *			'Comments'	=> '// (C) Andrea Giammarchi',	// JavaScript Comments
 *			'Compression'	=> 9,				// JavaScript Compression level (0 - 9)
 *			'Crunch'		=> 2				// JavaScript Crunch level (0 - 3)
 *		),
 *		
 *		'CSSInformations'	=> array(				// CSS configuration
 *			'Folder'		=> 'css',			// relative CSS folder*
 *			'Extension'	=> 'css',			// CSS extension
 *			'Type'		=> 'text/css',			// CSS Content-Type
 *			'Comments'	=> '// (C) Andrea Giammarchi',	// CSS Comments
 *			'Compression'=> 9,				// CSS Compression level (0 - 9)
 *			'Crunch'	=> 3					// CSS Crunch level (0 - 3)
 *		)
 *	);
 *
 *	// please don't modify these two lines
 *	require $config['GzOutputClassFile'];
 *	GzOutput::createFromConfig($config);
 *	?>
 * --------------------------------------------------------------
 * @Compatibility	PHP >= 4 [requires zlib extension and gzencode function to work correctly or produced output will not be compressed]
 * @Dependencies	Crunch.class.php
 * @Author	Andrea Giammarchi
 * @Site		http://www.devpro.it/
 * @Date		2007/01/15
 * @Version	0.5 [new createFromCache and createFromConfig static methods, please view bootstrap.php example file to know more]
 */
define('GzOutput_ACCEPT_ENCODING', isset($_SERVER['HTTP_ACCEPT_ENCODING']));
define('GzOutput_GZIP', GzOutput_ACCEPT_ENCODING && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false);
define('GzOutput_DEFLATE', GzOutput_ACCEPT_ENCODING && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') !== false);
class GzOutput {

	/**
	 * public static method,
	 *	exit showing created output using a compression level
	 *
	 *	GzOutput::create(output:String, type:String [, level:UIntRange(0,9)]):Void(exit)
	 *
	 * @param	String		the content to show
	 * @param	String		the Content-Type (i.e. "text/css", "text/javascript", "text/html; charset:UTF-8")
	 * @param	UInt		compression level from 0 to 9, default: 9
	 */
	function create($output, $type, $level = 9) {
		$cache = !defined('GzOutput_NO_CACHE');
		$hash = GzOutput::__getGzHeader($output, $level);
		if(!empty($hash))
			header($hash);
		$hash = '"'.sha1($output).'"';
		if($cache) {
			header('ETag: '.$hash);
			header('Cache-Control: public');
		}
		if($cache && isset($_SERVER['HTTP_IF_NONE_MATCH']) && $hash === $_SERVER['HTTP_IF_NONE_MATCH']) {
			header('HTTP/1.1 304 Not Modified');
			$output = '';
		}
		else
			header('Content-Length: '.strlen($output));
		header('Content-Type: '.$type);
		exit($output);
	}
	
	/**
	 * public static method,
	 *	exit showing created output using a cache list of directories, a list of files, used type, compression level and crunch level.
	 *
	 *	GzOutput::createFromCache(dirs:Array, list:Array, type:String [, level:Uint [, crunch:UInt]]):Void(exit)
	 *
	 * @param	Array		associative array with relative paths. The cache key with its path is required. Other keys should be setted or not.
	 *			Exmple:	array('cache'=>'cache', 'javascript'=>'js')	// if You use this method to parse a list of javascript files
	 *				array('cache'=>'cache', 'css'=>'css')		// if You use this method to parse a list of css files
	 * @param	Array		one or more file to cache
	 * @param	String		the Content-Type (i.e. "text/css", "text/javascript", "text/html; charset:UTF-8")
	 * @param	UInt		gz compression level from 0 to 9, default: 9
	 * @param	UInt		Cruncher compression level from 0 to 3, default: 0 (no crunch)
	 */
	function createFromCache($dir, $list, $type, $comments = '', $level = 9, $crunch = 0){
		$etag = $ext = $hash = 'a';
		$fp = null;
		$lastmod = 0;
		$output = GzOutput::getRealType($type);
		if(!isset($dir[$output])) {
			header('HTTP/1.0 503 Not Implemented');
			$output = '';
		}
		else {
			foreach($list as $key => $value) {
				if(!file_exists($list[$key] = $dir[$output].'/'.$value)) {
					header('HTTP/1.0 404 Not Found');
					$output = '';
					$lastmod = 0;
					break;
				}
				else
					$lastmod = max(filemtime($list[$key]), $lastmod);
			}
			if($lastmod) {
				switch($ext = GzOutput::__getGzHeader($hash, $level)) {
					case	'Content-Encoding: gzip':
						header($ext);
						$ext = 'gzip';
						break;
					case	'Content-Encoding: deflate':
						header($ext);
						$ext = 'deflate';
						break;
					default:
						$ext = 'text';
						break;
				}
				$hash = $output.sha1(implode('|', $list)).$ext;
				$etag = '"'.$lastmod.$hash.'"';
				header('ETag: '.$etag);
				header('Cache-Control: public');
				if(isset($_SERVER['HTTP_IF_NONE_MATCH']) && $etag === $_SERVER['HTTP_IF_NONE_MATCH']) {
					header('HTTP/1.1 304 Not Modified');
					$output = '';
				}
				else {
					$dir['cache'] = $dir['cache'].'/'.$hash;
					switch(file_exists($dir['cache']) && filemtime($dir['cache']) === $lastmod){
						case	true:
							$output = file_get_contents($dir['cache']);
							break;
						default:
							$output = '';
							foreach($list as $value)
								$output .= trim(file_get_contents($value))."\r\n";
							$output = rtrim($output);
							if($crunch) {
								$fp = new Cruncher;
								$output = $fp->crunch($output, $crunch);
							}
							if(!empty($comments))
								$output = $comments."\r\n".$output;
							GzOutput::__getGzHeader($output, $level);
							if(@$fp = fopen($dir['cache'], 'wb')){
								@flock($fp, LOCK_EX );
								fwrite($fp, $output);
								@flock($fp, LOCK_UN);
								fclose($fp);
								@touch($dir['cache'], $lastmod, $lastmod);
								$fp = fopen($dir['cache'].'.txt', 'wb');
								@fwrite($fp, implode("\r\n\r\n", $list = array(
									"[CACHE FILE INFORMATIONS]\r\n\tcreated by GzOutput createFromCache public static method\r\n".
									"Date:	".gmdate('D, d M Y H:i:s')."\r\n".
									"File:	".__FILE__."\r\n".
									"___________________________________________",
									"[host]\r\n".$_SERVER['HTTP_HOST'],
									"[self]\r\n".$_SERVER['PHP_SELF'],
									"[query string]\r\n".$_SERVER['QUERY_STRING'],
									"[request]\r\n".$_SERVER['REQUEST_URI'],
									"[user agent]\r\n".$_SERVER['HTTP_USER_AGENT'],
									"[remote address]\r\n".$_SERVER['REMOTE_ADDR'],
									"[included files]\r\n".implode("\r\n", $list),
									"[file type]\t[compression]\t[crunch]\r\n{$ext}\t\t{$level}\t\t{$crunch}",
									"[last modification date]\r\n".gmdate('D, d M Y H:i:s', $lastmod),
									"[created hash]\r\n".$hash,
									"[returned etag]\r\n".$etag,
									"[user comments]\r\n".$comments
								)));
								fclose($fp);
							}
							GzOutput::forceNewContent();
							break;
					}
					header('Content-Length: '.strlen($output));
				}
			}
		}
		header('Content-Type: '.$type);
		exit($output);
	}
	
	/**
	 * public static method,
	 *	exit showing created output using a compression level
	 *
	 *	GzOutput::createFromConfig(*config:Array):Void(exit)
	 *
	 * @param	Array		configuration associative array
	 */
	function createFromConfig(&$config){
		$pos = strpos($_SERVER['REQUEST_URI'], '?');
		$ext = '';
		$list = array();
		if(
			$pos !== false && 
			isset($config['GlobalCacheFolder']) && 
			is_dir($config['GlobalCacheFolder'])
		) {
			$list = explode('|', substr($_SERVER['REQUEST_URI'], ++$pos));
			$ext = array_pop($list);
			foreach($config as $key => $value) {
				if($key !== 'GlobalCacheFolder' && $value['Extension'] === $ext) {
					GzOutput::createFromCache(
						array(
							'cache'					=> $config['GlobalCacheFolder'],
							GzOutput::getRealType($value['Type'])	=> $value['Folder']
						),
						array_map(create_function('$f', 'return str_replace(array("..", "./"), "", $f).".'.$ext.'";'), $list),
						$value['Type'],
						$value['Comments'],
						$value['Compression'],
						$value['Crunch']
					);
				}
			}
		}
	}
	
	/**
	 * public static method,
	 *	exit showing created output using an external "bootstrap.php" file
	 *
	 *	GzOutput::createFromFile(__FILE__:String [, type:String [, extension:String[, level:UIntRange(0,9) [, new:Boolean]]]]):Void(exit)
	 * @example
	 *	[bootstrap.php inside folder js]
	 *	<?php require '../classes/GzOutput.class.php'; GzOutput::createFromFile(__FILE__); ?>
	 *
	 *	[index.html]
	 *	<script type="text/javascript" src="js/bootstrap.php?jsfile1|jsfile2|otherjsFile|folder/jsFile3"></script>
	 *
	 * bootstrap.php file will read content of these files: js/jsfile1, js/jsfile2, js/otherjsFile.js, js/folder/jsFile3.js
	 *
	 * @param	String		external __FILE__ constant
	 * @param	String		the Content-Type (i.e. "text/css", "text/javascript", "text/html; charset:UTF-8"), default: 'text/javascript'
	 * @param	String		valid file extension, default: 'js'
	 * @param	UInt		compression level from 0 to 9, default: 9
	 * @param	Boolean		doesn't use cache or use it if present, default: false
	 */
	function createFromFile($__FILE__, $type = 'text/javascript', $ext = 'js', $level = 9, $new = false){
		if(($list = strpos($_SERVER['REQUEST_URI'], '?')) !== false)
			GzOutput::__listToOutput($list = array_map(
				create_function('$f', 'return "'.dirname($__FILE__).'/".$f.".'.$ext.'";'),
				explode('|', substr($_SERVER['REQUEST_URI'], ++$list))
			), $type, $level, $new);
	}
	
	/**
	 * public static method,
	 *	exit showing created output using an array with file names to read
	 *
	 *	GzOutput::createFromList(list:Array, type:String [, level:UIntRange(0,9)]):Void(exit)
	 *
	 * @param	Array		an array with one or more files to get contents to create the output
	 * @param	String		the Content-Type (i.e. "text/css", "text/javascript", "text/html; charset:UTF-8")
	 * @param	UInt		compression level from 0 to 9, default: 9
	 */
	function createFromList($list, $type, $level = 9) {
		GzOutput::__listToOutput($list, $type, $level, false);
        }

	/**
	 * public static method,
	 *	exit showing created output using a compression level and removing cache problems
	 *
	 *	GzOutput::createNew(output:String, type:String [, level:UIntRange(0,9)]):Void(exit)
	 *
	 * @param	String		the content to show
	 * @param	String		the Content-Type (i.e. "text/css", "text/javascript", "text/html; charset:UTF-8")
	 * @param	UInt		compression level from 0 to 9, default: 9
	 */
	function createNew($output, $type, $level = 9) {
		define('GzOutput_NO_CACHE', true);
		GzOutput::forceNewContent();
		call_user_func(array('GzOutput', 'create'), $output, $type, $level);
	}

	/**
	 * public static method,
	 *	exit showing created output using an array with file names to read and removing cache problems
	 *
	 *	GzOutput::createNewFromList(list:Array, type:String [, level:UIntRange(0,9)]):Void(exit)
	 *
	 * @param	Array		an array with one or more files to get contents to create the output
	 * @param	String		the Content-Type (i.e. "text/css", "text/javascript", "text/html; charset:UTF-8")
	 * @param	UInt		compression level from 0 to 9, default: 9
	 */
	function createNewFromList($list, $type, $level = 9) {
		GzOutput::__listToOutput($list, $type, $level, true);
        }
	
	/**
	 * public static method,
	 *	set headers to force download of new content
	 *
	 *	GzOutput::forceNewContent(Void):Void()
	 */
	function forceNewContent() {
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
		header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
		header('Cache-Control: no-store, no-cache, must-revalidate');
		header('Cache-Control: post-check=0, pre-check=0', false);
		header('Pragma: no-cache');
	}
	
	/**
	 * public static method,
	 *	parses a content-type string and returns the type ('css', 'javascript' ... others)
	 *
	 *	GzOutput::getRealType(*type:String):String
	 *
	 * @param	String		the Content-Type (i.e. "text/css", "text/javascript", "text/html; charset:UTF-8")
	 * @return	String		content type witout text , charset and other informations
	 */
	function getRealType(&$type){
		$realtype = substr($type, strpos($type, '/') + 1);
		$i = strpos($realtype, ';');
		if($i !== false)
			$realtype = substr($realtype, 0, $i);
		return trim($realtype);
	}
	
	/**
	 * private static method,
	 *	checks extensions loaded, gz capabilities, compress output and returns header value
	 *
	 *	GzOutput::__getGzHeader(*output:String, *level:UInt):String
	 *
	 * @param	String		the content to show
	 * @param	UInt		compression level from 0 to 9, default: 9
	 * @return	String		empty string or used gz compression header
	 */
	function __getGzHeader(&$output, &$level){
		$header = '';
		if(
			extension_loaded('zlib') &&
			function_exists('gzencode') && 
			(GzOutput_GZIP || GzOutput_DEFLATE)
		) {
			switch(GzOutput_GZIP) {
				case true:
					$output = gzencode($output, $level, FORCE_GZIP);
					$header = 'Content-Encoding: gzip';
					break;
				case false:
					$output = gzencode($output, $level, FORCE_DEFLATE);
					$header = 'Content-Encoding: deflate';
					break;
			}
		}
		return $header;
	}

	/**
	 * private static method,
	 *	creates an output looping over list array and reading each file content.
	 *
	 *	GzOutput::__listToOutput(*list:Array, *type:String, *level:UIntRange(0,9) [, nocache:Boolean]):Void(exit)
	 *
	 * @param	Array		an array with one or more files to get contents to create the output
	 * @param	String		the Content-Type (i.e. "text/css", "text/javascript", "text/html; charset:UTF-8")
	 * @param	UInt		compression level from 0 to 9, default: 9
	 * @param	Boolean		doesn't use cache or use it if present
	 */
	function __listToOutput(&$list, &$type, &$level, $new) {
		$create = $new ? 'createNew' : 'create';
		for($i = 0, $j = count($list); $i < $j; $i++)
			$list[$i] = file_exists($list[$i]) && is_readable($list[$i]) ? file_get_contents($list[$i]) : '';
		GzOutput::$create(implode("\r\n", $list), $type, $level);
	}
}
?>
Return current item: GzOutput