MediaWiki  master
AjaxResponse.php
Go to the documentation of this file.
1 <?php
24 
31 class AjaxResponse {
36  private $mCacheDuration;
37 
42  private $mContentType;
43 
48  private $mDisabled;
49 
54  private $mLastModified;
55 
60  private $mResponseCode;
61 
66  private $mVary;
67 
72  private $mText;
73 
77  private $mConfig;
78 
83  function __construct( $text = null, Config $config = null ) {
84  $this->mCacheDuration = null;
85  $this->mVary = null;
86  $this->mConfig = $config ?: MediaWikiServices::getInstance()->getMainConfig();
87 
88  $this->mDisabled = false;
89  $this->mText = '';
90  $this->mResponseCode = 200;
91  $this->mLastModified = false;
92  $this->mContentType = 'application/x-wiki';
93 
94  if ( $text ) {
95  $this->addText( $text );
96  }
97  }
98 
103  function setCacheDuration( $duration ) {
104  $this->mCacheDuration = $duration;
105  }
106 
111  function setVary( $vary ) {
112  $this->mVary = $vary;
113  }
114 
119  function setResponseCode( $code ) {
120  $this->mResponseCode = $code;
121  }
122 
127  function setContentType( $type ) {
128  $this->mContentType = $type;
129  }
130 
134  function disable() {
135  $this->mDisabled = true;
136  }
137 
142  function addText( $text ) {
143  if ( !$this->mDisabled && $text ) {
144  $this->mText .= $text;
145  }
146  }
147 
151  function printText() {
152  if ( !$this->mDisabled ) {
154  }
155  }
156 
160  function sendHeaders() {
161  if ( $this->mResponseCode ) {
162  // For back-compat, it is supported that mResponseCode be a string like " 200 OK"
163  // (with leading space and the status message after). Cast response code to an integer
164  // to take advantage of PHP's conversion rules which will turn " 200 OK" into 200.
165  // https://www.php.net/manual/en/language.types.string.php#language.types.string.conversion
166  $n = intval( trim( $this->mResponseCode ) );
167  HttpStatus::header( $n );
168  }
169 
170  header( "Content-Type: " . $this->mContentType );
171 
172  if ( $this->mLastModified ) {
173  header( "Last-Modified: " . $this->mLastModified );
174  } else {
175  header( "Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . " GMT" );
176  }
177 
178  if ( $this->mCacheDuration ) {
179  # If CDN caches are configured, tell them to cache the response,
180  # and tell the client to always check with the CDN. Otherwise,
181  # tell the client to use a cached copy, without a way to purge it.
182  if ( $this->mConfig->get( 'UseCdn' ) ) {
183  # Expect explicit purge of the proxy cache, but require end user agents
184  # to revalidate against the proxy on each visit.
185  header( 'Cache-Control: s-maxage=' . $this->mCacheDuration . ', must-revalidate, max-age=0' );
186  } else {
187  # Let the client do the caching. Cache is not purged.
188  header( "Expires: " . gmdate( "D, d M Y H:i:s", time() + $this->mCacheDuration ) . " GMT" );
189  header( "Cache-Control: s-maxage={$this->mCacheDuration}," .
190  "public,max-age={$this->mCacheDuration}" );
191  }
192 
193  } else {
194  # always expired, always modified
195  header( "Expires: Mon, 26 Jul 1997 05:00:00 GMT" ); // Date in the past
196  header( "Cache-Control: no-cache, must-revalidate" ); // HTTP/1.1
197  header( "Pragma: no-cache" ); // HTTP/1.0
198  }
199 
200  if ( $this->mVary ) {
201  header( "Vary: " . $this->mVary );
202  }
203  }
204 
213  function checkLastModified( $timestamp ) {
214  global $wgCachePages, $wgCacheEpoch, $wgUser;
215  $fname = 'AjaxResponse::checkLastModified';
216 
217  if ( !$timestamp || $timestamp == '19700101000000' ) {
218  wfDebug( "$fname: CACHE DISABLED, NO TIMESTAMP", 'private' );
219  return false;
220  }
221 
222  if ( !$wgCachePages ) {
223  wfDebug( "$fname: CACHE DISABLED", 'private' );
224  return false;
225  }
226 
227  $timestamp = wfTimestamp( TS_MW, $timestamp );
228  $lastmod = wfTimestamp( TS_RFC2822, max( $timestamp, $wgUser->getTouched(), $wgCacheEpoch ) );
229 
230  if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {
231  # IE sends sizes after the date like this:
232  # Wed, 20 Aug 2003 06:51:19 GMT; length=5202
233  # this breaks strtotime().
234  $modsince = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] );
235  $modsinceTime = strtotime( $modsince );
236  $ismodsince = wfTimestamp( TS_MW, $modsinceTime ?: 1 );
237  wfDebug( "$fname: -- client send If-Modified-Since: $modsince", 'private' );
238  wfDebug( "$fname: -- we might send Last-Modified : $lastmod", 'private' );
239 
240  if ( ( $ismodsince >= $timestamp )
241  && $wgUser->validateCache( $ismodsince ) &&
242  $ismodsince >= $wgCacheEpoch
243  ) {
244  ini_set( 'zlib.output_compression', 0 );
245  $this->setResponseCode( 304 );
246  $this->disable();
247  $this->mLastModified = $lastmod;
248 
249  wfDebug( "$fname: CACHED client: $ismodsince ; user: {$wgUser->getTouched()} ; " .
250  "page: $timestamp ; site $wgCacheEpoch", 'private' );
251 
252  return true;
253  } else {
254  wfDebug( "$fname: READY client: $ismodsince ; user: {$wgUser->getTouched()} ; " .
255  "page: $timestamp ; site $wgCacheEpoch", 'private' );
256  $this->mLastModified = $lastmod;
257  }
258  } else {
259  wfDebug( "$fname: client did not send If-Modified-Since header", 'private' );
260  $this->mLastModified = $lastmod;
261  }
262  return false;
263  }
264 
270  function loadFromMemcached( $mckey, $touched ) {
271  if ( !$touched ) {
272  return false;
273  }
274 
275  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
276  $mcvalue = $cache->get( $mckey );
277  if ( $mcvalue ) {
278  # Check to see if the value has been invalidated
279  if ( $touched <= $mcvalue['timestamp'] ) {
280  wfDebug( "Got $mckey from cache" );
281  $this->mText = $mcvalue['value'];
282 
283  return true;
284  } else {
285  wfDebug( "$mckey has expired" );
286  }
287  }
288 
289  return false;
290  }
291 
297  function storeInMemcached( $mckey, $expiry = 86400 ) {
298  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
299  $cache->set( $mckey,
300  [
301  'timestamp' => wfTimestampNow(),
302  'value' => $this->mText
303  ],
304  $expiry
305  );
306 
307  return true;
308  }
309 }
sendHeaders()
Construct the header and output it.
printText()
Output text.
setCacheDuration( $duration)
Set the number of seconds to get the response cached by a proxy.
disable()
Disable output.
setContentType( $type)
Set the HTTP header Content-Type.
$mLastModified
Date for the HTTP header Last-modified.
Config $mConfig
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
setVary( $vary)
Set the HTTP Vary header.
loadFromMemcached( $mckey, $touched)
$wgCachePages
Allow client-side caching of pages.
Interface for configuration instances.
Definition: Config.php:28
Handle responses for Ajax requests (send headers, print content, that sort of thing) ...
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
$mText
Content of our HTTP response.
addText( $text)
Add content to the response.
$mDisabled
Disables output.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
static header( $code)
Output an HTTP status code header.
Definition: HttpStatus.php:96
$cache
Definition: mcc.php:33
$mVary
HTTP Vary header.
$mResponseCode
HTTP response code.
checkLastModified( $timestamp)
checkLastModified tells the client to use the client-cached response if possible. ...
$mContentType
HTTP header Content-Type.
storeInMemcached( $mckey, $expiry=86400)
__construct( $text=null, Config $config=null)
$wgCacheEpoch
Set this to current time to invalidate all prior cached pages.
$mCacheDuration
Number of seconds to get the response cached by a proxy.
setResponseCode( $code)
Set the HTTP response code.