]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/CacheDependency.php
MediaWiki 1.11.0
[autoinstallsdev/mediawiki.git] / includes / CacheDependency.php
1 <?php
2
3 /**
4  * This class stores an arbitrary value along with its dependencies. 
5  * Users should typically only use DependencyWrapper::getFromCache(), rather
6  * than instantiating one of these objects directly.
7  * @addtogroup Cache
8  */
9 class DependencyWrapper {
10         var $value;
11         var $deps;
12
13         /**
14          * Create an instance.
15          * @param mixed $value The user-supplied value
16          * @param mixed $deps A dependency or dependency array. All dependencies
17          *        must be objects implementing CacheDependency.
18          */
19         function __construct( $value = false, $deps = array() ) {
20                 $this->value = $value;
21                 if ( !is_array( $deps ) ) {
22                         $deps = array( $deps );
23                 }
24                 $this->deps = $deps;
25         }
26
27         /** 
28          * Returns true if any of the dependencies have expired
29          */
30         function isExpired() {
31                 foreach ( $this->deps as $dep ) {
32                         if ( $dep->isExpired() ) {
33                                 return true;
34                         }
35                 }
36                 return false;
37         }
38
39         /**
40          * Initialise dependency values in preparation for storing. This must be
41          * called before serialization.
42          */
43         function initialiseDeps() {
44                 foreach ( $this->deps as $dep ) {
45                         $dep->loadDependencyValues();
46                 }
47         }
48
49         /**
50          * Get the user-defined value
51          */
52         function getValue() {
53                 return $this->value;
54         }
55
56         /**
57          * Store the wrapper to a cache
58          */
59         function storeToCache( $cache, $key, $expiry = 0 ) {
60                 $this->initialiseDeps();
61                 $cache->set( $key, $this, $expiry );
62         }
63
64         /**
65          * Attempt to get a value from the cache. If the value is expired or missing, 
66          * it will be generated with the callback function (if present), and the newly
67          * calculated value will be stored to the cache in a wrapper. 
68          *
69          * @param object $cache A cache object such as $wgMemc
70          * @param string $key The cache key
71          * @param integer $expiry The expiry timestamp or interval in seconds
72          * @param mixed $callback The callback for generating the value, or false
73          * @param array $callbackParams The function parameters for the callback
74          * @param array $deps The dependencies to store on a cache miss. Note: these 
75          *    are not the dependencies used on a cache hit! Cache hits use the stored
76          *    dependency array.
77          *
78          * @return mixed The value, or null if it was not present in the cache and no
79          *    callback was defined.
80          */
81         static function getValueFromCache( $cache, $key, $expiry = 0, $callback = false, 
82                 $callbackParams = array(), $deps = array() ) 
83         {
84                 $obj = $cache->get( $key );
85                 if ( is_object( $obj ) && $obj instanceof DependencyWrapper && !$obj->isExpired() ) {
86                         $value = $obj->value;
87                 } elseif ( $callback ) {
88                         $value = call_user_func_array( $callback, $callbackParams );
89                         # Cache the newly-generated value
90                         $wrapper = new DependencyWrapper( $value, $deps );
91                         $wrapper->storeToCache( $cache, $key, $expiry );
92                 } else {
93                         $value = null;
94                 }
95                 return $value;
96         }
97 }
98
99 /**
100  * @addtogroup Cache
101  */
102 abstract class CacheDependency {
103         /**
104          * Returns true if the dependency is expired, false otherwise
105          */
106         abstract function isExpired();
107
108         /**
109          * Hook to perform any expensive pre-serialize loading of dependency values.
110          */
111         function loadDependencyValues() {}
112 }
113
114 /**
115  * @addtogroup Cache
116  */
117 class FileDependency extends CacheDependency {
118         var $filename, $timestamp;
119
120         /**
121          * Create a file dependency
122          *
123          * @param string $filename The name of the file, preferably fully qualified
124          * @param mixed $timestamp The unix last modified timestamp, or false if the
125          *        file does not exist. If omitted, the timestamp will be loaded from 
126          *        the file.
127          *
128          * A dependency on a nonexistent file will be triggered when the file is 
129          * created. A dependency on an existing file will be triggered when the 
130          * file is changed.
131          */
132         function __construct( $filename, $timestamp = null ) {
133                 $this->filename = $filename;
134                 $this->timestamp = $timestamp;
135         }
136
137         function loadDependencyValues() {
138                 if ( is_null( $this->timestamp ) ) {
139                         if ( !file_exists( $this->filename ) ) {
140                                 # Dependency on a non-existent file
141                                 # This is a valid concept!
142                                 $this->timestamp = false;
143                         } else {
144                                 $this->timestamp = filemtime( $this->filename );
145                         }
146                 }
147         }
148
149         function isExpired() {
150                 if ( !file_exists( $this->filename ) ) {
151                         if ( $this->timestamp === false ) {
152                                 # Still nonexistent
153                                 return false;
154                         } else {
155                                 # Deleted
156                                 wfDebug( "Dependency triggered: {$this->filename} deleted.\n" );
157                                 return true;
158                         }
159                 } else {
160                         $lastmod = filemtime( $this->filename );
161                         if ( $lastmod > $this->timestamp ) {
162                                 # Modified or created
163                                 wfDebug( "Dependency triggered: {$this->filename} changed.\n" );
164                                 return true;
165                         } else {
166                                 # Not modified
167                                 return false;
168                         }
169                 }
170         }
171 }
172
173 /**
174  * @addtogroup Cache
175  */
176 class TitleDependency extends CacheDependency {
177         var $titleObj;
178         var $ns, $dbk;
179         var $touched;
180
181         /**
182          * Construct a title dependency
183          * @param Title $title
184          */
185         function __construct( Title $title ) {
186                 $this->titleObj = $title;
187                 $this->ns = $title->getNamespace();
188                 $this->dbk = $title->getDBkey();
189         }
190
191         function loadDependencyValues() {
192                 $this->touched = $this->getTitle()->getTouched();
193         }
194         
195         /**
196          * Get rid of bulky Title object for sleep
197          */
198         function __sleep() {
199                 return array( 'ns', 'dbk', 'touched' );
200         }
201
202         function getTitle() {
203                 if ( !isset( $this->titleObj ) ) {
204                         $this->titleObj = Title::makeTitle( $this->ns, $this->dbk );
205                 } 
206                 return $this->titleObj;
207         }
208
209         function isExpired() {
210                 $touched = $this->getTitle()->getTouched();
211                 if ( $this->touched === false ) {
212                         if ( $touched === false ) {
213                                 # Still missing
214                                 return false;
215                         } else {
216                                 # Created
217                                 return true;
218                         }
219                 } elseif ( $touched === false ) {
220                         # Deleted
221                         return true;
222                 } elseif ( $touched > $this->touched ) {
223                         # Updated
224                         return true;
225                 } else {
226                         # Unmodified
227                         return false;
228                 }
229         }
230 }
231
232 /**
233  * @addtogroup Cache
234  */
235 class TitleListDependency extends CacheDependency {
236         var $linkBatch;
237         var $timestamps;
238         
239         /**
240          * Construct a dependency on a list of titles
241          */
242         function __construct( LinkBatch $linkBatch ) {
243                 $this->linkBatch = $linkBatch;
244         }
245
246         function calculateTimestamps() {
247                 # Initialise values to false
248                 $timestamps = array();
249                 foreach ( $this->getLinkBatch()->data as $ns => $dbks ) {
250                         if ( count( $dbks ) > 0 ) {
251                                 $timestamps[$ns] = array();
252                                 foreach ( $dbks as $dbk => $value ) {
253                                         $timestamps[$ns][$dbk] = false;
254                                 }
255                         }
256                 }
257
258                 # Do the query
259                 if ( count( $timestamps ) ) {
260                         $dbr = wfGetDB( DB_SLAVE );
261                         $where = $this->getLinkBatch()->constructSet( 'page', $dbr );
262                         $res = $dbr->select( 'page', 
263                                 array( 'page_namespace', 'page_title', 'page_touched' ),
264                                 $where, __METHOD__ );
265                         while ( $row = $dbr->fetchObject( $res ) ) {
266                                 $timestamps[$row->page_namespace][$row->page_title] = $row->page_touched;
267                         }
268                 }
269                 return $timestamps;
270         }
271
272         function loadDependencyValues() {
273                 $this->timestamps = $this->calculateTimestamps();
274         }
275
276         function __sleep() {
277                 return array( 'timestamps' );
278         }
279
280         function getLinkBatch() {
281                 if ( !isset( $this->linkBatch ) ){
282                         $this->linkBatch = new LinkBatch;
283                         $this->linkBatch->setArray( $this->timestamps );
284                 }
285                 return $this->linkBatch;
286         }
287
288         function isExpired() {
289                 $newTimestamps = $this->calculateTimestamps();
290                 foreach ( $this->timestamps as $ns => $dbks ) {
291                         foreach ( $dbks as $dbk => $oldTimestamp ) {
292                                 $newTimestamp = $newTimestamps[$ns][$dbk];
293                                 if ( $oldTimestamp === false ) {
294                                         if ( $newTimestamp === false ) {
295                                                 # Still missing
296                                         } else {
297                                                 # Created
298                                                 return true;
299                                         }
300                                 } elseif ( $newTimestamp === false ) {
301                                         # Deleted
302                                         return true;
303                                 } elseif ( $newTimestamp > $oldTimestamp ) {
304                                         # Updated
305                                         return true;
306                                 } else {
307                                         # Unmodified
308                                 }
309                         }
310                 }
311                 return false;
312         }
313 }
314
315 /**
316  * @addtogroup Cache
317  */
318 class GlobalDependency extends CacheDependency {
319         var $name, $value;
320         
321         function __construct( $name ) {
322                 $this->name = $name;
323                 $this->value = $GLOBALS[$name];
324         }
325
326         function isExpired() {
327                 return $GLOBALS[$this->name] != $this->value;
328         }
329 }
330
331 /**
332  * @addtogroup Cache
333  */
334 class ConstantDependency extends CacheDependency {
335         var $name, $value;
336
337         function __construct( $name ) {
338                 $this->name = $name;
339                 $this->value = constant( $name );
340         }
341
342         function isExpired() {
343                 return constant( $this->name ) != $this->value;
344         }
345 }
346
347