gluon-web-remote

Web remote Administration for big size of gluon routers
git clone git://archive.git.mtrnord.blog/MTRNord/gluon-web-remote.git
Log | Files | Refs | README

smarty_cacheresource_keyvaluestore.php (17663B)


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