follow up to r114081: qqq, and to r114082: match rename
[lhc/web/wiklou.git] / includes / specials / SpecialCachedPage.php
1 <?php
2
3 /**
4 * Abstract special page class with scaffolding for caching the HTML output.
5 *
6 * To enable the caching functionality, the cacheExpiry field should be set
7 * in the constructor.
8 *
9 * To add HTML that should be cached, use addCachedHTML like this:
10 * $this->addCachedHTML( array( $this, 'displayCachedContent' ) );
11 *
12 * After adding the last HTML that should be cached, call $this->saveCache();
13 *
14 * @since 1.20
15 *
16 * @file SpecialCachedPage.php
17 * @ingroup SpecialPage
18 *
19 * @licence GNU GPL v2 or later
20 * @author Jeroen De Dauw < jeroendedauw@gmail.com >
21 */
22 abstract class SpecialCachedPage extends SpecialPage {
23
24 /**
25 * The time to live for the cache, in seconds or a unix timestamp indicating the point of expiry.
26 *
27 * @since 1.20
28 * @var integer|null
29 */
30 protected $cacheExpiry = null;
31
32 /**
33 * List of HTML chunks to be cached (if !hasCached) or that where cashed (of hasCached).
34 * If no cached already, then the newly computed chunks are added here,
35 * if it as cached already, chunks are removed from this list as they are needed.
36 *
37 * @since 1.20
38 * @var array
39 */
40 protected $cachedChunks;
41
42 /**
43 * Indicates if the to be cached content was already cached.
44 * Null if this information is not available yet.
45 *
46 * @since 1.20
47 * @var boolean|null
48 */
49 protected $hasCached = null;
50
51 /**
52 * Main method.
53 *
54 * @since 1.20
55 *
56 * @param string|null $subPage
57 */
58 public function execute( $subPage ) {
59 if ( $this->getRequest()->getText( 'action' ) === 'purge' ) {
60 $this->hasCached = false;
61 }
62
63 if ( !is_null( $this->cacheExpiry ) ) {
64 $this->initCaching();
65
66 if ( $this->hasCached === true ) {
67 $this->getOutput()->setSubtitle( $this->getCachedNotice( $subPage ) );
68 }
69 }
70 }
71
72 /**
73 * Returns a message that notifies the user he/she is looking at
74 * a cached version of the page, including a refresh link.
75 *
76 * @since 1.20
77 *
78 * @param string|null $subPage
79 *
80 * @return string
81 */
82 protected function getCachedNotice( $subPage ) {
83 $refreshArgs = $_GET;
84 unset( $refreshArgs['title'] );
85 $refreshArgs['action'] = 'purge';
86
87 $refreshLink = Linker::link(
88 $this->getTitle( $subPage ),
89 $this->msg( 'cachedspecial-refresh-now' )->escaped(),
90 array(),
91 $refreshArgs
92 );
93
94 if ( $this->cacheExpiry < 1000000000 ) {
95 $message = $this->msg(
96 'cachedspecial-viewing-cached-ttl',
97 $this->getLanguage()->formatDuration( $this->cacheExpiry )
98 )->escaped();
99 }
100 else {
101 $message = $this->msg(
102 'cachedspecial-viewing-cached-ts'
103 )->escaped();
104 }
105
106 return $message . ' ' . $refreshLink;
107 }
108
109 /**
110 * Initializes the caching if not already done so.
111 * Should be called before any of the caching functionality is used.
112 *
113 * @since 1.20
114 */
115 protected function initCaching() {
116 if ( is_null( $this->hasCached ) ) {
117 $cachedChunks = wfGetCache( CACHE_ANYTHING )->get( $this->getCacheKey() );
118
119 $this->hasCached = is_array( $cachedChunks );
120 $this->cachedChunks = $this->hasCached ? $cachedChunks : array();
121 }
122 }
123
124 /**
125 * Add some HTML to be cached.
126 * This is done by providing a callback function that should
127 * return the HTML to be added. It will only be called if the
128 * item is not in the cache yet or when the cache has been invalidated.
129 *
130 * @since 1.20
131 *
132 * @param {function} $callback
133 * @param array $args
134 * @param string|null $key
135 */
136 public function addCachedHTML( $callback, $args = array(), $key = null ) {
137 $this->initCaching();
138
139 if ( $this->hasCached ) {
140 $html = '';
141
142 if ( is_null( $key ) ) {
143 $itemKey = array_keys( array_slice( $this->cachedChunks, 0, 1 ) );
144 $itemKey = array_shift( $itemKey );
145
146 if ( !is_integer( $itemKey ) ) {
147 wfWarn( "Attempted to get item with non-numeric key while the next item in the queue has a key ($itemKey) in " . __METHOD__ );
148 }
149 elseif ( is_null( $itemKey ) ) {
150 wfWarn( "Attempted to get an item while the queue is empty in " . __METHOD__ );
151 }
152 else {
153 $html = array_shift( $this->cachedChunks );
154 }
155 }
156 else {
157 if ( array_key_exists( $key, $this->cachedChunks ) ) {
158 $html = $this->cachedChunks[$key];
159 unset( $this->cachedChunks[$key] );
160 }
161 else {
162 wfWarn( "There is no item with key '$key' in this->cachedChunks in " . __METHOD__ );
163 }
164 }
165 }
166 else {
167 $html = call_user_func_array( $callback, $args );
168
169 if ( is_null( $key ) ) {
170 $this->cachedChunks[] = $html;
171 }
172 else {
173 $this->cachedChunks[$key] = $html;
174 }
175 }
176
177 $this->getOutput()->addHTML( $html );
178 }
179
180 /**
181 * Saves the HTML to the cache in case it got recomputed.
182 * Should be called after the last time anything is added via addCachedHTML.
183 *
184 * @since 1.20
185 */
186 public function saveCache() {
187 if ( $this->hasCached === false && !empty( $this->cachedChunks ) ) {
188 wfGetCache( CACHE_ANYTHING )->set( $this->getCacheKey(), $this->cachedChunks, $this->cacheExpiry );
189 }
190 }
191
192 /**
193 * Sets the time to live for the cache, in seconds or a unix timestamp indicating the point of expiry..
194 *
195 * @since 1.20
196 *
197 * @param integer $cacheExpiry
198 */
199 protected function setExpirey( $cacheExpiry ) {
200 $this->cacheExpiry = $cacheExpiry;
201 }
202
203 /**
204 * Returns the cache key to use to cache this page's HTML output.
205 * Is constructed from the special page name and language code.
206 *
207 * @since 1.20
208 *
209 * @return string
210 */
211 protected function getCacheKey() {
212 return wfMemcKey( $this->mName, $this->getLanguage()->getCode() );
213 }
214
215 }