PageRenderTime 44ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/includes/cache/CacheDependency.php

https://gitlab.com/qiusct/mediawiki-i
PHP | 265 lines | 116 code | 29 blank | 120 comment | 18 complexity | 75f36eb1c601fdb9c32fdc6cd04db12b MD5 | raw file
Possible License(s): Apache-2.0, MIT, GPL-2.0
  1. <?php
  2. /**
  3. * Data caching with dependencies.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. * @ingroup Cache
  22. */
  23. /**
  24. * This class stores an arbitrary value along with its dependencies.
  25. * Users should typically only use DependencyWrapper::getValueFromCache(),
  26. * rather than instantiating one of these objects directly.
  27. * @ingroup Cache
  28. */
  29. class DependencyWrapper {
  30. private $value;
  31. /** @var CacheDependency[] */
  32. private $deps;
  33. /**
  34. * Create an instance.
  35. * @param mixed $value The user-supplied value
  36. * @param CacheDependency|CacheDependency[] $deps A dependency or dependency
  37. * array. All dependencies must be objects implementing CacheDependency.
  38. */
  39. function __construct( $value = false, $deps = array() ) {
  40. $this->value = $value;
  41. if ( !is_array( $deps ) ) {
  42. $deps = array( $deps );
  43. }
  44. $this->deps = $deps;
  45. }
  46. /**
  47. * Returns true if any of the dependencies have expired
  48. *
  49. * @return bool
  50. */
  51. function isExpired() {
  52. foreach ( $this->deps as $dep ) {
  53. if ( $dep->isExpired() ) {
  54. return true;
  55. }
  56. }
  57. return false;
  58. }
  59. /**
  60. * Initialise dependency values in preparation for storing. This must be
  61. * called before serialization.
  62. */
  63. function initialiseDeps() {
  64. foreach ( $this->deps as $dep ) {
  65. $dep->loadDependencyValues();
  66. }
  67. }
  68. /**
  69. * Get the user-defined value
  70. * @return bool|mixed
  71. */
  72. function getValue() {
  73. return $this->value;
  74. }
  75. /**
  76. * Store the wrapper to a cache
  77. *
  78. * @param BagOStuff $cache
  79. * @param string $key
  80. * @param int $expiry
  81. */
  82. function storeToCache( $cache, $key, $expiry = 0 ) {
  83. $this->initialiseDeps();
  84. $cache->set( $key, $this, $expiry );
  85. }
  86. /**
  87. * Attempt to get a value from the cache. If the value is expired or missing,
  88. * it will be generated with the callback function (if present), and the newly
  89. * calculated value will be stored to the cache in a wrapper.
  90. *
  91. * @param BagOStuff $cache A cache object such as $wgMemc
  92. * @param string $key The cache key
  93. * @param int $expiry The expiry timestamp or interval in seconds
  94. * @param bool|array $callback The callback for generating the value, or false
  95. * @param array $callbackParams The function parameters for the callback
  96. * @param array $deps The dependencies to store on a cache miss. Note: these
  97. * are not the dependencies used on a cache hit! Cache hits use the stored
  98. * dependency array.
  99. *
  100. * @return mixed The value, or null if it was not present in the cache and no
  101. * callback was defined.
  102. */
  103. static function getValueFromCache( $cache, $key, $expiry = 0, $callback = false,
  104. $callbackParams = array(), $deps = array()
  105. ) {
  106. $obj = $cache->get( $key );
  107. if ( is_object( $obj ) && $obj instanceof DependencyWrapper && !$obj->isExpired() ) {
  108. $value = $obj->value;
  109. } elseif ( $callback ) {
  110. $value = call_user_func_array( $callback, $callbackParams );
  111. # Cache the newly-generated value
  112. $wrapper = new DependencyWrapper( $value, $deps );
  113. $wrapper->storeToCache( $cache, $key, $expiry );
  114. } else {
  115. $value = null;
  116. }
  117. return $value;
  118. }
  119. }
  120. /**
  121. * @ingroup Cache
  122. */
  123. abstract class CacheDependency {
  124. /**
  125. * Returns true if the dependency is expired, false otherwise
  126. */
  127. abstract function isExpired();
  128. /**
  129. * Hook to perform any expensive pre-serialize loading of dependency values.
  130. */
  131. function loadDependencyValues() {
  132. }
  133. }
  134. /**
  135. * @ingroup Cache
  136. */
  137. class FileDependency extends CacheDependency {
  138. private $filename;
  139. private $timestamp;
  140. /**
  141. * Create a file dependency
  142. *
  143. * @param string $filename the name of the file, preferably fully qualified
  144. * @param null|bool|int $timestamp The unix last modified timestamp, or false if the
  145. * file does not exist. If omitted, the timestamp will be loaded from
  146. * the file.
  147. *
  148. * A dependency on a nonexistent file will be triggered when the file is
  149. * created. A dependency on an existing file will be triggered when the
  150. * file is changed.
  151. */
  152. function __construct( $filename, $timestamp = null ) {
  153. $this->filename = $filename;
  154. $this->timestamp = $timestamp;
  155. }
  156. /**
  157. * @return array
  158. */
  159. function __sleep() {
  160. $this->loadDependencyValues();
  161. return array( 'filename', 'timestamp' );
  162. }
  163. function loadDependencyValues() {
  164. if ( is_null( $this->timestamp ) ) {
  165. if ( !file_exists( $this->filename ) ) {
  166. # Dependency on a non-existent file
  167. # This is a valid concept!
  168. $this->timestamp = false;
  169. } else {
  170. $this->timestamp = filemtime( $this->filename );
  171. }
  172. }
  173. }
  174. /**
  175. * @return bool
  176. */
  177. function isExpired() {
  178. if ( !file_exists( $this->filename ) ) {
  179. if ( $this->timestamp === false ) {
  180. # Still nonexistent
  181. return false;
  182. } else {
  183. # Deleted
  184. wfDebug( "Dependency triggered: {$this->filename} deleted.\n" );
  185. return true;
  186. }
  187. } else {
  188. $lastmod = filemtime( $this->filename );
  189. if ( $lastmod > $this->timestamp ) {
  190. # Modified or created
  191. wfDebug( "Dependency triggered: {$this->filename} changed.\n" );
  192. return true;
  193. } else {
  194. # Not modified
  195. return false;
  196. }
  197. }
  198. }
  199. }
  200. /**
  201. * @ingroup Cache
  202. */
  203. class GlobalDependency extends CacheDependency {
  204. private $name;
  205. private $value;
  206. function __construct( $name ) {
  207. $this->name = $name;
  208. $this->value = $GLOBALS[$name];
  209. }
  210. /**
  211. * @return bool
  212. */
  213. function isExpired() {
  214. if ( !isset( $GLOBALS[$this->name] ) ) {
  215. return true;
  216. }
  217. return $GLOBALS[$this->name] != $this->value;
  218. }
  219. }
  220. /**
  221. * @ingroup Cache
  222. */
  223. class ConstantDependency extends CacheDependency {
  224. private $name;
  225. private $value;
  226. function __construct( $name ) {
  227. $this->name = $name;
  228. $this->value = constant( $name );
  229. }
  230. /**
  231. * @return bool
  232. */
  233. function isExpired() {
  234. return constant( $this->name ) != $this->value;
  235. }
  236. }