PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/zendframework/zendframework/library/Zend/Http/Header/SetCookie.php

https://bitbucket.org/Evgenii/zf2t
PHP | 619 lines | 324 code | 88 blank | 207 comment | 65 complexity | 2c8eab7e05493e4521b4571e1100d67a MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Http\Header;
  10. use Closure;
  11. use Zend\Uri\UriFactory;
  12. /**
  13. * @throws Exception\InvalidArgumentException
  14. * @see http://www.ietf.org/rfc/rfc2109.txt
  15. * @see http://www.w3.org/Protocols/rfc2109/rfc2109
  16. */
  17. class SetCookie implements MultipleHeaderInterface
  18. {
  19. /**
  20. * Cookie name
  21. *
  22. * @var string
  23. */
  24. protected $name = null;
  25. /**
  26. * Cookie value
  27. *
  28. * @var string
  29. */
  30. protected $value = null;
  31. /**
  32. * Version
  33. *
  34. * @var int
  35. */
  36. protected $version = null;
  37. /**
  38. * Max Age
  39. *
  40. * @var int
  41. */
  42. protected $maxAge = null;
  43. /**
  44. * Cookie expiry date
  45. *
  46. * @var int
  47. */
  48. protected $expires = null;
  49. /**
  50. * Cookie domain
  51. *
  52. * @var string
  53. */
  54. protected $domain = null;
  55. /**
  56. * Cookie path
  57. *
  58. * @var string
  59. */
  60. protected $path = null;
  61. /**
  62. * Whether the cookie is secure or not
  63. *
  64. * @var bool
  65. */
  66. protected $secure = null;
  67. /**
  68. * @var bool|null
  69. */
  70. protected $httponly = null;
  71. /**
  72. * @static
  73. * @throws Exception\InvalidArgumentException
  74. * @param $headerLine
  75. * @param bool $bypassHeaderFieldName
  76. * @return array|SetCookie
  77. */
  78. public static function fromString($headerLine, $bypassHeaderFieldName = false)
  79. {
  80. /* @var $setCookieProcessor Closure */
  81. static $setCookieProcessor = null;
  82. if ($setCookieProcessor === null) {
  83. $setCookieClass = get_called_class();
  84. $setCookieProcessor = function ($headerLine) use ($setCookieClass) {
  85. $header = new $setCookieClass;
  86. $keyValuePairs = preg_split('#;\s*#', $headerLine);
  87. foreach ($keyValuePairs as $keyValue) {
  88. if (strpos($keyValue, '=')) {
  89. list($headerKey, $headerValue) = preg_split('#=\s*#', $keyValue, 2);
  90. } else {
  91. $headerKey = $keyValue;
  92. $headerValue = null;
  93. }
  94. // First K=V pair is always the cookie name and value
  95. if ($header->getName() === NULL) {
  96. $header->setName($headerKey);
  97. $header->setValue(urldecode($headerValue));
  98. continue;
  99. }
  100. // Process the remaining elements
  101. switch (str_replace(array('-', '_'), '', strtolower($headerKey))) {
  102. case 'expires' : $header->setExpires($headerValue); break;
  103. case 'domain' : $header->setDomain($headerValue); break;
  104. case 'path' : $header->setPath($headerValue); break;
  105. case 'secure' : $header->setSecure(true); break;
  106. case 'httponly': $header->setHttponly(true); break;
  107. case 'version' : $header->setVersion((int) $headerValue); break;
  108. case 'maxage' : $header->setMaxAge((int) $headerValue); break;
  109. default:
  110. // Intentionally omitted
  111. }
  112. }
  113. return $header;
  114. };
  115. }
  116. list($name, $value) = explode(':', $headerLine, 2);
  117. $value = ltrim($value);
  118. // some sites return set-cookie::value, this is to get rid of the second :
  119. $name = (strtolower($name) =='set-cookie:') ? 'set-cookie' : $name;
  120. // check to ensure proper header type for this factory
  121. if (strtolower($name) !== 'set-cookie') {
  122. throw new Exception\InvalidArgumentException('Invalid header line for Set-Cookie string: "' . $name . '"');
  123. }
  124. $multipleHeaders = preg_split('#(?<!Sun|Mon|Tue|Wed|Thu|Fri|Sat),\s*#', $value);
  125. if (count($multipleHeaders) <= 1) {
  126. return $setCookieProcessor(array_pop($multipleHeaders));
  127. } else {
  128. $headers = array();
  129. foreach ($multipleHeaders as $headerLine) {
  130. $headers[] = $setCookieProcessor($headerLine);
  131. }
  132. return $headers;
  133. }
  134. }
  135. /**
  136. * Cookie object constructor
  137. *
  138. * @todo Add validation of each one of the parameters (legal domain, etc.)
  139. *
  140. * @param string $name
  141. * @param string $value
  142. * @param int $expires
  143. * @param string $path
  144. * @param string $domain
  145. * @param bool $secure
  146. * @param bool $httponly
  147. * @param string $maxAge
  148. * @param int $version
  149. * @return SetCookie
  150. */
  151. public function __construct($name = null, $value = null, $expires = null, $path = null, $domain = null, $secure = false, $httponly = false, $maxAge = null, $version = null)
  152. {
  153. $this->type = 'Cookie';
  154. if ($name) {
  155. $this->setName($name);
  156. }
  157. if ($value) {
  158. $this->setValue($value); // in parent
  159. }
  160. if ($version!==null) {
  161. $this->setVersion($version);
  162. }
  163. if ($maxAge!==null) {
  164. $this->setMaxAge($maxAge);
  165. }
  166. if ($domain) {
  167. $this->setDomain($domain);
  168. }
  169. if ($expires) {
  170. $this->setExpires($expires);
  171. }
  172. if ($path) {
  173. $this->setPath($path);
  174. }
  175. if ($secure) {
  176. $this->setSecure($secure);
  177. }
  178. if ($httponly) {
  179. $this->setHttpOnly($httponly);
  180. }
  181. }
  182. /**
  183. * @return string 'Set-Cookie'
  184. */
  185. public function getFieldName()
  186. {
  187. return 'Set-Cookie';
  188. }
  189. /**
  190. * @throws Exception\RuntimeException
  191. * @return string
  192. */
  193. public function getFieldValue()
  194. {
  195. if ($this->getName() == '') {
  196. return '';
  197. }
  198. $value = $this->getValue();
  199. if (strpos($value, '"')!==false) {
  200. $value = '"'.urlencode(str_replace('"', '', $value)).'"';
  201. } else {
  202. $value = urlencode($value);
  203. }
  204. $fieldValue = $this->getName() . '=' . $value;
  205. $version = $this->getVersion();
  206. if ($version!==null) {
  207. $fieldValue .= '; Version=' . $version;
  208. }
  209. $maxAge = $this->getMaxAge();
  210. if ($maxAge!==null) {
  211. $fieldValue .= '; Max-Age=' . $maxAge;
  212. }
  213. $expires = $this->getExpires();
  214. if ($expires) {
  215. $fieldValue .= '; Expires=' . $expires;
  216. }
  217. $domain = $this->getDomain();
  218. if ($domain) {
  219. $fieldValue .= '; Domain=' . $domain;
  220. }
  221. $path = $this->getPath();
  222. if ($path) {
  223. $fieldValue .= '; Path=' . $path;
  224. }
  225. if ($this->isSecure()) {
  226. $fieldValue .= '; Secure';
  227. }
  228. if ($this->isHttponly()) {
  229. $fieldValue .= '; HttpOnly';
  230. }
  231. return $fieldValue;
  232. }
  233. /**
  234. * @param string $name
  235. * @throws Exception\InvalidArgumentException
  236. * @return SetCookie
  237. */
  238. public function setName($name)
  239. {
  240. if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
  241. throw new Exception\InvalidArgumentException("Cookie name cannot contain these characters: =,; \\t\\r\\n\\013\\014 ({$name})");
  242. }
  243. $this->name = $name;
  244. return $this;
  245. }
  246. /**
  247. * @return string
  248. */
  249. public function getName()
  250. {
  251. return $this->name;
  252. }
  253. /**
  254. * @param string $value
  255. */
  256. public function setValue($value)
  257. {
  258. $this->value = $value;
  259. }
  260. /**
  261. * @return string
  262. */
  263. public function getValue()
  264. {
  265. return $this->value;
  266. }
  267. /**
  268. * Set version
  269. *
  270. * @param int $version
  271. * @throws Exception\InvalidArgumentException
  272. */
  273. public function setVersion($version)
  274. {
  275. if (!is_int($version)) {
  276. throw new Exception\InvalidArgumentException('Invalid Version number specified');
  277. }
  278. $this->version = $version;
  279. }
  280. /**
  281. * Get version
  282. *
  283. * @return int
  284. */
  285. public function getVersion()
  286. {
  287. return $this->version;
  288. }
  289. /**
  290. * Set Max-Age
  291. *
  292. * @param int $maxAge
  293. * @throws Exception\InvalidArgumentException
  294. */
  295. public function setMaxAge($maxAge)
  296. {
  297. if (!is_int($maxAge) || ($maxAge<0)) {
  298. throw new Exception\InvalidArgumentException('Invalid Max-Age number specified');
  299. }
  300. $this->maxAge = $maxAge;
  301. }
  302. /**
  303. * Get Max-Age
  304. *
  305. * @return int
  306. */
  307. public function getMaxAge()
  308. {
  309. return $this->maxAge;
  310. }
  311. /**
  312. * @param int $expires
  313. * @throws Exception\InvalidArgumentException
  314. * @return SetCookie
  315. */
  316. public function setExpires($expires)
  317. {
  318. if (!empty($expires)) {
  319. if (is_string($expires)) {
  320. $expires = strtotime($expires);
  321. } elseif (!is_int($expires)) {
  322. throw new Exception\InvalidArgumentException('Invalid expires time specified');
  323. }
  324. $this->expires = (int) $expires;
  325. }
  326. return $this;
  327. }
  328. /**
  329. * @param bool $inSeconds
  330. * @return int
  331. */
  332. public function getExpires($inSeconds = false)
  333. {
  334. if ($this->expires == null) {
  335. return;
  336. }
  337. if ($inSeconds) {
  338. return $this->expires;
  339. }
  340. return gmdate('D, d-M-Y H:i:s', $this->expires) . ' GMT';
  341. }
  342. /**
  343. * @param string $domain
  344. */
  345. public function setDomain($domain)
  346. {
  347. $this->domain = $domain;
  348. }
  349. /**
  350. * @return string
  351. */
  352. public function getDomain()
  353. {
  354. return $this->domain;
  355. }
  356. /**
  357. * @param string $path
  358. */
  359. public function setPath($path)
  360. {
  361. $this->path = $path;
  362. }
  363. /**
  364. * @return string
  365. */
  366. public function getPath()
  367. {
  368. return $this->path;
  369. }
  370. /**
  371. * @param bool $secure
  372. */
  373. public function setSecure($secure)
  374. {
  375. $this->secure = $secure;
  376. }
  377. /**
  378. * @return bool
  379. */
  380. public function isSecure()
  381. {
  382. return $this->secure;
  383. }
  384. /**
  385. * @param bool $httponly
  386. */
  387. public function setHttponly($httponly)
  388. {
  389. $this->httponly = $httponly;
  390. }
  391. /**
  392. * @return bool
  393. */
  394. public function isHttponly()
  395. {
  396. return $this->httponly;
  397. }
  398. /**
  399. * Check whether the cookie has expired
  400. *
  401. * Always returns false if the cookie is a session cookie (has no expiry time)
  402. *
  403. * @param int $now Timestamp to consider as "now"
  404. * @return bool
  405. */
  406. public function isExpired($now = null)
  407. {
  408. if ($now === null) {
  409. $now = time();
  410. }
  411. if (is_int($this->expires) && $this->expires < $now) {
  412. return true;
  413. }
  414. return false;
  415. }
  416. /**
  417. * Check whether the cookie is a session cookie (has no expiry time set)
  418. *
  419. * @return bool
  420. */
  421. public function isSessionCookie()
  422. {
  423. return ($this->expires === null);
  424. }
  425. public function isValidForRequest($requestDomain, $path, $isSecure = false)
  426. {
  427. if ($this->getDomain() && (strrpos($requestDomain, $this->getDomain()) === false)) {
  428. return false;
  429. }
  430. if ($this->getPath() && (strpos($path, $this->getPath()) !== 0)) {
  431. return false;
  432. }
  433. if ($this->secure && $this->isSecure()!==$isSecure) {
  434. return false;
  435. }
  436. return true;
  437. }
  438. /**
  439. * Checks whether the cookie should be sent or not in a specific scenario
  440. *
  441. * @param string|Zend\Uri\Uri $uri URI to check against (secure, domain, path)
  442. * @param bool $matchSessionCookies Whether to send session cookies
  443. * @param int $now Override the current time when checking for expiry time
  444. * @return bool
  445. */
  446. public function match($uri, $matchSessionCookies = true, $now = null)
  447. {
  448. if (is_string ($uri)) {
  449. $uri = UriFactory::factory($uri);
  450. }
  451. // Make sure we have a valid Zend_Uri_Http object
  452. if (! ($uri->isValid() && ($uri->getScheme() == 'http' || $uri->getScheme() =='https'))) {
  453. throw new Exception\InvalidArgumentException('Passed URI is not a valid HTTP or HTTPS URI');
  454. }
  455. // Check that the cookie is secure (if required) and not expired
  456. if ($this->secure && $uri->getScheme() != 'https') return false;
  457. if ($this->isExpired($now)) return false;
  458. if ($this->isSessionCookie() && ! $matchSessionCookies) return false;
  459. // Check if the domain matches
  460. if (! self::matchCookieDomain($this->getDomain(), $uri->getHost())) {
  461. return false;
  462. }
  463. // Check that path matches using prefix match
  464. if (! self::matchCookiePath($this->getPath(), $uri->getPath())) {
  465. return false;
  466. }
  467. // If we didn't die until now, return true.
  468. return true;
  469. }
  470. /**
  471. * Check if a cookie's domain matches a host name.
  472. *
  473. * Used by Zend\Http\Cookies for cookie matching
  474. *
  475. * @param string $cookieDomain
  476. * @param string $host
  477. *
  478. * @return bool
  479. */
  480. public static function matchCookieDomain($cookieDomain, $host)
  481. {
  482. if (! $cookieDomain) {
  483. throw new Exception\InvalidArgumentException('$cookieDomain is expected to be a cookie domain');
  484. }
  485. if (! $host) {
  486. throw new Exception\InvalidArgumentException('$host is expected to be a host name');
  487. }
  488. $cookieDomain = strtolower($cookieDomain);
  489. $host = strtolower($host);
  490. // Check for either exact match or suffix match
  491. return ($cookieDomain == $host ||
  492. preg_match('/' . preg_quote($cookieDomain) . '$/', $host));
  493. }
  494. /**
  495. * Check if a cookie's path matches a URL path
  496. *
  497. * Used by Zend\Http\Cookies for cookie matching
  498. *
  499. * @param string $cookiePath
  500. * @param string $path
  501. * @return bool
  502. */
  503. public static function matchCookiePath($cookiePath, $path)
  504. {
  505. if (! $cookiePath) {
  506. throw new Exception\InvalidArgumentException('$cookiePath is expected to be a cookie path');
  507. }
  508. if (! $path) {
  509. throw new Exception\InvalidArgumentException('$path is expected to be a host name');
  510. }
  511. return (strpos($path, $cookiePath) === 0);
  512. }
  513. public function toString()
  514. {
  515. return 'Set-Cookie: ' . $this->getFieldValue();
  516. }
  517. public function toStringMultipleHeaders(array $headers)
  518. {
  519. $headerLine = $this->toString();
  520. /* @var $header SetCookie */
  521. foreach ($headers as $header) {
  522. if (!$header instanceof SetCookie) {
  523. throw new Exception\RuntimeException(
  524. 'The SetCookie multiple header implementation can only accept an array of SetCookie headers'
  525. );
  526. }
  527. $headerLine .= ', ' . $header->getFieldValue();
  528. }
  529. return $headerLine;
  530. }
  531. }