Cookie.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\BrowserKit;
  11. /**
  12. * Cookie represents an HTTP cookie.
  13. *
  14. * @author Fabien Potencier <fabien@symfony.com>
  15. *
  16. * @api
  17. */
  18. class Cookie
  19. {
  20. /**
  21. * Handles dates as defined by RFC 2616 section 3.3.1, and also some other
  22. * non-standard, but common formats.
  23. *
  24. * @var array
  25. */
  26. private static $dateFormats = array(
  27. 'D, d M Y H:i:s T',
  28. 'D, d-M-y H:i:s T',
  29. 'D, d-M-Y H:i:s T',
  30. 'D, d-m-y H:i:s T',
  31. 'D, d-m-Y H:i:s T',
  32. 'D M j G:i:s Y',
  33. 'D M d H:i:s Y T',
  34. );
  35. protected $name;
  36. protected $value;
  37. protected $expires;
  38. protected $path;
  39. protected $domain;
  40. protected $secure;
  41. protected $httponly;
  42. protected $rawValue;
  43. /**
  44. * Sets a cookie.
  45. *
  46. * @param string $name The cookie name
  47. * @param string $value The value of the cookie
  48. * @param string $expires The time the cookie expires
  49. * @param string $path The path on the server in which the cookie will be available on
  50. * @param string $domain The domain that the cookie is available
  51. * @param Boolean $secure Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client
  52. * @param Boolean $httponly The cookie httponly flag
  53. * @param Boolean $encodedValue Whether the value is encoded or not
  54. *
  55. * @api
  56. */
  57. public function __construct($name, $value, $expires = null, $path = null, $domain = '', $secure = false, $httponly = true, $encodedValue = false)
  58. {
  59. if ($encodedValue) {
  60. $this->value = urldecode($value);
  61. $this->rawValue = $value;
  62. } else {
  63. $this->value = $value;
  64. $this->rawValue = urlencode($value);
  65. }
  66. $this->name = $name;
  67. $this->expires = null === $expires ? null : (integer) $expires;
  68. $this->path = empty($path) ? '/' : $path;
  69. $this->domain = $domain;
  70. $this->secure = (Boolean) $secure;
  71. $this->httponly = (Boolean) $httponly;
  72. }
  73. /**
  74. * Returns the HTTP representation of the Cookie.
  75. *
  76. * @return string The HTTP representation of the Cookie
  77. *
  78. * @api
  79. */
  80. public function __toString()
  81. {
  82. $cookie = sprintf('%s=%s', $this->name, $this->rawValue);
  83. if (null !== $this->expires) {
  84. $cookie .= '; expires='.substr(\DateTime::createFromFormat('U', $this->expires, new \DateTimeZone('GMT'))->format(self::$dateFormats[0]), 0, -5);
  85. }
  86. if ('' !== $this->domain) {
  87. $cookie .= '; domain='.$this->domain;
  88. }
  89. if ($this->path) {
  90. $cookie .= '; path='.$this->path;
  91. }
  92. if ($this->secure) {
  93. $cookie .= '; secure';
  94. }
  95. if ($this->httponly) {
  96. $cookie .= '; httponly';
  97. }
  98. return $cookie;
  99. }
  100. /**
  101. * Creates a Cookie instance from a Set-Cookie header value.
  102. *
  103. * @param string $cookie A Set-Cookie header value
  104. * @param string $url The base URL
  105. *
  106. * @return Cookie A Cookie instance
  107. *
  108. * @throws \InvalidArgumentException
  109. *
  110. * @api
  111. */
  112. public static function fromString($cookie, $url = null)
  113. {
  114. $parts = explode(';', $cookie);
  115. if (false === strpos($parts[0], '=')) {
  116. throw new \InvalidArgumentException('The cookie string "%s" is not valid.');
  117. }
  118. list($name, $value) = explode('=', array_shift($parts), 2);
  119. $values = array(
  120. 'name' => trim($name),
  121. 'value' => trim($value),
  122. 'expires' => null,
  123. 'path' => '/',
  124. 'domain' => '',
  125. 'secure' => false,
  126. 'httponly' => false,
  127. 'passedRawValue' => true,
  128. );
  129. if (null !== $url) {
  130. if ((false === $urlParts = parse_url($url)) || !isset($urlParts['host']) || !isset($urlParts['path'])) {
  131. throw new \InvalidArgumentException(sprintf('The URL "%s" is not valid.', $url));
  132. }
  133. $values['domain'] = $urlParts['host'];
  134. $values['path'] = substr($urlParts['path'], 0, strrpos($urlParts['path'], '/'));
  135. }
  136. foreach ($parts as $part) {
  137. $part = trim($part);
  138. if ('secure' === strtolower($part)) {
  139. // Ignore the secure flag if the original URI is not given or is not HTTPS
  140. if (!$url || !isset($urlParts['scheme']) || 'https' != $urlParts['scheme']) {
  141. continue;
  142. }
  143. $values['secure'] = true;
  144. continue;
  145. }
  146. if ('httponly' === strtolower($part)) {
  147. $values['httponly'] = true;
  148. continue;
  149. }
  150. if (2 === count($elements = explode('=', $part, 2))) {
  151. if ('expires' === strtolower($elements[0])) {
  152. $elements[1] = self::parseDate($elements[1]);
  153. }
  154. $values[strtolower($elements[0])] = $elements[1];
  155. }
  156. }
  157. return new static(
  158. $values['name'],
  159. $values['value'],
  160. $values['expires'],
  161. $values['path'],
  162. $values['domain'],
  163. $values['secure'],
  164. $values['httponly'],
  165. $values['passedRawValue']
  166. );
  167. }
  168. private static function parseDate($dateValue)
  169. {
  170. // trim single quotes around date if present
  171. if (($length = strlen($dateValue)) > 1 && "'" === $dateValue[0] && "'" === $dateValue[$length-1]) {
  172. $dateValue = substr($dateValue, 1, -1);
  173. }
  174. foreach (self::$dateFormats as $dateFormat) {
  175. if (false !== $date = \DateTime::createFromFormat($dateFormat, $dateValue, new \DateTimeZone('GMT'))) {
  176. return $date->getTimestamp();
  177. }
  178. }
  179. // attempt a fallback for unusual formatting
  180. if (false !== $date = date_create($dateValue, new \DateTimeZone('GMT'))) {
  181. return $date->getTimestamp();
  182. }
  183. throw new \InvalidArgumentException(sprintf('Could not parse date "%s".', $dateValue));
  184. }
  185. /**
  186. * Gets the name of the cookie.
  187. *
  188. * @return string The cookie name
  189. *
  190. * @api
  191. */
  192. public function getName()
  193. {
  194. return $this->name;
  195. }
  196. /**
  197. * Gets the value of the cookie.
  198. *
  199. * @return string The cookie value
  200. *
  201. * @api
  202. */
  203. public function getValue()
  204. {
  205. return $this->value;
  206. }
  207. /**
  208. * Gets the raw value of the cookie.
  209. *
  210. * @return string The cookie value
  211. *
  212. * @api
  213. */
  214. public function getRawValue()
  215. {
  216. return $this->rawValue;
  217. }
  218. /**
  219. * Gets the expires time of the cookie.
  220. *
  221. * @return string The cookie expires time
  222. *
  223. * @api
  224. */
  225. public function getExpiresTime()
  226. {
  227. return $this->expires;
  228. }
  229. /**
  230. * Gets the path of the cookie.
  231. *
  232. * @return string The cookie path
  233. *
  234. * @api
  235. */
  236. public function getPath()
  237. {
  238. return $this->path;
  239. }
  240. /**
  241. * Gets the domain of the cookie.
  242. *
  243. * @return string The cookie domain
  244. *
  245. * @api
  246. */
  247. public function getDomain()
  248. {
  249. return $this->domain;
  250. }
  251. /**
  252. * Returns the secure flag of the cookie.
  253. *
  254. * @return Boolean The cookie secure flag
  255. *
  256. * @api
  257. */
  258. public function isSecure()
  259. {
  260. return $this->secure;
  261. }
  262. /**
  263. * Returns the httponly flag of the cookie.
  264. *
  265. * @return Boolean The cookie httponly flag
  266. *
  267. * @api
  268. */
  269. public function isHttpOnly()
  270. {
  271. return $this->httponly;
  272. }
  273. /**
  274. * Returns true if the cookie has expired.
  275. *
  276. * @return Boolean true if the cookie has expired, false otherwise
  277. *
  278. * @api
  279. */
  280. public function isExpired()
  281. {
  282. return null !== $this->expires && 0 !== $this->expires && $this->expires < time();
  283. }
  284. }