MediaWiki  master
WebResponse.php
Go to the documentation of this file.
1 <?php
28 class WebResponse {
29 
33  protected static $setCookies = [];
34 
36  protected static $disableForPostSend = false;
37 
47  public static function disableForPostSend() {
48  self::$disableForPostSend = true;
49  }
50 
57  public function header( $string, $replace = true, $http_response_code = null ) {
58  if ( self::$disableForPostSend ) {
59  wfDebugLog( 'header', 'ignored post-send header {header}', 'all', [
60  'header' => $string,
61  'replace' => $replace,
62  'http_response_code' => $http_response_code,
63  'exception' => new RuntimeException( 'Ignored post-send header' ),
64  ] );
65  return;
66  }
67 
69  if ( $http_response_code ) {
70  header( $string, $replace, $http_response_code );
71  } else {
72  header( $string, $replace );
73  }
74  }
75 
82  public function getHeader( $key ) {
83  foreach ( headers_list() as $header ) {
84  list( $name, $val ) = explode( ':', $header, 2 );
85  if ( !strcasecmp( $name, $key ) ) {
86  return trim( $val );
87  }
88  }
89  return null;
90  }
91 
97  public function statusHeader( $code ) {
98  if ( self::$disableForPostSend ) {
99  wfDebugLog( 'header', 'ignored post-send status header {code}', 'all', [
100  'code' => $code,
101  'exception' => new RuntimeException( 'Ignored post-send status header' ),
102  ] );
103  return;
104  }
105 
106  HttpStatus::header( $code );
107  }
108 
114  public function headersSent() {
115  return headers_sent();
116  }
117 
133  public function setCookie( $name, $value, $expire = 0, $options = [] ) {
136 
137  $options = array_filter( $options, function ( $a ) {
138  return $a !== null;
139  } ) + [
140  'prefix' => $wgCookiePrefix,
141  'domain' => $wgCookieDomain,
142  'path' => $wgCookiePath,
143  'secure' => $wgCookieSecure,
144  'httpOnly' => $wgCookieHttpOnly,
145  'raw' => false,
146  ];
147 
148  if ( $expire === null ) {
149  $expire = 0; // Session cookie
150  } elseif ( $expire == 0 && $wgCookieExpiration != 0 ) {
151  $expire = time() + $wgCookieExpiration;
152  }
153 
154  if ( self::$disableForPostSend ) {
155  $cookie = $options['prefix'] . $name;
156  wfDebugLog( 'cookie', 'ignored post-send cookie {cookie}', 'all', [
157  'cookie' => $cookie,
158  'data' => [
159  'name' => (string)$cookie,
160  'value' => (string)$value,
161  'expire' => (int)$expire,
162  'path' => (string)$options['path'],
163  'domain' => (string)$options['domain'],
164  'secure' => (bool)$options['secure'],
165  'httpOnly' => (bool)$options['httpOnly'],
166  ],
167  'exception' => new RuntimeException( 'Ignored post-send cookie' ),
168  ] );
169  return;
170  }
171 
172  $func = $options['raw'] ? 'setrawcookie' : 'setcookie';
173 
174  if ( Hooks::run( 'WebResponseSetCookie', [ &$name, &$value, &$expire, &$options ] ) ) {
175  // Note: Don't try to move this earlier to reuse it for self::$disableForPostSend,
176  // we need to use the altered values from the hook here. (T198525)
177  $cookie = $options['prefix'] . $name;
178  $data = [
179  'name' => (string)$cookie,
180  'value' => (string)$value,
181  'expire' => (int)$expire,
182  'path' => (string)$options['path'],
183  'domain' => (string)$options['domain'],
184  'secure' => (bool)$options['secure'],
185  'httpOnly' => (bool)$options['httpOnly'],
186  ];
187 
188  // Per RFC 6265, key is name + domain + path
189  $key = "{$data['name']}\n{$data['domain']}\n{$data['path']}";
190 
191  // If this cookie name was in the request, fake an entry in
192  // self::$setCookies for it so the deleting check works right.
193  if ( isset( $_COOKIE[$cookie] ) && !array_key_exists( $key, self::$setCookies ) ) {
194  self::$setCookies[$key] = [];
195  }
196 
197  // PHP deletes if value is the empty string; also, a past expiry is deleting
198  $deleting = ( $data['value'] === '' || $data['expire'] > 0 && $data['expire'] <= time() );
199 
200  if ( $deleting && !isset( self::$setCookies[$key] ) ) { // isset( null ) is false
201  wfDebugLog( 'cookie', 'already deleted ' . $func . ': "' . implode( '", "', $data ) . '"' );
202  } elseif ( !$deleting && isset( self::$setCookies[$key] ) &&
203  self::$setCookies[$key] === [ $func, $data ]
204  ) {
205  wfDebugLog( 'cookie', 'already set ' . $func . ': "' . implode( '", "', $data ) . '"' );
206  } else {
207  wfDebugLog( 'cookie', $func . ': "' . implode( '", "', $data ) . '"' );
208  if ( $func( ...array_values( $data ) ) ) {
209  self::$setCookies[$key] = $deleting ? null : [ $func, $data ];
210  }
211  }
212  }
213  }
214 
224  public function clearCookie( $name, $options = [] ) {
225  $this->setCookie( $name, '', time() - 31536000 /* 1 year */, $options );
226  }
227 
234  public function hasCookies() {
235  return (bool)self::$setCookies;
236  }
237 }
static bool $disableForPostSend
Used to disable setters before running jobs post-request (T191537)
Definition: WebResponse.php:36
setCookie( $name, $value, $expire=0, $options=[])
Set the browser cookie.
hasCookies()
Checks whether this request is performing cookie operations.
statusHeader( $code)
Output an HTTP status code header.
Definition: WebResponse.php:97
$wgCookieDomain
Set to set an explicit domain on the login cookies eg, "justthis.domain.org" or ".any.subdomain.net".
$wgCookiePrefix
Cookies generated by MediaWiki have names starting with this prefix.
$wgCookieHttpOnly
Set authentication cookies to HttpOnly to prevent access by JavaScript, in browsers that support this...
getHeader( $key)
Get a response header.
Definition: WebResponse.php:82
$wgCookieSecure
Whether the "secure" flag should be set on the cookie.
clearCookie( $name, $options=[])
Unset a browser cookie.
header( $string, $replace=true, $http_response_code=null)
Output an HTTP header, wrapper for PHP&#39;s header()
Definition: WebResponse.php:57
static warnIfHeadersSent()
Log a warning message if headers have already been sent.
static header( $code)
Output an HTTP status code header.
Definition: HttpStatus.php:96
headersSent()
Test if headers have been sent.
$header
$wgCookieExpiration
Default cookie lifetime, in seconds.
static disableForPostSend()
Disable setters for post-send processing.
Definition: WebResponse.php:47
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not...
$wgCookiePath
Set this variable if you want to restrict cookies to a certain path within the domain specified by $w...
static array $setCookies
Used to record set cookies, because PHP&#39;s setcookie() will happily send an identical Set-Cookie to th...
Definition: WebResponse.php:33
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200