MediaWiki  master
SetCookieCompat.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Wikimedia\Http;
4 
21  public static function setcookie( $name, $value, $options = [] ) {
22  return ( new self )->setCookieInternal( true, $name, $value, $options );
23  }
24 
36  public static function setrawcookie( $name, $value, $options = [] ) {
37  return ( new self )->setCookieInternal( false, $name, $value, $options );
38  }
39 
48  public function setCookieInternal( $urlEncode, $name, $value, $options = [] ) {
49  $supportsAssoc = version_compare( PHP_VERSION, '7.3.0', '>=' );
50  if ( $supportsAssoc ) {
51  if ( $urlEncode ) {
52  return setcookie( $name, $value, $options );
53  } else {
54  return setrawcookie( $name, $value, $options );
55  }
56  }
57 
58  if ( !isset( $options['samesite'] ) || !strlen( $options['samesite'] ) ) {
59  if ( $urlEncode ) {
60  return setcookie(
61  $name,
62  $value,
63  $options['expires'],
64  $options['path'],
65  $options['domain'],
66  $options['secure'],
67  $options['httponly']
68  );
69  } else {
70  return setrawcookie(
71  $name,
72  $value,
73  $options['expires'],
74  $options['path'],
75  $options['domain'],
76  $options['secure'],
77  $options['httponly']
78  );
79  }
80  }
81 
82  return self::setCookieEmulated( $urlEncode, $name, $value, $options );
83  }
84 
98  public function setCookieEmulated( $urlEncode, $name, $value, $options = [] ) {
99  $func = $urlEncode ? 'setcookie()' : 'setrawcookie()';
100  $expires = 0;
101  $path = null;
102  $domain = null;
103  $secure = false;
104  $httponly = false;
105  $samesite = null;
106  $found = 0;
107  foreach ( $options as $key => $opt ) {
108  if ( $key === 'expires' ) {
109  $expires = (int)$opt;
110  $found++;
111  } elseif ( $key === 'path' ) {
112  $path = (string)$opt;
113  $found++;
114  } elseif ( $key === 'domain' ) {
115  $domain = (string)$opt;
116  $found++;
117  } elseif ( $key === 'secure' ) {
118  $secure = (bool)$opt;
119  $found++;
120  } elseif ( $key === 'httponly' ) {
121  $httponly = (bool)$opt;
122  $found++;
123  } elseif ( $key === 'samesite' ) {
124  $samesite = (string)$opt;
125  $found++;
126  } else {
127  $this->error( "$func: Unrecognized key '$key' found in the options array" );
128  }
129  }
130 
131  if ( $found == 0 && count( $options ) > 0 ) {
132  $this->error( "$func: No valid options were found in the given array" );
133  }
134 
135  if ( !strlen( $name ) ) {
136  $this->error( 'Cookie names must not be empty' );
137  return false;
138  } elseif ( strpbrk( $name, "=,; \t\r\n\013\014" ) !== false ) {
139  $this->error( "Cookie names cannot contain any of the following " .
140  "'=,; \\t\\r\\n\\013\\014'" );
141  return false;
142  }
143 
144  if ( !$urlEncode && $value !== null
145  && strpbrk( $value, ",; \t\r\n\013\014" ) !== false
146  ) {
147  $this->error( "Cookie values cannot contain any of the following ',; \\t\\r\\n\\013\\014'" );
148  return false;
149  }
150 
151  if ( $path !== null && strpbrk( $path, ",; \t\r\n\013\014" ) !== false ) {
152  $this->error( "Cookie paths cannot contain any of the following ',; \\t\\r\\n\\013\\014'" );
153  return false;
154  }
155 
156  if ( $domain !== null && strpbrk( $domain, ",; \t\r\n\013\014" ) !== false ) {
157  $this->error( "Cookie domains cannot contain any of the following ',; \\t\\r\\n\\013\\014'" );
158  return false;
159  }
160 
161  $buf = '';
162  if ( $value === null || strlen( $value ) === 0 ) {
163  $dt = gmdate( "D, d-M-Y H:i:s T", 1 );
164  $buf .= "Set-Cookie: $name=deleted; expires=$dt; Max-Age=0";
165  } else {
166  $buf .= "Set-Cookie: $name=";
167  if ( $urlEncode ) {
168  $buf .= urlencode( $value );
169  } else {
170  $buf .= $value;
171  }
172 
173  if ( $expires > 0 ) {
174  $dt = gmdate( "D, d-M-Y H:i:s T", $expires );
175  $p = strrpos( $dt, '-' );
176  if ( $p === false || substr( $dt, $p + 5, 1 ) !== ' ' ) {
177  $this->error( "Expiry date cannot have a year greater than 9999" );
178  return false;
179  }
180 
181  $buf .= "; expires=$dt";
182 
183  $diff = $expires - $this->time();
184  if ( $diff < 0 ) {
185  $diff = 0;
186  }
187  $buf .= "; Max-Age=$diff";
188  }
189  }
190 
191  if ( $path !== null && strlen( $path ) ) {
192  $buf .= "; path=$path";
193  }
194  if ( $domain !== null && strlen( $domain ) ) {
195  $buf .= "; domain=$domain";
196  }
197  if ( $secure ) {
198  $buf .= "; secure";
199  }
200  if ( $httponly ) {
201  $buf .= "; HttpOnly";
202  }
203  if ( $samesite !== null && strlen( $samesite ) ) {
204  $buf .= "; SameSite=$samesite";
205  }
206 
207  // sapi_header_op() returns a value which setcookie() uses, but
208  // header() discards it. The most likely way for sapi_header_op() to
209  // fail is due to headers already being sent.
210  if ( $this->headers_sent() ) {
211  $this->error( "Cannot modify header information - headers already sent" );
212  return false;
213  }
214  $this->header( $buf );
215  return true;
216  }
217 
218  protected function time() {
219  return time();
220  }
221 
222  protected function error( $message ) {
223  trigger_error( $message, E_USER_WARNING );
224  }
225 
226  protected function headers_sent() {
227  return headers_sent();
228  }
229 
230  protected function header( $header ) {
231  header( $header, false );
232  }
233 }
Wikimedia\Http\SetCookieCompat\setrawcookie
static setrawcookie( $name, $value, $options=[])
Temporary emulation for setrawcookie() with a SameSite option.
Definition: SetCookieCompat.php:36
Wikimedia\Http\SetCookieCompat
Definition: SetCookieCompat.php:9
Wikimedia\Http\SetCookieCompat\error
error( $message)
Definition: SetCookieCompat.php:222
Wikimedia\Http\SetCookieCompat\header
header( $header)
Definition: SetCookieCompat.php:230
Wikimedia\Http\SetCookieCompat\setCookieEmulated
setCookieEmulated( $urlEncode, $name, $value, $options=[])
Temporary emulation for setcookie() with a SameSite option.
Definition: SetCookieCompat.php:98
Wikimedia\Http
Utility for parsing a HTTP Accept header value into a weight map.
Definition: HttpAcceptNegotiator.php:3
Wikimedia\Http\SetCookieCompat\time
time()
Definition: SetCookieCompat.php:218
$header
$header
Definition: updateCredits.php:37
$path
$path
Definition: NoLocalSettings.php:25
Wikimedia\Http\SetCookieCompat\setCookieInternal
setCookieInternal( $urlEncode, $name, $value, $options=[])
Definition: SetCookieCompat.php:48
Wikimedia\Http\SetCookieCompat\headers_sent
headers_sent()
Definition: SetCookieCompat.php:226
Wikimedia\Http\SetCookieCompat\setcookie
static setcookie( $name, $value, $options=[])
Temporary emulation for setcookie() with a SameSite option.
Definition: SetCookieCompat.php:21