Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
616 lines (529 sloc) 15.1 KB
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @package Zend_Http
*/
namespace Zend\Http\Header;
use Closure;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.ietf.org/rfc/rfc2109.txt
* @see http://www.w3.org/Protocols/rfc2109/rfc2109
*/
class SetCookie implements MultipleHeaderInterface
{
/**
* Cookie name
*
* @var string
*/
protected $name = null;
/**
* Cookie value
*
* @var string
*/
protected $value = null;
/**
* Version
*
* @var integer
*/
protected $version = null;
/**
* Max Age
*
* @var integer
*/
protected $maxAge = null;
/**
* Cookie expiry date
*
* @var int
*/
protected $expires = null;
/**
* Cookie domain
*
* @var string
*/
protected $domain = null;
/**
* Cookie path
*
* @var string
*/
protected $path = null;
/**
* Whether the cookie is secure or not
*
* @var boolean
*/
protected $secure = null;
/**
* @var boolean|null
*/
protected $httponly = null;
/**
* @static
* @throws Exception\InvalidArgumentException
* @param $headerLine
* @param bool $bypassHeaderFieldName
* @return array|SetCookie
*/
public static function fromString($headerLine, $bypassHeaderFieldName = false)
{
/* @var $setCookieProcessor Closure */
static $setCookieProcessor = null;
if ($setCookieProcessor === null) {
$setCookieClass = get_called_class();
$setCookieProcessor = function($headerLine) use ($setCookieClass) {
$header = new $setCookieClass;
$keyValuePairs = preg_split('#;\s*#', $headerLine);
foreach ($keyValuePairs as $keyValue) {
if (strpos($keyValue, '=')) {
list($headerKey, $headerValue) = preg_split('#=\s*#', $keyValue, 2);
} 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) = explode(': ', $headerLine, 2);
// 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('#(?<!Sun|Mon|Tue|Wed|Thu|Fri|Sat),\s*#', $value);
if (count($multipleHeaders) <= 1) {
return $setCookieProcessor(array_pop($multipleHeaders));
} else {
$headers = array();
foreach ($multipleHeaders as $headerLine) {
$headers[] = $setCookieProcessor($headerLine);
}
return $headers;
}
}
/**
* Cookie object constructor
*
* @todo Add validation of each one of the parameters (legal domain, etc.)
*
* @param string $name
* @param string $value
* @param int $expires
* @param string $path
* @param string $domain
* @param bool $secure
* @param bool $httponly
* @param string $maxAge
* @param int $version
* @return SetCookie
*/
public function __construct($name = null, $value = null, $expires = null, $path = null, $domain = null, $secure = false, $httponly = false, $maxAge = null, $version = null)
{
$this->type = 'Cookie';
if ($name) {
$this->setName($name);
}
if ($value) {
$this->setValue($value); // in parent
}
if ($version!==null) {
$this->setVersion($version);
}
if ($maxAge!==null) {
$this->setMaxAge($maxAge);
}
if ($domain) {
$this->setDomain($domain);
}
if ($expires) {
$this->setExpires($expires);
}
if ($path) {
$this->setPath($path);
}
if ($secure) {
$this->setSecure($secure);
}
if ($httponly) {
$this->setHttpOnly($httponly);
}
}
/**
* @return string 'Set-Cookie'
*/
public function getFieldName()
{
return 'Set-Cookie';
}
/**
* @throws Exception\RuntimeException
* @return string
*/
public function getFieldValue()
{
if ($this->getName() == '') {
throw new Exception\RuntimeException('A cookie name is required to generate a field value for this cookie');
}
$value = $this->getValue();
if (strpos($value, '"')!==false) {
$value = '"'.urlencode(str_replace('"', '', $value)).'"';
} else {
$value = urlencode($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)
{
if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
throw new Exception\InvalidArgumentException("Cookie name cannot contain these characters: =,; \\t\\r\\n\\013\\014 ({$name})");
}
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $value
*/
public function setValue($value)
{
$this->value = $value;
}
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* Set version
*
* @param integer $version
* @throws Exception\InvalidArgumentException
*/
public function setVersion($version)
{
if (!is_int($version)) {
throw new Exception\InvalidArgumentException('Invalid Version number specified');
}
$this->version = $version;
}
/**
* Get version
*
* @return integer
*/
public function getVersion()
{
return $this->version;
}
/**
* Set Max-Age
*
* @param integer $maxAge
* @throws Exception\InvalidArgumentException
*/
public function setMaxAge($maxAge)
{
if (!is_int($maxAge) || ($maxAge<0)) {
throw new Exception\InvalidArgumentException('Invalid Max-Age number specified');
}
$this->maxAge = $maxAge;
}
/**
* Get Max-Age
*
* @return integer
*/
public function getMaxAge()
{
return $this->maxAge;
}
/**
* @param int $expires
* @throws Exception\InvalidArgumentException
* @return SetCookie
*/
public function setExpires($expires)
{
if (!empty($expires)) {
if (is_string($expires)) {
$expires = strtotime($expires);
} elseif (!is_int($expires)) {
throw new Exception\InvalidArgumentException('Invalid expires time specified');
}
$this->expires = (int) $expires;
}
return $this;
}
/**
* @param bool $inSeconds
* @return int
*/
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
*/
public function setDomain($domain)
{
$this->domain = $domain;
}
/**
* @return string
*/
public function getDomain()
{
return $this->domain;
}
/**
* @param string $path
*/
public function setPath($path)
{
$this->path = $path;
}
/**
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* @param boolean $secure
*/
public function setSecure($secure)
{
$this->secure = $secure;
}
/**
* @return boolean
*/
public function isSecure()
{
return $this->secure;
}
/**
* @param boolean $httponly
*/
public function setHttponly($httponly)
{
$this->httponly = $httponly;
}
/**
* @return boolean
*/
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 boolean
*/
public function isExpired($now = null)
{
if ($now === null) {
$now = time();
}
if (is_int($this->expires) && $this->expires < $now) {
return true;
} else {
return false;
}
}
/**
* Check whether the cookie is a session cookie (has no expiry time set)
*
* @return boolean
*/
public function isSessionCookie()
{
return ($this->expires === null);
}
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 to check against (secure, domain, path)
* @param boolean $matchSessionCookies Whether to send session cookies
* @param int $now Override the current time when checking for expiry time
* @return boolean
*/
public function match($uri, $matchSessionCookies = true, $now = null)
{
if (is_string ($uri)) {
$uri = Zend\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 boolean
*/
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 boolean
*/
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 .= ', ' . $header->getFieldValue();
}
return $headerLine;
}
}
Something went wrong with that request. Please try again.