class-wp-http-cookie.php 6.3 KB

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