PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/Classes/TYPO3/FLOW3/Cache/Backend/ApcBackend.php

https://github.com/christianjul/FLOW3-Composer
PHP | 271 lines | 105 code | 22 blank | 144 comment | 13 complexity | 9bd4497b3da26ca3c4719291baeb8d6d MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0
  1. <?php
  2. namespace TYPO3\FLOW3\Cache\Backend;
  3. /* *
  4. * This script belongs to the FLOW3 framework. *
  5. * *
  6. * It is free software; you can redistribute it and/or modify it under *
  7. * the terms of the GNU Lesser General Public License, either version 3 *
  8. * of the License, or (at your option) any later version. *
  9. * *
  10. * The TYPO3 project - inspiring people to share! *
  11. * */
  12. /**
  13. * A caching backend which stores cache entries by using APC.
  14. *
  15. * This backend uses the following types of keys:
  16. * - tag_xxx
  17. * xxx is tag name, value is array of associated identifiers identifier. This
  18. * is "forward" tag index. It is mainly used for obtaining content by tag
  19. * (get identifier by tag -> get content by identifier)
  20. * - ident_xxx
  21. * xxx is identifier, value is array of associated tags. This is "reverse" tag
  22. * index. It provides quick access for all tags associated with this identifier
  23. * and used when removing the identifier
  24. * - tagIndex
  25. * Value is a List of all tags (array)
  26. *
  27. * Each key is prepended with a prefix. By default prefix consists from two parts
  28. * separated by underscore character and ends in yet another underscore character:
  29. * - "FLOW3"
  30. * - MD5 of SAPI type, path to FLOW3 and user running FLOW3
  31. * This prefix makes sure that keys from the different installations do not
  32. * conflict.
  33. *
  34. * @api
  35. */
  36. class ApcBackend extends AbstractBackend implements TaggableBackendInterface {
  37. /**
  38. * A prefix to seperate stored data from other data possible stored in the APC
  39. * @var string
  40. */
  41. protected $identifierPrefix;
  42. /**
  43. * Constructs this backend
  44. *
  45. * @param \TYPO3\FLOW3\Core\ApplicationContext $context FLOW3's application context
  46. * @param array $options Configuration options - unused here
  47. * @throws \TYPO3\FLOW3\Cache\Exception
  48. */
  49. public function __construct(\TYPO3\FLOW3\Core\ApplicationContext $context, array $options = array()) {
  50. if (!extension_loaded('apc')) {
  51. throw new \TYPO3\FLOW3\Cache\Exception('The PHP extension "apc" must be installed and loaded in order to use the APC backend.', 1232985414);
  52. }
  53. parent::__construct($context, $options);
  54. }
  55. /**
  56. * Initializes the identifier prefix when setting the cache.
  57. *
  58. * @param \TYPO3\FLOW3\Cache\Frontend\FrontendInterface $cache
  59. * @return void
  60. */
  61. public function setCache(\TYPO3\FLOW3\Cache\Frontend\FrontendInterface $cache) {
  62. parent::setCache($cache);
  63. $processUser = extension_loaded('posix') ? posix_getpwuid(posix_geteuid()) : array('name' => 'default');
  64. $pathHash = substr(md5(FLOW3_PATH_WEB . PHP_SAPI . $processUser['name'] . $this->context), 0, 12);
  65. $this->identifierPrefix = 'FLOW3_' . $pathHash;
  66. }
  67. /**
  68. * Saves data in the cache.
  69. *
  70. * @param string $entryIdentifier An identifier for this specific cache entry
  71. * @param string $data The data to be stored
  72. * @param array $tags Tags to associate with this cache entry
  73. * @param integer $lifetime Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited liftime.
  74. * @return void
  75. * @throws \TYPO3\FLOW3\Cache\Exception if no cache frontend has been set.
  76. * @throws \InvalidArgumentException if the identifier is not valid
  77. * @throws \TYPO3\FLOW3\Cache\Exception\InvalidDataException if $data is not a string
  78. * @api
  79. */
  80. public function set($entryIdentifier, $data, array $tags = array(), $lifetime = NULL) {
  81. if (!$this->cache instanceof \TYPO3\FLOW3\Cache\Frontend\FrontendInterface) {
  82. throw new \TYPO3\FLOW3\Cache\Exception('No cache frontend has been set yet via setCache().', 1232986818);
  83. }
  84. if (!is_string($data)) {
  85. throw new \TYPO3\FLOW3\Cache\Exception\InvalidDataException('The specified data is of type "' . gettype($data) . '" but a string is expected.', 1232986825);
  86. }
  87. $tags[] = '%APCBE%' . $this->cacheIdentifier;
  88. $expiration = $lifetime !== NULL ? $lifetime : $this->defaultLifetime;
  89. $success = apc_store($this->identifierPrefix . $entryIdentifier, $data, $expiration);
  90. if ($success === TRUE) {
  91. $this->removeIdentifierFromAllTags($entryIdentifier);
  92. $this->addIdentifierToTags($entryIdentifier, $tags);
  93. } else {
  94. throw new \TYPO3\FLOW3\Cache\Exception('Could not set value.', 1232986877);
  95. }
  96. }
  97. /**
  98. * Loads data from the cache.
  99. *
  100. * @param string $entryIdentifier An identifier which describes the cache entry to load
  101. * @return mixed The cache entry's content as a string or FALSE if the cache entry could not be loaded
  102. * @api
  103. */
  104. public function get($entryIdentifier) {
  105. $success = FALSE;
  106. $value = apc_fetch($this->identifierPrefix . $entryIdentifier, $success);
  107. return ($success ? $value : $success);
  108. }
  109. /**
  110. * Checks if a cache entry with the specified identifier exists.
  111. *
  112. * @param string $entryIdentifier An identifier specifying the cache entry
  113. * @return boolean TRUE if such an entry exists, FALSE if not
  114. * @api
  115. */
  116. public function has($entryIdentifier) {
  117. $success = FALSE;
  118. apc_fetch($this->identifierPrefix . $entryIdentifier, $success);
  119. return $success;
  120. }
  121. /**
  122. * Removes all cache entries matching the specified identifier.
  123. * Usually this only affects one entry but if - for what reason ever -
  124. * old entries for the identifier still exist, they are removed as well.
  125. *
  126. * @param string $entryIdentifier Specifies the cache entry to remove
  127. * @return boolean TRUE if (at least) an entry could be removed or FALSE if no entry was found
  128. * @api
  129. */
  130. public function remove($entryIdentifier) {
  131. $this->removeIdentifierFromAllTags($entryIdentifier);
  132. return apc_delete($this->identifierPrefix . $entryIdentifier);
  133. }
  134. /**
  135. * Finds and returns all cache entry identifiers which are tagged by the
  136. * specified tag.
  137. *
  138. * @param string $tag The tag to search for
  139. * @return array An array with identifiers of all matching entries. An empty array if no entries matched
  140. * @api
  141. */
  142. public function findIdentifiersByTag($tag) {
  143. $success = FALSE;
  144. $identifiers = apc_fetch($this->identifierPrefix . 'tag_' . $tag, $success);
  145. if ($success === FALSE) {
  146. return array();
  147. } else {
  148. return (array) $identifiers;
  149. }
  150. }
  151. /**
  152. * Finds all tags for the given identifier. This function uses reverse tag
  153. * index to search for tags.
  154. *
  155. * @param string $identifier Identifier to find tags by
  156. * @return array Array with tags
  157. */
  158. protected function findTagsByIdentifier($identifier) {
  159. $success = FALSE;
  160. $tags = apc_fetch($this->identifierPrefix . 'ident_' . $identifier, $success);
  161. return ($success ? (array)$tags : array());
  162. }
  163. /**
  164. * Removes all cache entries of this cache.
  165. *
  166. * @return void
  167. * @throws \TYPO3\FLOW3\Cache\Exception
  168. * @api
  169. */
  170. public function flush() {
  171. if (!$this->cache instanceof \TYPO3\FLOW3\Cache\Frontend\FrontendInterface) {
  172. throw new \TYPO3\FLOW3\Cache\Exception('Yet no cache frontend has been set via setCache().', 1232986971);
  173. }
  174. $this->flushByTag('%APCBE%' . $this->cacheIdentifier);
  175. }
  176. /**
  177. * Removes all cache entries of this cache which are tagged by the specified tag.
  178. *
  179. * @param string $tag The tag the entries must have
  180. * @return void
  181. * @api
  182. */
  183. public function flushByTag($tag) {
  184. $identifiers = $this->findIdentifiersByTag($tag);
  185. foreach ($identifiers as $identifier) {
  186. $this->remove($identifier);
  187. }
  188. }
  189. /**
  190. * Associates the identifier with the given tags
  191. *
  192. * @param string $entryIdentifier
  193. * @param array $tags
  194. * @return void
  195. */
  196. protected function addIdentifierToTags($entryIdentifier, array $tags) {
  197. foreach ($tags as $tag) {
  198. // Update tag-to-identifier index
  199. $identifiers = $this->findIdentifiersByTag($tag);
  200. if (array_search($entryIdentifier, $identifiers) === FALSE) {
  201. $identifiers[] = $entryIdentifier;
  202. apc_store($this->identifierPrefix . 'tag_' . $tag, $identifiers);
  203. }
  204. // Update identifier-to-tag index
  205. $existingTags = $this->findTagsByIdentifier($entryIdentifier);
  206. if (array_search($entryIdentifier, $existingTags) === FALSE) {
  207. apc_store($this->identifierPrefix . 'ident_' . $entryIdentifier, array_merge($existingTags, $tags));
  208. }
  209. }
  210. }
  211. /**
  212. * Removes association of the identifier with the given tags
  213. *
  214. * @param string $entryIdentifier
  215. * @return void
  216. */
  217. protected function removeIdentifierFromAllTags($entryIdentifier) {
  218. // Get tags for this identifier
  219. $tags = $this->findTagsByIdentifier($entryIdentifier);
  220. // Deassociate tags with this identifier
  221. foreach ($tags as $tag) {
  222. $identifiers = $this->findIdentifiersByTag($tag);
  223. // Formally array_search() below should never return false due to
  224. // the behavior of findTagsByIdentifier(). But if reverse index is
  225. // corrupted, we still can get 'false' from array_search(). This is
  226. // not a problem because we are removing this identifier from
  227. // anywhere.
  228. if (($key = array_search($entryIdentifier, $identifiers)) !== FALSE) {
  229. unset($identifiers[$key]);
  230. if (count($identifiers)) {
  231. apc_store($this->identifierPrefix . 'tag_' . $tag, $identifiers);
  232. } else {
  233. apc_delete($this->identifierPrefix . 'tag_' . $tag);
  234. }
  235. }
  236. }
  237. // Clear reverse tag index for this identifier
  238. apc_delete($this->identifierPrefix . 'ident_' . $entryIdentifier);
  239. }
  240. /**
  241. * Does nothing, as APC does GC itself
  242. *
  243. * @return void
  244. * @api
  245. */
  246. public function collectGarbage() {
  247. }
  248. }
  249. ?>