/phpmaster/06_performance/Memcache.php

https://bitbucket.org/tumivn/phpexamples · PHP · 227 lines · 98 code · 30 blank · 99 comment · 22 complexity · 52c01f042eb7ec480da610311d02542c MD5 · raw file

  1. <?php
  2. /**
  3. * Memcache Wrapper
  4. */
  5. /**
  6. * Memcache Wrapper
  7. *
  8. * Allows for partitioned cache
  9. * that can be cleared on a partition basis.
  10. *
  11. * Uses keys that consist of a partition, followed
  12. * by the current namespace key, followed by the
  13. * cached items key e.g. sql_128_$sha1ofquery
  14. */
  15. class Cache_Memcache {
  16. /**
  17. * @var bool Whether we are connected to at least one server in the pool
  18. */
  19. protected $connected = false;
  20. /**
  21. * @var Memcache
  22. */
  23. protected $memcache = null;
  24. protected $pool = array(
  25. array('host' => 'localhost', 'port' => '11211', 'weight' => 1),
  26. // Define other hosts here
  27. );
  28. /**
  29. * Constructor
  30. */
  31. public function __construct() {
  32. $this->connect();
  33. }
  34. public function isConnected()
  35. {
  36. return $this->connected;
  37. }
  38. /**
  39. * Connect to the memcached pool
  40. *
  41. * @return void
  42. */
  43. protected function connect() {
  44. $this->connected = false;
  45. $this->memcache = new Memcache();
  46. foreach($this->pool as $host) {
  47. $this->memcache->addServer($host['host'], $host['port'], true, $host['weight']);
  48. // Confirm that at least one server in the pool connected
  49. $stats = $this->memcache->getExtendedStats();
  50. if ($this->connected || ($stats["{$host['host']}:{$host['port']}"] !== false && sizeof($stats["{$host['host']}:{$host['port']}"]) > 0)) {
  51. $this->connected = true;
  52. }
  53. }
  54. return $this->connected;
  55. }
  56. /**
  57. * Returns the namespace value for the current partition
  58. *
  59. * This method will create a new namespace key for the current partition.
  60. *
  61. * To clear the cache for a specific partition of the cache, just increment
  62. * this key.
  63. *
  64. * @param string $key
  65. * @return string
  66. */
  67. protected function addNamespace($partition = '') {
  68. // If we're not connected, just return false
  69. if(!$this->connected) {
  70. return false;
  71. }
  72. // Get the current namespace key
  73. $ns_key = $this->memcache->get($partition);
  74. if($ns_key == false) {
  75. // No key currently set, set one at random
  76. $ns_key = rand(1, 10000);
  77. $result = $this->memcache->set($partition, $ns_key, 0, 0);
  78. }
  79. // Return the key with the naamespace key
  80. $my_key = $partition."_".$ns_key."_".$key;
  81. return $my_key;
  82. }
  83. /**
  84. * Clears the cache by incrementing the namespace key
  85. *
  86. * @return void
  87. */
  88. public function clearCache($partition = '') {
  89. if (!$this->connected) {
  90. return false;
  91. }
  92. // Memcache has a built in increment method
  93. $this->memcache->increment($partition);
  94. }
  95. /**
  96. * Add a value to the cache
  97. *
  98. * Will also add a metadata key
  99. * with modified date and split
  100. * large values (>=1MB) across
  101. * multiple keys automatically.
  102. *
  103. * @param string $key
  104. * @param string $value
  105. * @param int $expires
  106. * @return boolean
  107. */
  108. public function set($key, $value, $partition = '', $expires = 14400) {
  109. // Define a constant so we don't have a magic number
  110. define('ONE_MB', 1 * 1024 * 1024);
  111. if (!$this->connected) {
  112. return false;
  113. } elseif (strlen($value) >= ONE_MB) {
  114. // Value is more than 1MB, split it
  115. $value = str_split($value, ONE_MB);
  116. }
  117. // Set an expiration of now plus timeout
  118. if ($expires !== 0) {
  119. $expires += time();
  120. }
  121. // Add the partion and namespace key to our item key
  122. $ns_key = $this->addNameSpace($key, $partition);
  123. $this->memcache->set($ns_key.'_metadata', json_encode((object) array("modified" => gmdate('D, d M Y H:i:s') . ' GMT', 'slabs' => sizeof($value))), MEMCACHE_COMPRESSED, $expires);
  124. // If our value is split, we need to store it in mulitple keys
  125. if (is_array($value)) {
  126. foreach ($value as $k => $v) {
  127. // Add an incrementing number to the key and store the chunk
  128. $this->memcache->set($ns_key . '_' .$k, $v, MEMCACHE_COMPRESSED, $expires);
  129. }
  130. return true;
  131. }
  132. return $this->memcache->set($ns_key, $value, MEMCACHE_COMPRESSED, $expires);
  133. }
  134. /**
  135. * Returns the data for a given key.
  136. *
  137. * Returns false if no data exists.
  138. *
  139. * Automatically fetches the metadata key
  140. * and sends the Last-Modified header.
  141. *
  142. * Automatically retrieves large values split
  143. * across multiple slabs.
  144. *
  145. * Also sends an X-Cache-Hit header to indicate
  146. * if the item was found in the cache.
  147. *
  148. * @param string $key
  149. * @return string
  150. */
  151. public function get($key, $partition = '') {
  152. if (!$this->connected) {
  153. return false;
  154. }
  155. $ns_key = $this->addNameSpace($key, $partition);
  156. $meta = $this->memcache->get($ns_key.'_metadata');
  157. // Send appropriate headers
  158. if ($meta && !empty($meta) && !headers_sent()) {
  159. $meta = json_decode($meta);
  160. header("X-Cache-Hit: 1", false);
  161. if (isset($meta->modified)) {
  162. header('Last-Modified: ' .$meta->modified);
  163. }
  164. } elseif (!$meta && !headers_sent()) {
  165. header("X-Cache-Hit: 0", false);
  166. return false;
  167. }
  168. // Retrieve data split across multiple keys
  169. $value = '';
  170. if ($meta && isset($meta->slabs) && $meta->slabs > 1) {
  171. // Item is split across keys
  172. for ($i = 0; $i < $meta->slabs; $i++) {
  173. // Concat each key to the previously returned data
  174. $value .= $this->memcache->get($ns_key . '_' .$i);
  175. }
  176. } else {
  177. // Item is not split
  178. $value = $this->memcache->get($ns_key);
  179. }
  180. return $value;
  181. }
  182. /**
  183. * Deletes the data for a given key.
  184. *
  185. * Returns true on successful deletion, false if unsuccessful.
  186. *
  187. * @param string $key
  188. * @return boolean
  189. */
  190. public function delete($key, $partition = '') {
  191. if (!$this->connected) {
  192. return false;
  193. }
  194. return $this->memcache->delete($this->addNamespace($key, $partition));
  195. }
  196. }
  197. ?>