b2evolution PHP Cross Reference Blogging Systems

Source: /inc/_core/model/_blockcache.class.php - 296 lines - 8155 bytes - Summary - Text - Print

Description: This file implements the BlockCache class, which caches HTML blocks/snippets genereated by the app. This file is part of the evoCore framework - {@link http://evocore.net/} See also {@link http://sourceforge.net/projects/evocms/}.

   1  <?php
   2  /**
   3   * This file implements the BlockCache class, which caches HTML blocks/snippets genereated by the app.
   4   *
   5   * This file is part of the evoCore framework - {@link http://evocore.net/}
   6   * See also {@link http://sourceforge.net/projects/evocms/}.
   7   *
   8   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}
   9   * Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
  10   *
  11   * {@internal License choice
  12   * - If you have received this file as part of a package, please find the license.txt file in
  13   *   the same folder or the closest folder above for complete license terms.
  14   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
  15   *   then you must choose one of the following licenses before using the file:
  16   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  17   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  18   * }}
  19   *
  20   * {@internal Open Source relicensing agreement:
  21   * }}
  22   *
  23   * @package evocore
  24   *
  25   * @version $Id: _blockcache.class.php 6136 2014-03-08 07:59:48Z manuel $ }}}
  26   *
  27   */
  28  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  29  
  30  
  31  /**
  32   * Block Cache.
  33   *
  34   * @package evocore
  35   */
  36  class BlockCache
  37  {
  38      var $type;
  39      var $keys;
  40      var $serialized_keys = '';
  41  
  42      /**
  43       * After how many bytes should we output sth live while collecting cache content:
  44       */
  45      var $output_chunk_size = 2000;
  46  
  47      /**
  48       * Progressively caching the content of the current page:
  49       */
  50      var $cached_page_content = '';
  51      /**
  52       * Are we currently recording cache contents
  53       */
  54      var $is_collecting = false;
  55  
  56  
  57      /**
  58       * Constructor
  59       */
  60  	function BlockCache( $type, $keys )
  61      {
  62          $this->type = $type;
  63  
  64          // Make sure keys are always in the same order:
  65          ksort( $keys );
  66          $this->keys = $keys;
  67  
  68          $this->serialized_keys = $type;
  69          foreach( $keys as $key => $val )
  70          {
  71              $this->serialized_keys .= '+'.$key.'='.$val;
  72          }
  73  
  74          // echo $this->serialized_keys;
  75      }
  76  
  77  
  78  
  79      /**
  80       * Invalidate a special key
  81       *
  82       * All we do is store the timestamp of teh invalidation
  83       *
  84       */
  85  	function invalidate_key( $key, $val )
  86      {
  87          global $Debuglog, $servertimenow;
  88  
  89          $lastchanged_key_name = 'last_changed+'.$key.'='.$val;
  90  
  91          // Invalidate using the real time (seconds may have elapsed since $sertimenow)
  92          // Add 1 second because of teh granularity that's down to teh second
  93          // Worst case scenario: content will be collected/cahced several times for a whole second (as well as the first request after the end of that second)
  94          BlockCache::cacheproviderstore( $lastchanged_key_name, time()+1 );
  95  
  96          $Debuglog->add( 'Invalidated: '.$lastchanged_key_name.' @ '.(time()+1), 'blockcache' );
  97      }
  98  
  99  
 100      /**
 101       * Check if cache contents are available, otherwise start collecting output to be cached
 102       *
 103       * Basically we get all the invalidation dates we need, then we get the
 104       * data and then we check if some invalidation occured after the data was cached.
 105       * If an invalidation date is missing we consider the cache to be
 106       * obsolete but we generate a new invalidation date for next time we try to retrieve.
 107       *
 108       * @return true if we found and have echoed content from the cache
 109       */
 110  	function check()
 111      {
 112          global $Debuglog, $servertimenow;
 113  
 114          $missing_date = false;
 115          $most_recent_invalidation_ts = 0;
 116          $most_recent_invaliating_key = '';
 117          foreach( $this->keys as $key => $val )
 118          {
 119              $lastchanged_key_name = 'last_changed+'.$key.'='.$val;
 120              $last_changed_ts = $this->cacheproviderretrieve( $lastchanged_key_name, $success );
 121              if( ! $success )
 122              {    // We have lost the key! Recreate and keep going for other keys:
 123                  $Debuglog->add( 'Missing: '.$lastchanged_key_name, 'blockcache' );
 124                  $missing_date = true;
 125                  $this->cacheproviderstore( $lastchanged_key_name, $servertimenow );
 126                  continue;
 127              }
 128  
 129              if( $last_changed_ts > $most_recent_invalidation_ts )
 130              {    // This is the new most recent invalidation date.
 131                  $most_recent_invalidation_ts = $last_changed_ts;
 132                  $most_recent_invaliating_key = $lastchanged_key_name;
 133              }
 134          }
 135  
 136          if( !$missing_date && ($content = $this->retrieve( $most_recent_invalidation_ts, $most_recent_invaliating_key )) !== false )
 137          { // cache was not invalidated yet and we could retrieve:
 138              return $content;
 139          }
 140  
 141          // Caller should call BlockCache->start_collect() right after this
 142          return false;
 143      }
 144  
 145  
 146      /**
 147       * Retrieve and output cache
 148       *
 149       * @param integer oldest acceptable timestamp
 150       * @return boolean true if we could retrieve
 151       */
 152  	function retrieve( $oldest_acceptable_ts = NULL, $most_recent_invaliating_key = '' )
 153      {
 154          global $Debuglog;
 155          global $servertimenow;
 156  
 157          // return false;
 158  
 159          $content = $this->cacheproviderretrieve( $this->serialized_keys, $success );
 160  
 161          if( ! $success )
 162          {
 163              return false;
 164          }
 165  
 166          if( !is_null($oldest_acceptable_ts) )
 167          { // We want to do timestamp checking:
 168  
 169  
 170              //if( ! preg_match( '/^([0-9]+) (.*)$/ms', $content, $matches ) )
 171              if( ! $pos = strpos( $content, ' ' ) )
 172              {    // Could not find timestamp
 173                  $Debuglog->add( 'MISSING TIMESTAMP on retrieval of: '.$this->serialized_keys, 'blockcache' );
 174                  return false;
 175              }
 176  
 177              // if( $matches[1] < $oldest_acceptable_ts )
 178              if( ($cache_ts = substr( $content, 0, $pos )) < $oldest_acceptable_ts )
 179              {    // Timestamp too old (there has been an invalidation in between)
 180                  $Debuglog->add( 'Retrieved INVALIDATED cached content: '.$this->serialized_keys
 181                      .' (invalidated by '.$most_recent_invaliating_key.' - '.$cache_ts.' < '.$oldest_acceptable_ts.')', 'blockcache' );
 182                  return false;
 183              }
 184  
 185              // OK, we have content that is still valid:
 186              // $content = $matches[2];
 187              $content = substr( $content, $pos+1 );
 188          }
 189  
 190          $Debuglog->add( 'Retrieved: '.$this->serialized_keys, 'blockcache' );
 191  
 192          return $content;
 193      }
 194  
 195  
 196      /**
 197       * This is called every x bytes to provide real time output
 198       */
 199  	function output_handler( $buffer )
 200      {
 201          $this->cached_page_content .= $buffer;
 202          return $buffer;
 203      }
 204  
 205  
 206      /**
 207       * This should be called right after ::check() in order to start collecting a new block for the cache.
 208       */
 209  	function start_collect()
 210      {
 211          global $Debuglog;
 212  
 213          $this->is_collecting = true;
 214  
 215          $Debuglog->add( 'Collecting: '.$this->serialized_keys, 'blockcache' );
 216  
 217          ob_start( array( & $this, 'output_handler'), $this->output_chunk_size );
 218  
 219      }
 220  
 221  
 222      /**
 223       * We are going to output personal data and we want to abort collecting the data for the cache.
 224       */
 225  	function abort_collect()
 226      {
 227          global $Debuglog;
 228  
 229          if( ! $this->is_collecting )
 230          {    // We are not collecting anyway
 231              return;
 232          }
 233  
 234           $Debuglog->add( 'Aborting cache data collection...', 'blockcache' );
 235  
 236          ob_end_flush();
 237  
 238          // We are no longer collecting...
 239          $this->is_collecting = false;
 240      }
 241  
 242  
 243      /**
 244       * End collecting output to be cached
 245       *
 246       * We just concatenate all the individual keys to have a single one
 247       * Then we store with the current timestamp
 248       */
 249  	function end_collect()
 250      {
 251          global $Debuglog, $servertimenow;
 252  
 253          if( ! $this->is_collecting )
 254          {    // We are not collecting
 255              return;
 256          }
 257  
 258          ob_end_flush();
 259  
 260          // We use servertimenow because we may have used data that was loaded at the very start of this page
 261          $this->cacheproviderstore( $this->serialized_keys, $servertimenow.' '.$this->cached_page_content );
 262      }
 263  
 264  
 265      /**
 266       * Store payload/data into the cache provider.
 267       *
 268       * @todo dh> This method should get removed from here, it's not limited to BlockCache.
 269       * @param mixed $key
 270       * @param mixed $payload
 271       * @param int Time to live in seconds (default: 86400; 0 means "as long as possible")
 272       */
 273  	function cacheproviderstore( $key, $payload, $ttl = 86400 )
 274      {
 275          return set_to_mem_cache($key, $payload, $ttl);
 276      }
 277  
 278  
 279      /**
 280       * Fetch key from the cache provider.
 281       *
 282       * {@internal JFI: apc_fetch supports fetching an array of keys}}
 283       *
 284       * @todo dh> This method should get removed from here, it's not limited to BlockCache.
 285       * @todo dh> Add $default param, defaulting to NULL. This will be used on lookup failures.
 286       * @param mixed $key
 287       * @param boolean $success (by reference)
 288       */
 289  	function cacheproviderretrieve( $key, & $success )
 290      {
 291          return get_from_mem_cache($key, $success);
 292      }
 293  
 294  }
 295  
 296  ?>

title

Description

title

Description

title

Description

title

title

Body