PageRenderTime 66ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/surosql/suracache/SuraCache.class.php

https://bitbucket.org/SuRaMoN/surosql
PHP | 213 lines | 179 code | 33 blank | 1 comment | 38 complexity | a46635817080e05cb173ddafaba2c073 MD5 | raw file
Possible License(s): LGPL-3.0, AGPL-1.0, GPL-2.0, LGPL-2.1
  1. <?php
  2. // version 0.2
  3. class SuraCache {
  4. const RECURSIVE = true;
  5. const NON_RECURSIVE = false;
  6. const CACHE_PREFIX = 'suracache_';
  7. static $inst;
  8. public $backend;
  9. public $cachestack = array();
  10. public $options;
  11. static function register($args = array()) {
  12. if(is_string($args)) {
  13. parse_str($args, $args);
  14. }
  15. return new SuraCache(array_merge(array('register_func' => true), $args));
  16. }
  17. function __construct($args = array()) {
  18. $defaults = array(
  19. 'backend' => 'SuraCacheTrivial',
  20. 'cache_timeout' => 60, // seconds
  21. 'cache_timeout_randomization' => 2, // seconds
  22. 'content_creation_timeout' => 5, // seconds
  23. 'recursive' => true,
  24. 'register_func' => false,
  25. );
  26. if(is_string($args)) {
  27. parse_str($args, $args);
  28. }
  29. $args = array_merge($defaults, $args);
  30. extract($args);
  31. if(is_string($backend)) {
  32. $backend_class_vars = get_class_vars($backend);
  33. $this->backend = is_null($backend_class_vars['instance']) ? new $backend() : $backend_class_vars['instance'];
  34. } else {
  35. $this->backend = $backend;
  36. }
  37. if($register_func) {
  38. self::$inst = $this;
  39. self::register_func();
  40. }
  41. $this->options = $args;
  42. }
  43. static function register_func() {
  44. function suracache() {
  45. return SuraCache::$inst;
  46. }
  47. }
  48. function add_key_parts($key_parts) {
  49. if(!is_array($key_parts)) {
  50. return $this->add_key_parts(array($key_parts));
  51. } else {
  52. for($i = count($this->cachestack) - 1; $i >= 0; $i--) {
  53. $this->cachestack[$i]['extra_key_parts'] = array_merge($this->cachestack[$i]['extra_key_parts'], $key_parts);
  54. if(!$this->cachestack[$i]['args']['recursive']) {
  55. break;
  56. }
  57. }
  58. }
  59. }
  60. static function variable($var, $key = null) {
  61. return (object)compact('var', 'key');
  62. }
  63. static function func($func) {
  64. return (object)compact('func');
  65. }
  66. static function evaluate($keys) {
  67. $evaled_keys = array();
  68. foreach($keys as $key) {
  69. if(is_string($key)) {
  70. $evaled_keys[] = $key;
  71. } else if(isset($key->var)) {
  72. $var = isset($GLOBALS[$key->var]) ? $GLOBALS[$key->var] : null;
  73. $evaled_keys[] = !is_null($key->key) && isset($var[$key->key]) ? $var[$key->key] : null;
  74. } else if(isset($key->func)) {
  75. $func = create_function('', $key->func);
  76. $evaled_keys[] = $func();
  77. }
  78. }
  79. return $evaled_keys;
  80. }
  81. function get_cache($key) {
  82. $hashed_key = self::CACHE_PREFIX . md5(serialize($key));
  83. $cache = $this->backend->get($hashed_key);
  84. $cache = is_null($cache) ? (object)array() : @unserialize($cache);
  85. $cache = $cache === false ? (object)array() : $cache;
  86. $cache->hashed_key = $hashed_key;
  87. $cache->is_expired = !isset($cache->expiration_date) || time() >= $cache->expiration_date;
  88. $cache->is_recreating = !(!isset($cache->creation_exp_date) || time() >= $cache->creation_exp_date);
  89. $cache->is_usable = !$cache->is_expired || $cache->is_recreating;
  90. return $cache;
  91. }
  92. function cache_var($key, $content, $args = array()) {
  93. if(is_string($args)) {
  94. parse_str($args, $args);
  95. }
  96. $args = array_merge($this->options, $args);
  97. extract($args);
  98. $cache = $this->get_cache($key);
  99. if($cache->is_usable && isset($cache->content)) {
  100. return $cache->content;
  101. }
  102. $expiration_date = time() + $cache_timeout + rand(0, (int)$cache_timeout_randomization);
  103. $this->backend->set($cache->hashed_key, serialize((object)compact('content', 'expiration_date')));
  104. }
  105. function get_var_status($key) {
  106. $cache = $this->get_cache($key);
  107. if($cache->is_usable && isset($cache->content)) {
  108. return (object)array('content' => $cache->content, 'cache_available' => true);
  109. } else {
  110. return (object)array('cache_available' => false);
  111. }
  112. }
  113. function get_var($key, $default = null) {
  114. $status = $this->get_var_status($key);
  115. return $status->cache_available ? $status->content : $default;
  116. }
  117. function start($key, $args = array()) {
  118. if(is_string($args)) {
  119. parse_str($args, $args);
  120. }
  121. $args = array_merge($this->options, $args);
  122. extract($args);
  123. $cache = $this->get_cache($key);
  124. if($cache->is_usable) {
  125. if(isset($cache->content)) {
  126. print($cache->content);
  127. return false;
  128. } else if(isset($cache->extra_key_parts)) {
  129. $indirect_key = array($cache->hashed_key, $cache->extra_key_parts, self::evaluate($cache->extra_key_parts));
  130. $indirect_cache = $this->get_cache($indirect_key, $args);
  131. if($indirect_cache->is_usable && isset($indirect_cache->content)) {
  132. print($indirect_cache->content);
  133. return false;
  134. }
  135. }
  136. }
  137. $this->cachestack[] = array('cache' => $cache, 'args' => $args, 'extra_key_parts' => array());
  138. if(!$cache->is_usable) {
  139. $value = (object)array('creation_exp_date' => time() + $content_creation_timeout);
  140. if(isset($cache->content)) {
  141. $value->content = $cache->content;
  142. }
  143. $this->backend->set($cache->hashed_key, serialize($value));
  144. }
  145. ob_start();
  146. return true;
  147. }
  148. static function array_sort_unique($arr) {
  149. usort($arr, create_function('$a,$b', 'return strcmp(serialize($a), serialize($b));'));
  150. $ret_arr = array();
  151. $last_ser_val = '';
  152. foreach($arr as $val) {
  153. $ser_val = serialize($val);
  154. if($ser_val !== $last_ser_val) {
  155. $ret_arr[] = $val;
  156. }
  157. $last_ser_val = $ser_val;
  158. }
  159. return $ret_arr;
  160. }
  161. function end() {
  162. extract(array_merge($this->options, array_pop($this->cachestack)));
  163. extract($args);
  164. $expiration_date = time() + $cache_timeout + rand(0, (int)$cache_timeout_randomization);
  165. $content = ob_get_flush();
  166. if(count($extra_key_parts) != 0) {
  167. $extra_key_parts = $cache->is_expired ? $extra_key_parts : array_merge($cache->extra_key_parts, $extra_key_parts);
  168. $extra_key_parts = self::array_sort_unique($extra_key_parts);
  169. $indirect_key = array($cache->hashed_key, $extra_key_parts, self::evaluate($extra_key_parts));
  170. if($cache->is_expired) {
  171. $this->backend->set($cache->hashed_key, serialize((object)compact('extra_key_parts', 'expiration_date')));
  172. }
  173. $hashed_indirect_key = self::CACHE_PREFIX . md5(serialize($indirect_key));
  174. $this->backend->set($hashed_indirect_key, serialize((object)compact('content', 'expiration_date')));
  175. } else {
  176. $this->backend->set($cache->hashed_key, serialize((object)compact('content', 'expiration_date')));
  177. }
  178. }
  179. }
  180. ?>