1<?php 2// +----------------------------------------------------------------------+ 3// | PEAR :: Cache | 4// +----------------------------------------------------------------------+ 5// | Copyright (c) 1997-2003 The PHP Group | 6// +----------------------------------------------------------------------+ 7// | This source file is subject to version 2.0 of the PHP license, | 8// | that is bundled with this package in the file LICENSE, and is | 9// | available at through the world-wide-web at | 10// | http://www.php.net/license/2_02.txt. | 11// | If you did not receive a copy of the PHP license and are unable to | 12// | obtain it through the world-wide-web, please send a note to | 13// | license@php.net so we can mail you a copy immediately. | 14// +----------------------------------------------------------------------+ 15// | Authors: Ulf Wendel <ulf.wendel@phpdoc.de> | 16// | Christian Stocker <chregu@phant.ch> | 17// +----------------------------------------------------------------------+ 18 19require_once 'Cache/Output.php'; 20 21/** 22* Cache using Output Buffering and contnet (gz) compression. 23** Usage example: 24* 25* // place this somewhere in a central config file 26* define(CACHE_STORAGE_CLASS, 'file'); 27* // file storage needs a dir to put the cache files 28* define(CACHE_DIR, '/var/tmp/'); 29* 30* // get a cache object 31* $cache = new Cache_Output(CACHE_STORAGE_CLASS, array('cache_dir' => CACHE_DIR)); 32* 33* if (!($content = $cache->start($cache->generateID($REQUEST_URI)))) { 34* print "hello world"; 35* $cache->endPrint(+1000); 36* } 37* else { 38* $cache->printContent(); 39* } 40* 41* OR 42* 43* if (($content = $cache->start($cache->generateID($REQUEST_URI)))) { 44* $cache->printContent(); 45* die(); 46* } 47* print "hello world"; 48* $cache->endPrint(+1000); 49* 50* 51* Based upon a case study from Christian Stocker and inspired by jpcache. 52* 53* @version $Id: OutputCompression.php 178289 2005-01-26 09:47:28Z dufuz $ 54* @author Ulf Wendel <ulf.wendel@phpdoc.de>, Christian Stocker <chregu@phant.ch> 55* @access public 56* @package Cache 57*/ 58class Cache_OutputCompression extends Cache_Output 59{ 60 61 /** 62 * Encoding, what the user (its browser) of your website accepts 63 * 64 * "auto" stands for test using $_SERVER['HTTP_ACCEPT_ENCODING']($HTTP_ACCEPT_ENCODING). 65 * 66 * @var string 67 * @see Cache_OutputCompression(), setEncoding() 68 */ 69 var $encoding = 'auto'; 70 71 72 /** 73 * Method used for compression 74 * 75 * @var string 76 * @see isCompressed() 77 */ 78 var $compression = ''; 79 80 81 /** 82 * Sets the storage details and the content encoding used (if not autodetection) 83 * 84 * @param string Name of container class 85 * @param array Array with container class options 86 * @param string content encoding mode - auto => test which encoding the user accepts 87 */ 88 function Cache_OutputCompression($container, $container_options = '', $encoding = 'auto') 89 { 90 $this->setEncoding($encoding); 91 $this->Cache($container, $container_options); 92 93 } // end constructor 94 95 96 /** 97 * Call parent deconstructor. 98 */ 99 function _Cache_OutputCompression() 100 { 101 $this->_Cache(); 102 } // end deconstructor 103 104 105 function generateID($variable) 106 { 107 $this->compression = $this->getEncoding(); 108 return md5(serialize($variable) . serialize($this->compression)); 109 } // end generateID 110 111 112 function get($id, $group) 113 { 114 $this->content = ''; 115 116 if (!$this->caching) { 117 return ''; 118 } 119 120 if ($this->isCached($id, $group) && !$this->isExpired($id, $group)) { 121 $this->content = $this->load($id, $group); 122 } 123 return $this->content; 124 } // end func get 125 126 127 /** 128 * Stops the output buffering, saves it to the cache and returns the _compressed_ content. 129 * 130 * If you need the uncompressed content for further procession before 131 * it's saved in the cache use endGet(). endGet() does _not compress_. 132 */ 133 function end($expire = 0, $userdata = '') 134 { 135 $content = ob_get_contents(); 136 ob_end_clean(); 137 138 // store in the cache 139 if ($this->caching) { 140 $this->extSave($this->output_id, $content, $userdata, $expire, $this->output_group); 141 return $this->content; 142 } 143 144 return $content; 145 } // end func end() 146 147 148 function endPrint($expire = 0, $userdata = '') 149 { 150 $this->printContent($this->end($expire, $userdata)); 151 } // end func endPrint 152 153 154 /** 155 * Saves the given data to the cache. 156 * 157 */ 158 function extSave($id, $cachedata, $userdata, $expires = 0, $group = 'default') 159 { 160 if (!$this->caching) { 161 return true; 162 } 163 164 if ($this->compression) { 165 $len = strlen($cachedata); 166 $crc = crc32($cachedata); 167 $cachedata = gzcompress($cachedata, 9); 168 $this->content = substr($cachedata, 0, strlen($cachedata) - 4) . pack('V', $crc) . pack('V', $len); 169 } else { 170 $this->content = $cachedata; 171 } 172 return $this->container->save($id, $this->content, $expires, $group, $userdata); 173 } // end func extSave 174 175 /** 176 * Sends the compressed data to the user. 177 * 178 * @param string 179 * @access public 180 */ 181 function printContent($content = '') 182 { 183 $server = &$this->_importGlobalVariable("server"); 184 185 if ($content == '') { 186 $content = &$this->container->cachedata; 187 } 188 189 if ($this->compression && $this->caching) { 190 $etag = '"PEAR-Cache-' . md5(substr($content, -40)) .'"'; 191 header("ETag: $etag"); 192 if (isset($server['HTTP_IF_NONE_MATCH']) && strstr(stripslashes($server['HTTP_IF_NONE_MATCH']), $etag)) { 193 // not modified 194 header('HTTP/1.0 304'); 195 return; 196 } else { 197 // client acceppts some encoding - send headers & data 198 header("Content-Encoding: {$this->compression}"); 199 header('Vary: Accept-Encoding'); 200 print "\x1f\x8b\x08\x00\x00\x00\x00\x00"; 201 } 202 203 } 204 205 die($content); 206 } // end func printContent 207 208 209 /** 210 * Returns the encoding method of the current dataset. 211 * 212 * @access public 213 * @return string Empty string (which evaluates to false) means no compression 214 */ 215 function isCompressed() 216 { 217 return $this->compression; 218 } // end func isCompressed 219 220 /** 221 * Sets the encoding to be used. 222 * 223 * @param string "auto" means autodetect for every client 224 * @access public 225 * @see $encoding 226 */ 227 function setEncoding($encoding = 'auto') 228 { 229 $this->encoding = $encoding; 230 } // end func setEncoding 231 232 233 /** 234 * Returns the encoding to be used for the data transmission to the client. 235 * 236 * @see setEncoding() 237 */ 238 function getEncoding() 239 { 240 $server = &$this->_importGlobalVariable("server"); 241 242 // encoding set by user 243 if ('auto' != $this->encoding) { 244 return $this->encoding; 245 } 246 // check what the client accepts 247 if (false !== strpos($server['HTTP_ACCEPT_ENCODING'], 'x-gzip')) { 248 return 'x-gzip'; 249 } 250 if (false !== strpos($server['HTTP_ACCEPT_ENCODING'], 'gzip')) { 251 return 'gzip'; 252 } 253 // no compression 254 return ''; 255 256 } // end func getEncoding 257 258 // {{{ _importGlobalVariable() 259 260 /** 261 * Import variables from special namespaces. 262 * 263 * @access private 264 * @param string Type of variable (server, session, post) 265 * @return array 266 */ 267 function &_importGlobalVariable($variable) 268 { 269 270 $var = null; 271 272 switch (strtolower($variable)) { 273 274 case 'server': 275 if (isset($_SERVER)) { 276 $var = &$_SERVER; 277 } else { 278 $var = &$GLOBALS['HTTP_SERVER_VARS']; 279 } 280 break; 281 282 case 'session': 283 if (isset($_SESSION)) { 284 $var = &$_SESSION; 285 } else { 286 $var = &$GLOBALS['HTTP_SESSION_VARS']; 287 } 288 break; 289 290 case 'post': 291 if (isset($_POST)) { 292 $var = &$_POST; 293 } else { 294 $var = &$GLOBALS['HTTP_POST_VARS']; 295 } 296 break; 297 298 default: 299 break; 300 301 } 302 303 return $var; 304 } 305 306 // }} 307} // end class OutputCompression 308?> 309