MemcachedCache.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. <?php
  2. namespace Doctrine\Common\Cache;
  3. use Memcached;
  4. use function array_keys;
  5. use function preg_match;
  6. use function strlen;
  7. use function strpos;
  8. use function time;
  9. /**
  10. * Memcached cache provider.
  11. *
  12. * @link www.doctrine-project.org
  13. */
  14. class MemcachedCache extends CacheProvider
  15. {
  16. public const CACHE_ID_MAX_LENGTH = 250;
  17. /** @var Memcached|null */
  18. private $memcached;
  19. /**
  20. * Sets the memcache instance to use.
  21. *
  22. * @return void
  23. */
  24. public function setMemcached(Memcached $memcached)
  25. {
  26. $this->memcached = $memcached;
  27. }
  28. /**
  29. * Gets the memcached instance used by the cache.
  30. *
  31. * @return Memcached|null
  32. */
  33. public function getMemcached()
  34. {
  35. return $this->memcached;
  36. }
  37. /**
  38. * {@inheritdoc}
  39. */
  40. protected function doFetch($id)
  41. {
  42. return $this->memcached->get($id);
  43. }
  44. /**
  45. * {@inheritdoc}
  46. */
  47. protected function doFetchMultiple(array $keys)
  48. {
  49. return $this->memcached->getMulti($keys) ?: [];
  50. }
  51. /**
  52. * {@inheritdoc}
  53. */
  54. protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
  55. {
  56. foreach (array_keys($keysAndValues) as $id) {
  57. $this->validateCacheId($id);
  58. }
  59. if ($lifetime > 30 * 24 * 3600) {
  60. $lifetime = time() + $lifetime;
  61. }
  62. return $this->memcached->setMulti($keysAndValues, $lifetime);
  63. }
  64. /**
  65. * {@inheritdoc}
  66. */
  67. protected function doContains($id)
  68. {
  69. $this->memcached->get($id);
  70. return $this->memcached->getResultCode() === Memcached::RES_SUCCESS;
  71. }
  72. /**
  73. * {@inheritdoc}
  74. */
  75. protected function doSave($id, $data, $lifeTime = 0)
  76. {
  77. $this->validateCacheId($id);
  78. if ($lifeTime > 30 * 24 * 3600) {
  79. $lifeTime = time() + $lifeTime;
  80. }
  81. return $this->memcached->set($id, $data, (int) $lifeTime);
  82. }
  83. /**
  84. * {@inheritdoc}
  85. */
  86. protected function doDeleteMultiple(array $keys)
  87. {
  88. return $this->memcached->deleteMulti($keys)
  89. || $this->memcached->getResultCode() === Memcached::RES_NOTFOUND;
  90. }
  91. /**
  92. * {@inheritdoc}
  93. */
  94. protected function doDelete($id)
  95. {
  96. return $this->memcached->delete($id)
  97. || $this->memcached->getResultCode() === Memcached::RES_NOTFOUND;
  98. }
  99. /**
  100. * {@inheritdoc}
  101. */
  102. protected function doFlush()
  103. {
  104. return $this->memcached->flush();
  105. }
  106. /**
  107. * {@inheritdoc}
  108. */
  109. protected function doGetStats()
  110. {
  111. $stats = $this->memcached->getStats();
  112. $servers = $this->memcached->getServerList();
  113. $key = $servers[0]['host'] . ':' . $servers[0]['port'];
  114. $stats = $stats[$key];
  115. return [
  116. Cache::STATS_HITS => $stats['get_hits'],
  117. Cache::STATS_MISSES => $stats['get_misses'],
  118. Cache::STATS_UPTIME => $stats['uptime'],
  119. Cache::STATS_MEMORY_USAGE => $stats['bytes'],
  120. Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'],
  121. ];
  122. }
  123. /**
  124. * Validate the cache id
  125. *
  126. * @see https://github.com/memcached/memcached/blob/1.5.12/doc/protocol.txt#L41-L49
  127. *
  128. * @param string $id
  129. *
  130. * @return void
  131. *
  132. * @throws InvalidCacheId
  133. */
  134. private function validateCacheId($id)
  135. {
  136. if (strlen($id) > self::CACHE_ID_MAX_LENGTH) {
  137. throw InvalidCacheId::exceedsMaxLength($id, self::CACHE_ID_MAX_LENGTH);
  138. }
  139. if (strpos($id, ' ') !== false) {
  140. throw InvalidCacheId::containsUnauthorizedCharacter($id, ' ');
  141. }
  142. if (preg_match('/[\t\r\n]/', $id) === 1) {
  143. throw InvalidCacheId::containsControlCharacter($id);
  144. }
  145. }
  146. }