60 public function header( $string, $replace =
true, $http_response_code =
null ) {
61 if ( self::$disableForPostSend ) {
62 wfDebugLog(
'header',
'ignored post-send header {header}',
'all', [
64 'replace' => $replace,
65 'http_response_code' => $http_response_code,
66 'exception' =>
new RuntimeException(
'Ignored post-send header' ),
71 \MediaWiki\HeaderCallback::warnIfHeadersSent();
72 if ( $http_response_code ) {
73 header( $string, $replace, $http_response_code );
75 header( $string, $replace );
142 public function setCookie( $name, $value, $expire = 0, $options = [] ) {
143 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
144 $cookiePath = $mainConfig->get( MainConfigNames::CookiePath );
145 $cookiePrefix = $mainConfig->get( MainConfigNames::CookiePrefix );
146 $cookieDomain = $mainConfig->get( MainConfigNames::CookieDomain );
147 $cookieSecure = $mainConfig->get( MainConfigNames::CookieSecure );
148 $cookieExpiration = $mainConfig->get( MainConfigNames::CookieExpiration );
149 $cookieHttpOnly = $mainConfig->get( MainConfigNames::CookieHttpOnly );
150 $useSameSiteLegacyCookies = $mainConfig->get( MainConfigNames::UseSameSiteLegacyCookies );
151 $options = array_filter( $options,
static function ( $a ) {
154 'prefix' => $cookiePrefix,
155 'domain' => $cookieDomain,
156 'path' => $cookiePath,
157 'secure' => $cookieSecure,
158 'httpOnly' => $cookieHttpOnly,
161 'sameSiteLegacy' => $useSameSiteLegacyCookies
164 if ( strcasecmp( $options[
'sameSite'],
'none' ) === 0
165 && !empty( $options[
'sameSiteLegacy'] )
167 $legacyOptions = $options;
168 $legacyOptions[
'sameSiteLegacy'] =
false;
169 $legacyOptions[
'sameSite'] =
'';
170 $this->
setCookie(
"ss0-$name", $value, $expire, $legacyOptions );
173 if ( $expire ===
null ) {
175 } elseif ( $expire == 0 && $cookieExpiration != 0 ) {
176 $expire = time() + $cookieExpiration;
179 if ( self::$disableForPostSend ) {
180 $prefixedName = $options[
'prefix'] . $name;
181 wfDebugLog(
'cookie',
'ignored post-send cookie {cookie}',
'all', [
182 'cookie' => $prefixedName,
184 'name' => $prefixedName,
185 'value' => (
string)$value,
186 'expire' => (
int)$expire,
187 'path' => (
string)$options[
'path'],
188 'domain' => (
string)$options[
'domain'],
189 'secure' => (
bool)$options[
'secure'],
190 'httpOnly' => (
bool)$options[
'httpOnly'],
191 'sameSite' => (
string)$options[
'sameSite']
193 'exception' =>
new RuntimeException(
'Ignored post-send cookie' ),
198 if ( !Hooks::runner()->onWebResponseSetCookie( $name, $value, $expire, $options ) ) {
204 $prefixedName = $options[
'prefix'] . $name;
205 $value = (string)$value;
206 $func = $options[
'raw'] ?
'setrawcookie' :
'setcookie';
208 'expires' => (int)$expire,
209 'path' => (
string)$options[
'path'],
210 'domain' => (string)$options[
'domain'],
211 'secure' => (
bool)$options[
'secure'],
212 'httponly' => (bool)$options[
'httpOnly'],
213 'samesite' => (
string)$options[
'sameSite'],
217 $key =
"{$prefixedName}\n{$setOptions['domain']}\n{$setOptions['path']}";
221 if ( isset( $_COOKIE[$prefixedName] ) && !array_key_exists( $key, self::$setCookies ) ) {
222 self::$setCookies[$key] = [];
226 $deleting = ( $value ===
'' || $setOptions[
'expires'] > 0 && $setOptions[
'expires'] <= time() );
228 $logDesc =
"$func: \"$prefixedName\", \"$value\", \"" .
229 implode(
'", "', array_map(
'strval', $setOptions ) ) .
'"';
230 $optionsForDeduplication = [ $func, $prefixedName, $value, $setOptions ];
232 if ( $deleting && !isset( self::$setCookies[$key] ) ) {
233 wfDebugLog(
'cookie',
"already deleted $logDesc" );
235 } elseif ( !$deleting && isset( self::$setCookies[$key] ) &&
236 self::$setCookies[$key] === $optionsForDeduplication
238 wfDebugLog(
'cookie',
"already set $logDesc" );
243 if ( $func ===
'setrawcookie' ) {
244 setrawcookie( $prefixedName, $value, $setOptions );
246 setcookie( $prefixedName, $value, $setOptions );
248 self::$setCookies[$key] = $deleting ? null : $optionsForDeduplication;