[^=]+)=\s*("?)(?P[^"]*)\2#', $keyValue, $matches)) { $headerKey = $matches['headerKey']; $headerValue= $matches['headerValue']; } else { $headerKey = $keyValue; $headerValue = null; } // First K=V pair is always the cookie name and value if ($header->getName() === NULL) { $header->setName($headerKey); $header->setValue(urldecode($headerValue)); continue; } // Process the remaining elements switch (str_replace(array('-', '_'), '', strtolower($headerKey))) { case 'expires' : $header->setExpires($headerValue); break; case 'domain' : $header->setDomain($headerValue); break; case 'path' : $header->setPath($headerValue); break; case 'secure' : $header->setSecure(true); break; case 'httponly': $header->setHttponly(true); break; case 'version' : $header->setVersion((int) $headerValue); break; case 'maxage' : $header->setMaxAge((int) $headerValue); break; default: // Intentionally omitted } } return $header; }; } list($name, $value) = GenericHeader::splitHeaderLine($headerLine); // some sites return set-cookie::value, this is to get rid of the second : $name = (strtolower($name) =='set-cookie:') ? 'set-cookie' : $name; // check to ensure proper header type for this factory if (strtolower($name) !== 'set-cookie') { throw new Exception\InvalidArgumentException('Invalid header line for Set-Cookie string: "' . $name . '"'); } $multipleHeaders = preg_split('#(?type = 'Cookie'; $this->setName($name) ->setValue($value) ->setVersion($version) ->setMaxAge($maxAge) ->setDomain($domain) ->setExpires($expires) ->setPath($path) ->setSecure($secure) ->setHttpOnly($httponly); } /** * @return string 'Set-Cookie' */ public function getFieldName() { return 'Set-Cookie'; } /** * @throws Exception\RuntimeException * @return string */ public function getFieldValue() { if ($this->getName() == '') { return ''; } $value = urlencode($this->getValue()); if ( $this->hasQuoteFieldValue() ) { $value = '"'. $value . '"'; } $fieldValue = $this->getName() . '=' . $value; $version = $this->getVersion(); if ($version!==null) { $fieldValue .= '; Version=' . $version; } $maxAge = $this->getMaxAge(); if ($maxAge!==null) { $fieldValue .= '; Max-Age=' . $maxAge; } $expires = $this->getExpires(); if ($expires) { $fieldValue .= '; Expires=' . $expires; } $domain = $this->getDomain(); if ($domain) { $fieldValue .= '; Domain=' . $domain; } $path = $this->getPath(); if ($path) { $fieldValue .= '; Path=' . $path; } if ($this->isSecure()) { $fieldValue .= '; Secure'; } if ($this->isHttponly()) { $fieldValue .= '; HttpOnly'; } return $fieldValue; } /** * @param string $name * @throws Exception\InvalidArgumentException * @return SetCookie */ public function setName($name) { $this->name = $name; return $this; } /** * @return string */ public function getName() { return $this->name; } /** * @param string $value * @return SetCookie */ public function setValue($value) { $this->value = $value; return $this; } /** * @return string */ public function getValue() { return $this->value; } /** * Set version * * @param int $version * @throws Exception\InvalidArgumentException * @return SetCookie */ public function setVersion($version) { if ($version !== null && !is_int($version)) { throw new Exception\InvalidArgumentException('Invalid Version number specified'); } $this->version = $version; return $this; } /** * Get version * * @return int */ public function getVersion() { return $this->version; } /** * Set Max-Age * * @param int $maxAge * @throws Exception\InvalidArgumentException * @return SetCookie */ public function setMaxAge($maxAge) { if ($maxAge !== null && (!is_int($maxAge) || ($maxAge < 0))) { throw new Exception\InvalidArgumentException('Invalid Max-Age number specified'); } $this->maxAge = $maxAge; return $this; } /** * Get Max-Age * * @return int */ public function getMaxAge() { return $this->maxAge; } /** * @param int|string $expires * @throws Exception\InvalidArgumentException * @return SetCookie */ public function setExpires($expires) { if ($expires === null) { $this->expires = null; return $this; } $tsExpires = $expires; if (is_string($expires)) { $tsExpires = strtotime($expires); // if $tsExpires is invalid and PHP is compiled as 32bit. Check if it fail reason is the 2038 bug if (!is_int($tsExpires) && PHP_INT_SIZE === 4) { $dateTime = new \DateTime($expires); if ( $dateTime->format('Y') > 2038) { $tsExpires = PHP_INT_MAX; } } } if (!is_int($tsExpires) || $tsExpires < 0) { throw new Exception\InvalidArgumentException('Invalid expires time specified'); } $this->expires = $tsExpires; return $this; } /** * @param bool $inSeconds * @return int|string */ public function getExpires($inSeconds = false) { if ($this->expires === null) { return; } if ($inSeconds) { return $this->expires; } return gmdate('D, d-M-Y H:i:s', $this->expires) . ' GMT'; } /** * @param string $domain * @return SetCookie */ public function setDomain($domain) { $this->domain = $domain; return $this; } /** * @return string */ public function getDomain() { return $this->domain; } /** * @param string $path * @return SetCookie */ public function setPath($path) { $this->path = $path; return $this; } /** * @return string */ public function getPath() { return $this->path; } /** * @param bool $secure * @return SetCookie */ public function setSecure($secure) { $this->secure = $secure; return $this; } /** * @param bool $quotedValue * @return SetCookie */ public function setQuoteFieldValue($quotedValue) { $this->quoteFieldValue = (bool) $quotedValue; return $this; } /** * @return bool */ public function isSecure() { return $this->secure; } /** * @param bool $httponly * @return SetCookie */ public function setHttponly($httponly) { $this->httponly = $httponly; return $this; } /** * @return bool */ public function isHttponly() { return $this->httponly; } /** * Check whether the cookie has expired * * Always returns false if the cookie is a session cookie (has no expiry time) * * @param int $now Timestamp to consider as "now" * @return bool */ public function isExpired($now = null) { if ($now === null) { $now = time(); } if (is_int($this->expires) && $this->expires < $now) { return true; } return false; } /** * Check whether the cookie is a session cookie (has no expiry time set) * * @return bool */ public function isSessionCookie() { return ($this->expires === null); } /** * Check whether the cookie is a session cookie (has no expiry time set) * * @return bool */ public function hasQuoteFieldValue() { return $this->quoteFieldValue; } public function isValidForRequest($requestDomain, $path, $isSecure = false) { if ($this->getDomain() && (strrpos($requestDomain, $this->getDomain()) === false)) { return false; } if ($this->getPath() && (strpos($path, $this->getPath()) !== 0)) { return false; } if ($this->secure && $this->isSecure()!==$isSecure) { return false; } return true; } /** * Checks whether the cookie should be sent or not in a specific scenario * * @param string|Zend\Uri\Uri $uri URI to check against (secure, domain, path) * @param bool $matchSessionCookies Whether to send session cookies * @param int $now Override the current time when checking for expiry time * @return bool */ public function match($uri, $matchSessionCookies = true, $now = null) { if (is_string ($uri)) { $uri = UriFactory::factory($uri); } // Make sure we have a valid Zend_Uri_Http object if (! ($uri->isValid() && ($uri->getScheme() == 'http' || $uri->getScheme() =='https'))) { throw new Exception\InvalidArgumentException('Passed URI is not a valid HTTP or HTTPS URI'); } // Check that the cookie is secure (if required) and not expired if ($this->secure && $uri->getScheme() != 'https') return false; if ($this->isExpired($now)) return false; if ($this->isSessionCookie() && ! $matchSessionCookies) return false; // Check if the domain matches if (! self::matchCookieDomain($this->getDomain(), $uri->getHost())) { return false; } // Check that path matches using prefix match if (! self::matchCookiePath($this->getPath(), $uri->getPath())) { return false; } // If we didn't die until now, return true. return true; } /** * Check if a cookie's domain matches a host name. * * Used by Zend\Http\Cookies for cookie matching * * @param string $cookieDomain * @param string $host * * @return bool */ public static function matchCookieDomain($cookieDomain, $host) { if (! $cookieDomain) { throw new Exception\InvalidArgumentException('$cookieDomain is expected to be a cookie domain'); } if (! $host) { throw new Exception\InvalidArgumentException('$host is expected to be a host name'); } $cookieDomain = strtolower($cookieDomain); $host = strtolower($host); // Check for either exact match or suffix match return ($cookieDomain == $host || preg_match('/' . preg_quote($cookieDomain) . '$/', $host)); } /** * Check if a cookie's path matches a URL path * * Used by Zend\Http\Cookies for cookie matching * * @param string $cookiePath * @param string $path * @return bool */ public static function matchCookiePath($cookiePath, $path) { if (! $cookiePath) { throw new Exception\InvalidArgumentException('$cookiePath is expected to be a cookie path'); } if (! $path) { throw new Exception\InvalidArgumentException('$path is expected to be a host name'); } return (strpos($path, $cookiePath) === 0); } public function toString() { return 'Set-Cookie: ' . $this->getFieldValue(); } public function toStringMultipleHeaders(array $headers) { $headerLine = $this->toString(); /* @var $header SetCookie */ foreach ($headers as $header) { if (!$header instanceof SetCookie) { throw new Exception\RuntimeException( 'The SetCookie multiple header implementation can only accept an array of SetCookie headers' ); } $headerLine .= "\n" . $header->toString(); } return $headerLine; } }