PageRenderTime 54ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

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

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