├── README └── ironcache ├── language └── english │ └── ironcache_lang.php ├── ironcache_Output.php └── ext.ironcache.php /README: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ironcache/language/english/ironcache_lang.php: -------------------------------------------------------------------------------- 1 | "caching configuration", 5 | "enabled"=> "ironcache configured?", 6 | "memcached_port" => "memcached port (default: 11211)", 7 | "memcached_host" => "memcached host (e.g.: localhost or 10.10.10.101 or memcached.local)", 8 | "prefix" => "prefix" 9 | ); 10 | 11 | /* End of file lang.safecracker_file.php */ 12 | /* Location: ./system/expressionengine/third_party/safecracker_file/language/english/lang.safecracker_file.php */ 13 | -------------------------------------------------------------------------------- /ironcache/ironcache_Output.php: -------------------------------------------------------------------------------- 1 | o = ($output) ? $output : $this->final_output; 28 | 29 | // ------------------------------------------- 30 | // 'output_start' hook. 31 | // - override output behavior 32 | $edata = $EE->extensions->call('output_start', $this); 33 | if ($EE->end_script === TRUE) return; 34 | // 35 | // ------------------------------------------- 36 | 37 | parent::_display($output); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /ironcache/ext.ironcache.php: -------------------------------------------------------------------------------- 1 | '', 'prefix' => '', 'memcached_host' => '', 'memcached_port' => '', 'config' => ''); 34 | 35 | 36 | /** URI **/ 37 | 38 | public $uri; 39 | public $uri_key; 40 | 41 | /** Keys **/ 42 | 43 | public $prefix; 44 | public $key_count; 45 | public $key_page; 46 | public $key_flag; 47 | 48 | /** Caching variables for the current request, with defaults**/ 49 | 50 | public $cache_time = 500; //in seconds 51 | public $counter_reset = 30; //in seconds 52 | public $threshold = 2; 53 | public $cacheable = false; 54 | 55 | 56 | /** count from cache **/ 57 | 58 | public $count; 59 | 60 | /**our memcache object**/ 61 | 62 | private $memcache = false; 63 | 64 | 65 | /** 66 | * Ironcache Constructor with settings 67 | */ 68 | public function __construct($settings='') 69 | { 70 | 71 | $this->EE =& get_instance(); 72 | $this->settings = ($settings) ? $settings : $this->settings; 73 | 74 | //if not provided, assume a port of 11211 -- memcache daemon listens to that port by default, but who knows 75 | $this->settings['memcached_port'] = (trim($this->settings['memcached_port'])) ? $this->settings['memcached_port'] : 11211; 76 | 77 | //load language file 78 | $this->EE->lang->loadfile('ironcache'); 79 | 80 | //get the URI 81 | $this->uri = ltrim($this->EE->uri->uri_string,'/'); 82 | $this->uri_key = md5($this->uri); 83 | 84 | //parse caching configuration, and while we're at it determine whether we should be cacheing the current request 85 | $this->_parse_config(); 86 | 87 | //set the key prefix 88 | $this->prefix = ($this->settings['prefix']) ? $this->settings['prefix'].'_' : ''; 89 | 90 | //build the keys for the count, page and flag 91 | $site_id = $this->EE->config->item('site_id') . '_'; 92 | $key_base = $this->prefix . $site_id . $this->uri_key; 93 | $this->key_count = $key_base . '_c'; 94 | $this->key_page = $key_base . '_p'; 95 | $this->key_flag = $key_base . '_f'; 96 | } 97 | 98 | 99 | /** 100 | * Ironcache Activate 101 | **/ 102 | 103 | public function activate_extension() 104 | { 105 | 106 | $data = array( 107 | 'class' => __CLASS__, 108 | 'method' => 'sessions_end', 109 | 'hook' => 'sessions_end', 110 | 'settings' => serialize($this->settings), 111 | 'priority' => 10, 112 | 'version' => $this->version, 113 | 'enabled' => 'y' 114 | ); 115 | 116 | $this->EE->db->insert('extensions', $data); 117 | 118 | $data = array( 119 | 'class' => __CLASS__, 120 | 'method' => 'output_start', 121 | 'hook' => 'output_start', 122 | 'settings' => serialize($this->settings), 123 | 'priority' => 10, 124 | 'version' => $this->version, 125 | 'enabled' => 'y' 126 | ); 127 | 128 | $this->EE->db->insert('extensions', $data); 129 | } 130 | 131 | /** 132 | * Ironcache Update 133 | **/ 134 | 135 | public function update_extension($current = '') 136 | { 137 | if ($current == '' OR $current == $this->version) 138 | { 139 | return FALSE; 140 | } 141 | 142 | if ($current < '0.1') 143 | { 144 | // Update to version 0.1 145 | } 146 | 147 | $this->EE->db->where('class', __CLASS__); 148 | $this->EE->db->update('extensions', array('version' => $this->version)); 149 | } 150 | 151 | /** 152 | * Ironcache Settings 153 | **/ 154 | 155 | public function settings() 156 | { 157 | $settings = array(); 158 | 159 | $settings['enabled'] = array('s', array('y' => 'yes', 'n' => 'no'), 'n'); 160 | $settings['memcached_host'] = array('i', '', ''); 161 | $settings['memcached_port'] = array('i', '', '11211'); 162 | $settings['prefix'] = array('i', '', ''); 163 | $settings['config'] = array('t', array('rows'=>20), ''); 164 | 165 | return $settings; 166 | } 167 | 168 | 169 | /** 170 | * Ironcache sessions_end 171 | * 172 | **/ 173 | 174 | public function sessions_end(&$sess) 175 | { 176 | 177 | //all of the following must be true to proceed 178 | // -cache must be enabled 179 | // -page must be cacheable 180 | // -there cannot be a logged-in session 181 | 182 | if (($this->settings['enabled']!='y') || ($this->cacheable!==true) || ($sess->userdata['username']!='')) { 183 | return true; 184 | } 185 | 186 | 187 | //increment the count for this page 188 | $this->_increment_count(); 189 | 190 | //see if we have valid page in cache already 191 | $page = $this->_get_from_cache($this->key_page); 192 | 193 | if (!$page) 194 | { 195 | 196 | //no page is in cache. If we're at or above the threshold, we flag for caching 197 | if($this->_get_from_cache($this->key_count) >= $this->threshold) 198 | { 199 | 200 | $this->_flag_for_caching(); 201 | 202 | 203 | //Since we now know that we'll require it, let's make sure there's a hook in our output object so that we can later cache this stuff 204 | require_once(PATH_THIRD . 'ironcache/ironcache_Output.php'); 205 | $this->EE->output = new Ironcache_Output; 206 | 207 | } 208 | 209 | //nothing left to do -- cya at the end of the pageload 210 | return true; 211 | } 212 | 213 | //ah, so we do have a valid page from the cache to show. Let's output it now. 214 | 215 | $this->_show_page($page); 216 | 217 | } 218 | 219 | 220 | /** 221 | * Ironcache output_start 222 | * 223 | **/ 224 | 225 | public function output_start(&$out) 226 | { 227 | 228 | 229 | if ($this->_is_flagged_for_caching()) { 230 | 231 | //cache the page 232 | $this->_cache_page($out); 233 | } 234 | 235 | return true; 236 | } 237 | 238 | /** 239 | * Ironcache _parse_config 240 | **/ 241 | 242 | private function _parse_config() 243 | { 244 | 245 | //four conditions must apply to proceed: 246 | // ironcache must not be disabled 247 | // we must be looking at a PAGE request and 248 | // $_POST must be empty 249 | // there must be a non-empty config 250 | if (($this->settings['enabled'] != 'y') || REQ != 'PAGE' || !empty($_POST) || !trim($this->settings['config'])) { 251 | return false; 252 | } 253 | 254 | //now we check the actual uri against the settings to determine if the request is cacheable 255 | 256 | $configs = explode(PHP_EOL, $this->settings['config']); 257 | 258 | foreach ($configs as $config) 259 | { 260 | $t = explode('||', trim($config)); 261 | 262 | //if the URI pattern of the config line is empty, it's referring to the homepage. 263 | if ($t[0]=='') 264 | { 265 | //the homepage will have an empty trimmed URI 266 | if ($this->uri == '') 267 | { 268 | $this->cacheable = true; 269 | } 270 | } 271 | 272 | elseif(preg_match('/' . $t[0] . '/', $this->uri)) 273 | { 274 | $this->cacheable = true; 275 | } 276 | }//foreach 277 | 278 | //now we know for sure if this request is cacheable or not. If it's not, bye bye. 279 | if (!$this->cacheable) return false; 280 | 281 | //so -- the request is cacheable. Let's set the rest of the settings from the config array 282 | $this->cache_time = (isset($t[1]) && !empty($t[1])) ? $t[1] : $this->cache_time; 283 | $this->counter_reset = (isset($t[2]) && !empty($t[2])) ? $t[2] : $this->counter_reset; 284 | $this->threshold = (isset($t[3]) && !empty($t[3])) ? $t[3] : $this->threshold; 285 | } 286 | 287 | 288 | /** 289 | * Ironcache _get_from_cache() 290 | **/ 291 | 292 | private function _get_from_cache($k) 293 | { 294 | $C = $this->_connect_to_cache(); 295 | return $C->get($k); 296 | } 297 | 298 | /** 299 | * Ironcache _increment_count 300 | **/ 301 | 302 | private function _increment_count() 303 | { 304 | 305 | $C = $this->_connect_to_cache(); 306 | 307 | //try to increment the count 308 | if($C->increment($this->key_count)) return TRUE; 309 | 310 | //still here? then we must have to set the count. 311 | $C->set($this->key_count,1,0,$this->counter_reset); 312 | 313 | return TRUE; 314 | } 315 | 316 | /** 317 | * Ironcache _flag_for_caching() 318 | **/ 319 | 320 | private function _flag_for_caching() 321 | { 322 | $C = $this->_connect_to_cache(); 323 | // this flag is set for 30 seconds, but really this is more than enough time, 324 | // since the flag only needs to live until the end of the current page request 325 | $C->set($this->key_flag,'true',0,30); 326 | } 327 | 328 | /** 329 | * Ironcache _is_flagged_for_caching() 330 | **/ 331 | 332 | private function _is_flagged_for_caching() 333 | { 334 | 335 | if ($this->_get_from_cache($this->key_flag)) { 336 | return true; 337 | } 338 | 339 | return false; 340 | } 341 | 342 | /** 343 | * Ironcache _connect_to_cache() 344 | **/ 345 | 346 | private function _connect_to_cache() 347 | { 348 | if (!$this->memcache) 349 | { 350 | $this->memcache = new Memcache; 351 | $this->memcache->connect($this->settings['memcached_host'],$this->settings['memcached_port']); 352 | } 353 | return $this->memcache; 354 | } 355 | 356 | /** 357 | * Ironcache _cache_page($out) 358 | **/ 359 | 360 | private function _cache_page(&$out) 361 | { 362 | //finally connect to and set the cache 363 | $C = $this->_connect_to_cache(); 364 | $result = $C->set($this->key_page,$out->o,0,$this->cache_time); 365 | return $result; 366 | } 367 | 368 | 369 | /** 370 | * Ironcache _show_page($page) 371 | **/ 372 | 373 | private function _show_page($page) 374 | { 375 | @header("HTTP/1.1 200 OK", TRUE, 200); 376 | @header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); 377 | @header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); 378 | @header("Pragma: no-cache"); 379 | @header('X-Ironcache: true'); 380 | 381 | // uncomment this to get visual indication that caching is working - otherwise look for X-Ironcache header 382 | //echo "THIS PAGE IS CACHED"; 383 | echo $page; 384 | exit; 385 | } 386 | } 387 | 388 | /* End of file ext.ironcache.php */ --------------------------------------------------------------------------------