packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/smarty/sysplugins/smarty_cacheresource_keyvaluestore.php
changeset 0 3906ca745819
equal deleted inserted replaced
-1:000000000000 0:3906ca745819
       
     1 <?php
       
     2 /**
       
     3  * Smarty Internal Plugin
       
     4  *
       
     5  * @package Smarty
       
     6  * @subpackage Cacher
       
     7  */
       
     8 
       
     9 /**
       
    10  * Smarty Cache Handler Base for Key/Value Storage Implementations
       
    11  *
       
    12  * This class implements the functionality required to use simple key/value stores
       
    13  * for hierarchical cache groups. key/value stores like memcache or APC do not support
       
    14  * wildcards in keys, therefore a cache group cannot be cleared like "a|*" - which
       
    15  * is no problem to filesystem and RDBMS implementations.
       
    16  *
       
    17  * This implementation is based on the concept of invalidation. While one specific cache
       
    18  * can be identified and cleared, any range of caches cannot be identified. For this reason
       
    19  * each level of the cache group hierarchy can have its own value in the store. These values
       
    20  * are nothing but microtimes, telling us when a particular cache group was cleared for the
       
    21  * last time. These keys are evaluated for every cache read to determine if the cache has
       
    22  * been invalidated since it was created and should hence be treated as inexistent.
       
    23  *
       
    24  * Although deep hierarchies are possible, they are not recommended. Try to keep your
       
    25  * cache groups as shallow as possible. Anything up 3-5 parents should be ok. So
       
    26  * »a|b|c« is a good depth where »a|b|c|d|e|f|g|h|i|j|k« isn't. Try to join correlating
       
    27  * cache groups: if your cache groups look somewhat like »a|b|$page|$items|$whatever«
       
    28  * consider using »a|b|c|$page-$items-$whatever« instead.
       
    29  *
       
    30  * @package Smarty
       
    31  * @subpackage Cacher
       
    32  * @author Rodney Rehm
       
    33  */
       
    34 abstract class Smarty_CacheResource_KeyValueStore extends Smarty_CacheResource {
       
    35 
       
    36     /**
       
    37      * cache for contents
       
    38      * @var array
       
    39      */
       
    40     protected $contents = array();
       
    41     /**
       
    42      * cache for timestamps
       
    43      * @var array
       
    44      */
       
    45     protected $timestamps = array();
       
    46 
       
    47     /**
       
    48      * populate Cached Object with meta data from Resource
       
    49      *
       
    50      * @param Smarty_Template_Cached   $cached    cached object
       
    51      * @param Smarty_Internal_Template $_template template object
       
    52      * @return void
       
    53      */
       
    54     public function populate(Smarty_Template_Cached $cached, Smarty_Internal_Template $_template)
       
    55     {
       
    56         $cached->filepath = $_template->source->uid
       
    57                 . '#' . $this->sanitize($cached->source->name)
       
    58                 . '#' . $this->sanitize($cached->cache_id)
       
    59                 . '#' . $this->sanitize($cached->compile_id);
       
    60 
       
    61         $this->populateTimestamp($cached);
       
    62     }
       
    63 
       
    64     /**
       
    65      * populate Cached Object with timestamp and exists from Resource
       
    66      *
       
    67      * @param Smarty_Template_Cached $cached cached object
       
    68      * @return void
       
    69      */
       
    70     public function populateTimestamp(Smarty_Template_Cached $cached)
       
    71     {
       
    72         if (!$this->fetch($cached->filepath, $cached->source->name, $cached->cache_id, $cached->compile_id, $content, $timestamp, $cached->source->uid)) {
       
    73             return;
       
    74         }
       
    75         $cached->content = $content;
       
    76         $cached->timestamp = (int) $timestamp;
       
    77         $cached->exists = $cached->timestamp;
       
    78     }
       
    79 
       
    80     /**
       
    81      * Read the cached template and process the header
       
    82      *
       
    83      * @param Smarty_Internal_Template $_template template object
       
    84      * @param Smarty_Template_Cached $cached cached object
       
    85      * @return booelan true or false if the cached content does not exist
       
    86      */
       
    87     public function process(Smarty_Internal_Template $_template, Smarty_Template_Cached $cached=null)
       
    88     {
       
    89         if (!$cached) {
       
    90             $cached = $_template->cached;
       
    91         }
       
    92         $content = $cached->content ? $cached->content : null;
       
    93         $timestamp = $cached->timestamp ? $cached->timestamp : null;
       
    94         if ($content === null || !$timestamp) {
       
    95             if (!$this->fetch($_template->cached->filepath, $_template->source->name, $_template->cache_id, $_template->compile_id, $content, $timestamp, $_template->source->uid)) {
       
    96                 return false;
       
    97             }
       
    98         }
       
    99         if (isset($content)) {
       
   100             $_smarty_tpl = $_template;
       
   101             eval("?>" . $content);
       
   102             return true;
       
   103         }
       
   104         return false;
       
   105     }
       
   106 
       
   107     /**
       
   108      * Write the rendered template output to cache
       
   109      *
       
   110      * @param Smarty_Internal_Template $_template template object
       
   111      * @param string $content content to cache
       
   112      * @return boolean success
       
   113      */
       
   114     public function writeCachedContent(Smarty_Internal_Template $_template, $content)
       
   115     {
       
   116         $this->addMetaTimestamp($content);
       
   117         return $this->write(array($_template->cached->filepath => $content), $_template->properties['cache_lifetime']);
       
   118     }
       
   119 
       
   120     /**
       
   121      * Empty cache
       
   122      *
       
   123      * {@internal the $exp_time argument is ignored altogether }}
       
   124      *
       
   125      * @param Smarty  $smarty   Smarty object
       
   126      * @param integer $exp_time expiration time [being ignored]
       
   127      * @return integer number of cache files deleted [always -1]
       
   128      * @uses purge() to clear the whole store
       
   129      * @uses invalidate() to mark everything outdated if purge() is inapplicable
       
   130      */
       
   131     public function clearAll(Smarty $smarty, $exp_time=null)
       
   132     {
       
   133         if (!$this->purge()) {
       
   134             $this->invalidate(null);
       
   135         }
       
   136         return -1;
       
   137     }
       
   138 
       
   139     /**
       
   140      * Empty cache for a specific template
       
   141      *
       
   142      * {@internal the $exp_time argument is ignored altogether}}
       
   143      *
       
   144      * @param Smarty  $smarty        Smarty object
       
   145      * @param string  $resource_name template name
       
   146      * @param string  $cache_id      cache id
       
   147      * @param string  $compile_id    compile id
       
   148      * @param integer $exp_time      expiration time [being ignored]
       
   149      * @return integer number of cache files deleted [always -1]
       
   150      * @uses buildCachedFilepath() to generate the CacheID
       
   151      * @uses invalidate() to mark CacheIDs parent chain as outdated
       
   152      * @uses delete() to remove CacheID from cache
       
   153      */
       
   154     public function clear(Smarty $smarty, $resource_name, $cache_id, $compile_id, $exp_time)
       
   155     {
       
   156         $uid = $this->getTemplateUid($smarty, $resource_name, $cache_id, $compile_id);
       
   157         $cid = $uid . '#' . $this->sanitize($resource_name) . '#' . $this->sanitize($cache_id) . '#' . $this->sanitize($compile_id);
       
   158         $this->delete(array($cid));
       
   159         $this->invalidate($cid, $resource_name, $cache_id, $compile_id, $uid);
       
   160         return -1;
       
   161     }
       
   162     /**
       
   163      * Get template's unique ID
       
   164      *
       
   165      * @param Smarty $smarty        Smarty object
       
   166      * @param string $resource_name template name
       
   167      * @param string $cache_id      cache id
       
   168      * @param string $compile_id    compile id
       
   169      * @return string filepath of cache file
       
   170      */
       
   171     protected function getTemplateUid(Smarty $smarty, $resource_name, $cache_id, $compile_id)
       
   172     {
       
   173         $uid = '';
       
   174         if (isset($resource_name)) {
       
   175             $tpl = new $smarty->template_class($resource_name, $smarty);
       
   176             if ($tpl->source->exists) {
       
   177                 $uid = $tpl->source->uid;
       
   178             }
       
   179             
       
   180             // remove from template cache
       
   181             if ($smarty->allow_ambiguous_resources) {
       
   182                 $_templateId = $tpl->source->unique_resource . $tpl->cache_id . $tpl->compile_id;
       
   183             } else {
       
   184                 $_templateId = $smarty->joined_template_dir . '#' . $resource_name . $tpl->cache_id . $tpl->compile_id;
       
   185             }
       
   186             if (isset($_templateId[150])) {
       
   187                 $_templateId = sha1($_templateId);
       
   188             }
       
   189             unset($smarty->template_objects[$_templateId]);
       
   190         }
       
   191         return $uid;
       
   192     }
       
   193 
       
   194     /**
       
   195      * Sanitize CacheID components
       
   196      *
       
   197      * @param string $string CacheID component to sanitize
       
   198      * @return string sanitized CacheID component
       
   199      */
       
   200     protected function sanitize($string)
       
   201     {
       
   202         // some poeple smoke bad weed
       
   203         $string = trim($string, '|');
       
   204         if (!$string) {
       
   205             return null;
       
   206         }
       
   207         return preg_replace('#[^\w\|]+#S', '_', $string);
       
   208     }
       
   209 
       
   210     /**
       
   211      * Fetch and prepare a cache object.
       
   212      *
       
   213      * @param string  $cid           CacheID to fetch
       
   214      * @param string  $resource_name template name
       
   215      * @param string  $cache_id      cache id
       
   216      * @param string  $compile_id    compile id
       
   217      * @param string  $content       cached content
       
   218      * @param integer &$timestamp    cached timestamp (epoch)
       
   219      * @param string  $resource_uid  resource's uid
       
   220      * @return boolean success
       
   221      */
       
   222     protected function fetch($cid, $resource_name = null, $cache_id = null, $compile_id = null, &$content = null, &$timestamp = null, $resource_uid = null)
       
   223     {
       
   224         $t = $this->read(array($cid));
       
   225         $content = !empty($t[$cid]) ? $t[$cid] : null;
       
   226         $timestamp = null;
       
   227 
       
   228         if ($content && ($timestamp = $this->getMetaTimestamp($content))) {
       
   229             $invalidated = $this->getLatestInvalidationTimestamp($cid, $resource_name, $cache_id, $compile_id, $resource_uid);
       
   230             if ($invalidated > $timestamp) {
       
   231                 $timestamp = null;
       
   232                 $content = null;
       
   233             }
       
   234         }
       
   235 
       
   236         return !!$content;
       
   237     }
       
   238 
       
   239     /**
       
   240      * Add current microtime to the beginning of $cache_content
       
   241      *
       
   242      * {@internal the header uses 8 Bytes, the first 4 Bytes are the seconds, the second 4 Bytes are the microseconds}}
       
   243      *
       
   244      * @param string &$content the content to be cached
       
   245      */
       
   246     protected function addMetaTimestamp(&$content)
       
   247     {
       
   248         $mt = explode(" ", microtime());
       
   249         $ts = pack("NN", $mt[1], (int) ($mt[0] * 100000000));
       
   250         $content = $ts . $content;
       
   251     }
       
   252 
       
   253     /**
       
   254      * Extract the timestamp the $content was cached
       
   255      *
       
   256      * @param string &$content the cached content
       
   257      * @return float the microtime the content was cached
       
   258      */
       
   259     protected function getMetaTimestamp(&$content)
       
   260     {
       
   261         $s = unpack("N", substr($content, 0, 4));
       
   262         $m = unpack("N", substr($content, 4, 4));
       
   263         $content = substr($content, 8);
       
   264         return $s[1] + ($m[1] / 100000000);
       
   265     }
       
   266 
       
   267     /**
       
   268      * Invalidate CacheID
       
   269      *
       
   270      * @param string $cid           CacheID
       
   271      * @param string $resource_name template name
       
   272      * @param string $cache_id      cache id
       
   273      * @param string $compile_id    compile id
       
   274      * @param string $resource_uid  source's uid
       
   275      * @return void
       
   276      */
       
   277     protected function invalidate($cid = null, $resource_name = null, $cache_id = null, $compile_id = null, $resource_uid = null)
       
   278     {
       
   279         $now = microtime(true);
       
   280         $key = null;
       
   281         // invalidate everything
       
   282         if (!$resource_name && !$cache_id && !$compile_id) {
       
   283             $key = 'IVK#ALL';
       
   284         }
       
   285         // invalidate all caches by template
       
   286         else if ($resource_name && !$cache_id && !$compile_id) {
       
   287             $key = 'IVK#TEMPLATE#' . $resource_uid . '#' . $this->sanitize($resource_name);
       
   288         }
       
   289         // invalidate all caches by cache group
       
   290         else if (!$resource_name && $cache_id && !$compile_id) {
       
   291             $key = 'IVK#CACHE#' . $this->sanitize($cache_id);
       
   292         }
       
   293         // invalidate all caches by compile id
       
   294         else if (!$resource_name && !$cache_id && $compile_id) {
       
   295             $key = 'IVK#COMPILE#' . $this->sanitize($compile_id);
       
   296         }
       
   297         // invalidate by combination
       
   298         else {
       
   299             $key = 'IVK#CID#' . $cid;
       
   300         }
       
   301         $this->write(array($key => $now));
       
   302     }
       
   303 
       
   304     /**
       
   305      * Determine the latest timestamp known to the invalidation chain
       
   306      *
       
   307      * @param string $cid           CacheID to determine latest invalidation timestamp of
       
   308      * @param string $resource_name template name
       
   309      * @param string $cache_id      cache id
       
   310      * @param string $compile_id    compile id
       
   311      * @param string $resource_uid  source's filepath
       
   312      * @return float the microtime the CacheID was invalidated
       
   313      */
       
   314     protected function getLatestInvalidationTimestamp($cid, $resource_name = null, $cache_id = null, $compile_id = null, $resource_uid = null)
       
   315     {
       
   316         // abort if there is no CacheID
       
   317         if (false && !$cid) {
       
   318             return 0;
       
   319         }
       
   320         // abort if there are no InvalidationKeys to check
       
   321         if (!($_cid = $this->listInvalidationKeys($cid, $resource_name, $cache_id, $compile_id, $resource_uid))) {
       
   322             return 0;
       
   323         }
       
   324         
       
   325         // there are no InValidationKeys
       
   326         if (!($values = $this->read($_cid))) {
       
   327             return 0;
       
   328         }
       
   329         // make sure we're dealing with floats
       
   330         $values = array_map('floatval', $values);
       
   331         return max($values);
       
   332     }
       
   333 
       
   334     /**
       
   335      * Translate a CacheID into the list of applicable InvalidationKeys.
       
   336      *
       
   337      * Splits "some|chain|into|an|array" into array( '#clearAll#', 'some', 'some|chain', 'some|chain|into', ... )
       
   338      *
       
   339      * @param string $cid           CacheID to translate
       
   340      * @param string $resource_name template name
       
   341      * @param string $cache_id      cache id
       
   342      * @param string $compile_id    compile id
       
   343      * @param string $resource_uid  source's filepath
       
   344      * @return array list of InvalidationKeys
       
   345      * @uses $invalidationKeyPrefix to prepend to each InvalidationKey
       
   346      */
       
   347     protected function listInvalidationKeys($cid, $resource_name = null, $cache_id = null, $compile_id = null, $resource_uid = null)
       
   348     {
       
   349         $t = array('IVK#ALL');
       
   350         $_name = $_compile = '#';
       
   351         if ($resource_name) {
       
   352             $_name .= $resource_uid . '#' . $this->sanitize($resource_name);
       
   353             $t[] = 'IVK#TEMPLATE' . $_name;
       
   354         }
       
   355         if ($compile_id) {
       
   356             $_compile .= $this->sanitize($compile_id);
       
   357             $t[] = 'IVK#COMPILE' . $_compile;
       
   358         }
       
   359         $_name .= '#';
       
   360         // some poeple smoke bad weed
       
   361         $cid = trim($cache_id, '|');
       
   362         if (!$cid) {
       
   363             return $t;
       
   364         }
       
   365         $i = 0;
       
   366         while (true) {
       
   367             // determine next delimiter position
       
   368             $i = strpos($cid, '|', $i);
       
   369             // add complete CacheID if there are no more delimiters
       
   370             if ($i === false) {
       
   371                 $t[] = 'IVK#CACHE#' . $cid;
       
   372                 $t[] = 'IVK#CID' . $_name . $cid . $_compile;
       
   373                 $t[] = 'IVK#CID' . $_name . $_compile;
       
   374                 break;
       
   375             }
       
   376             $part = substr($cid, 0, $i);
       
   377             // add slice to list
       
   378             $t[] = 'IVK#CACHE#' . $part;
       
   379             $t[] = 'IVK#CID' . $_name . $part . $_compile;
       
   380             // skip past delimiter position
       
   381             $i++;
       
   382         }
       
   383         return $t;
       
   384     }
       
   385 
       
   386     /**
       
   387      * Check is cache is locked for this template
       
   388      *
       
   389      * @param Smarty $smarty Smarty object
       
   390      * @param Smarty_Template_Cached $cached cached object
       
   391      * @return booelan true or false if cache is locked
       
   392      */
       
   393     public function hasLock(Smarty $smarty, Smarty_Template_Cached $cached)
       
   394     {
       
   395         $key = 'LOCK#' . $cached->filepath;
       
   396         $data = $this->read(array($key));
       
   397         return $data && time() - $data[$key] < $smarty->locking_timeout;
       
   398     }
       
   399 
       
   400     /**
       
   401      * Lock cache for this template
       
   402      *
       
   403      * @param Smarty $smarty Smarty object
       
   404      * @param Smarty_Template_Cached $cached cached object
       
   405      */
       
   406     public function acquireLock(Smarty $smarty, Smarty_Template_Cached $cached)
       
   407     {
       
   408         $cached->is_locked = true;
       
   409         $key = 'LOCK#' . $cached->filepath;
       
   410         $this->write(array($key => time()), $smarty->locking_timeout);
       
   411     }
       
   412 
       
   413     /**
       
   414      * Unlock cache for this template
       
   415      *
       
   416      * @param Smarty $smarty Smarty object
       
   417      * @param Smarty_Template_Cached $cached cached object
       
   418      */
       
   419     public function releaseLock(Smarty $smarty, Smarty_Template_Cached $cached)
       
   420     {
       
   421         $cached->is_locked = false;
       
   422         $key = 'LOCK#' . $cached->filepath;
       
   423         $this->delete(array($key));
       
   424     }
       
   425 
       
   426     /**
       
   427      * Read values for a set of keys from cache
       
   428      *
       
   429      * @param array $keys list of keys to fetch
       
   430      * @return array list of values with the given keys used as indexes
       
   431      */
       
   432     protected abstract function read(array $keys);
       
   433 
       
   434     /**
       
   435      * Save values for a set of keys to cache
       
   436      *
       
   437      * @param array $keys   list of values to save
       
   438      * @param int   $expire expiration time
       
   439      * @return boolean true on success, false on failure
       
   440      */
       
   441     protected abstract function write(array $keys, $expire=null);
       
   442 
       
   443     /**
       
   444      * Remove values from cache
       
   445      *
       
   446      * @param array $keys list of keys to delete
       
   447      * @return boolean true on success, false on failure
       
   448      */
       
   449     protected abstract function delete(array $keys);
       
   450 
       
   451     /**
       
   452      * Remove *all* values from cache
       
   453      *
       
   454      * @return boolean true on success, false on failure
       
   455      */
       
   456     protected function purge()
       
   457     {
       
   458         return false;
       
   459     }
       
   460 
       
   461 }
       
   462 
       
   463 ?>