class-wp-http-cookie.php (7186B)
1 <?php 2 /** 3 * HTTP API: WP_Http_Cookie class 4 * 5 * @package WordPress 6 * @subpackage HTTP 7 * @since 4.4.0 8 */ 9 10 /** 11 * Core class used to encapsulate a single cookie object for internal use. 12 * 13 * Returned cookies are represented using this class, and when cookies are set, if they are not 14 * already a WP_Http_Cookie() object, then they are turned into one. 15 * 16 * @todo The WordPress convention is to use underscores instead of camelCase for function and method 17 * names. Need to switch to use underscores instead for the methods. 18 * 19 * @since 2.8.0 20 */ 21 class WP_Http_Cookie { 22 23 /** 24 * Cookie name. 25 * 26 * @since 2.8.0 27 * @var string 28 */ 29 public $name; 30 31 /** 32 * Cookie value. 33 * 34 * @since 2.8.0 35 * @var string 36 */ 37 public $value; 38 39 /** 40 * When the cookie expires. Unix timestamp or formatted date. 41 * 42 * @since 2.8.0 43 * @var string|int|null 44 */ 45 public $expires; 46 47 /** 48 * Cookie URL path. 49 * 50 * @since 2.8.0 51 * @var string 52 */ 53 public $path; 54 55 /** 56 * Cookie Domain. 57 * 58 * @since 2.8.0 59 * @var string 60 */ 61 public $domain; 62 63 /** 64 * host-only flag. 65 * 66 * @since 5.2.0 67 * @var bool 68 */ 69 public $host_only; 70 71 /** 72 * Sets up this cookie object. 73 * 74 * The parameter $data should be either an associative array containing the indices names below 75 * or a header string detailing it. 76 * 77 * @since 2.8.0 78 * @since 5.2.0 Added `host_only` to the `$data` parameter. 79 * 80 * @param string|array $data { 81 * Raw cookie data as header string or data array. 82 * 83 * @type string $name Cookie name. 84 * @type mixed $value Value. Should NOT already be urlencoded. 85 * @type string|int|null $expires Optional. Unix timestamp or formatted date. Default null. 86 * @type string $path Optional. Path. Default '/'. 87 * @type string $domain Optional. Domain. Default host of parsed $requested_url. 88 * @type int $port Optional. Port. Default null. 89 * @type bool $host_only Optional. host-only storage flag. Default true. 90 * } 91 * @param string $requested_url The URL which the cookie was set on, used for default $domain 92 * and $port values. 93 */ 94 public function __construct( $data, $requested_url = '' ) { 95 if ( $requested_url ) { 96 $arrURL = parse_url( $requested_url ); 97 } 98 if ( isset( $arrURL['host'] ) ) { 99 $this->domain = $arrURL['host']; 100 } 101 $this->path = isset( $arrURL['path'] ) ? $arrURL['path'] : '/'; 102 if ( '/' !== substr( $this->path, -1 ) ) { 103 $this->path = dirname( $this->path ) . '/'; 104 } 105 106 if ( is_string( $data ) ) { 107 // Assume it's a header string direct from a previous request. 108 $pairs = explode( ';', $data ); 109 110 // Special handling for first pair; name=value. Also be careful of "=" in value. 111 $name = trim( substr( $pairs[0], 0, strpos( $pairs[0], '=' ) ) ); 112 $value = substr( $pairs[0], strpos( $pairs[0], '=' ) + 1 ); 113 $this->name = $name; 114 $this->value = urldecode( $value ); 115 116 // Removes name=value from items. 117 array_shift( $pairs ); 118 119 // Set everything else as a property. 120 foreach ( $pairs as $pair ) { 121 $pair = rtrim( $pair ); 122 123 // Handle the cookie ending in ; which results in a empty final pair. 124 if ( empty( $pair ) ) { 125 continue; 126 } 127 128 list( $key, $val ) = strpos( $pair, '=' ) ? explode( '=', $pair ) : array( $pair, '' ); 129 $key = strtolower( trim( $key ) ); 130 if ( 'expires' === $key ) { 131 $val = strtotime( $val ); 132 } 133 $this->$key = $val; 134 } 135 } else { 136 if ( ! isset( $data['name'] ) ) { 137 return; 138 } 139 140 // Set properties based directly on parameters. 141 foreach ( array( 'name', 'value', 'path', 'domain', 'port', 'host_only' ) as $field ) { 142 if ( isset( $data[ $field ] ) ) { 143 $this->$field = $data[ $field ]; 144 } 145 } 146 147 if ( isset( $data['expires'] ) ) { 148 $this->expires = is_int( $data['expires'] ) ? $data['expires'] : strtotime( $data['expires'] ); 149 } else { 150 $this->expires = null; 151 } 152 } 153 } 154 155 /** 156 * Confirms that it's OK to send this cookie to the URL checked against. 157 * 158 * Decision is based on RFC 2109/2965, so look there for details on validity. 159 * 160 * @since 2.8.0 161 * 162 * @param string $url URL you intend to send this cookie to 163 * @return bool true if allowed, false otherwise. 164 */ 165 public function test( $url ) { 166 if ( is_null( $this->name ) ) { 167 return false; 168 } 169 170 // Expires - if expired then nothing else matters. 171 if ( isset( $this->expires ) && time() > $this->expires ) { 172 return false; 173 } 174 175 // Get details on the URL we're thinking about sending to. 176 $url = parse_url( $url ); 177 $url['port'] = isset( $url['port'] ) ? $url['port'] : ( 'https' === $url['scheme'] ? 443 : 80 ); 178 $url['path'] = isset( $url['path'] ) ? $url['path'] : '/'; 179 180 // Values to use for comparison against the URL. 181 $path = isset( $this->path ) ? $this->path : '/'; 182 $port = isset( $this->port ) ? $this->port : null; 183 $domain = isset( $this->domain ) ? strtolower( $this->domain ) : strtolower( $url['host'] ); 184 if ( false === stripos( $domain, '.' ) ) { 185 $domain .= '.local'; 186 } 187 188 // Host - very basic check that the request URL ends with the domain restriction (minus leading dot). 189 $domain = ( '.' === substr( $domain, 0, 1 ) ) ? substr( $domain, 1 ) : $domain; 190 if ( substr( $url['host'], -strlen( $domain ) ) != $domain ) { 191 return false; 192 } 193 194 // Port - supports "port-lists" in the format: "80,8000,8080". 195 if ( ! empty( $port ) && ! in_array( $url['port'], array_map( 'intval', explode( ',', $port ) ), true ) ) { 196 return false; 197 } 198 199 // Path - request path must start with path restriction. 200 if ( substr( $url['path'], 0, strlen( $path ) ) != $path ) { 201 return false; 202 } 203 204 return true; 205 } 206 207 /** 208 * Convert cookie name and value back to header string. 209 * 210 * @since 2.8.0 211 * 212 * @return string Header encoded cookie name and value. 213 */ 214 public function getHeaderValue() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid 215 if ( ! isset( $this->name ) || ! isset( $this->value ) ) { 216 return ''; 217 } 218 219 /** 220 * Filters the header-encoded cookie value. 221 * 222 * @since 3.4.0 223 * 224 * @param string $value The cookie value. 225 * @param string $name The cookie name. 226 */ 227 return $this->name . '=' . apply_filters( 'wp_http_cookie_value', $this->value, $this->name ); 228 } 229 230 /** 231 * Retrieve cookie header for usage in the rest of the WordPress HTTP API. 232 * 233 * @since 2.8.0 234 * 235 * @return string 236 */ 237 public function getFullHeader() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid 238 return 'Cookie: ' . $this->getHeaderValue(); 239 } 240 241 /** 242 * Retrieves cookie attributes. 243 * 244 * @since 4.6.0 245 * 246 * @return array { 247 * List of attributes. 248 * 249 * @type string|int|null $expires When the cookie expires. Unix timestamp or formatted date. 250 * @type string $path Cookie URL path. 251 * @type string $domain Cookie domain. 252 * } 253 */ 254 public function get_attributes() { 255 return array( 256 'expires' => $this->expires, 257 'path' => $this->path, 258 'domain' => $this->domain, 259 ); 260 } 261 }