Index: library/Zend/Http/Header/SetCookie.php
===================================================================
--- library/Zend/Http/Header/SetCookie.php (revision 0)
+++ library/Zend/Http/Header/SetCookie.php (revision 0)
@@ -0,0 +1,546 @@
+getName() === NULL) {
+ $header->setName($headerKey);
+ $header->setValue($headerValue);
+ continue;
+ }
+
+ // Process the remanining 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
+ }
+ }
+ $headers[] = $header;
+ }
+ return count($headers) == 1 ? array_pop($headers) : $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) {
+ $this->setVersion($version);
+ }
+
+ if ($maxAge) {
+ $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 Zend_Http_Header_Exception_RuntimeException
+ * @return string
+ */
+ public function getFieldValue()
+ {
+ if ($this->getName() == '') {
+ throw new Zend_Http_Header_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
+ * @return SetCookie
+ */
+ public function setName($name)
+ {
+ if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
+ throw new Zend_Http_Header_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 $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Set version
+ *
+ * @param integer $version
+ */
+ public function setVersion($version)
+ {
+ if (!is_int($version)) {
+ throw new Zend_Http_Header_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
+ */
+ public function setMaxAge($maxAge)
+ {
+ if (!is_int($maxAge) || ($maxAge<0)) {
+ throw new Zend_Http_Header_Exception_InvalidArgumentException('Invalid Max-Age number specified');
+ }
+ $this->maxAge = $maxAge;
+ }
+
+ /**
+ * Get Max-Age
+ *
+ * @return integer
+ */
+ public function getMaxAge()
+ {
+ return $this->maxAge;
+ }
+
+ /**
+ * @param int $expires
+ * @return SetCookie
+ */
+ public function setExpires($expires)
+ {
+ if (!empty($expires)) {
+ if (is_string($expires)) {
+ $expires = strtotime($expires);
+ } elseif (!is_int($expires)) {
+ throw new Zend_Http_Header_Exception_InvalidArgumentException('Invalid expires time specified');
+ }
+ $this->expires = (int) $expires;
+ }
+ return $this;
+ }
+
+ /**
+ * @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 $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDomain()
+ {
+ return $this->domain;
+ }
+
+ /**
+ * @param string $path
+ */
+ public function setPath($path)
+ {
+ $this->path = $path;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ /**
+ * @param boolean $secure
+ */
+ public function setSecure($secure)
+ {
+ $this->secure = $secure;
+ return $this;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isSecure()
+ {
+ return $this->secure;
+ }
+
+ /**
+ * @param bool $httponly
+ */
+ 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 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;
+
+ }
+
+ public function toString()
+ {
+ return $this->getFieldName() . ': ' . $this->getFieldValue();
+ }
+
+ public function __toString()
+ {
+ return $this->toString();
+ }
+
+ public function toStringMultipleHeaders(array $headers)
+ {
+ $headerLine = $this->toString();
+ /* @var $header SetCookie */
+ foreach ($headers as $header) {
+ if (!$header instanceof Zend_Http_Header_SetCookie) {
+ throw new Zend_Http_Header_Exception_RuntimeException(
+ 'The SetCookie multiple header implementation can only accept an array of SetCookie headers'
+ );
+ }
+ $headerLine .= ', ' . $header->getFieldValue();
+ }
+ return $headerLine;
+ }
+
+
+}
Index: library/Zend/Http/Header/Exception/RuntimeException.php
===================================================================
--- library/Zend/Http/Header/Exception/RuntimeException.php (revision 0)
+++ library/Zend/Http/Header/Exception/RuntimeException.php (revision 0)
@@ -0,0 +1,36 @@
+assertEquals('myname', $setCookieHeader->getName());
+ $this->assertEquals('myvalue', $setCookieHeader->getValue());
+ $this->assertEquals('Wed, 13-Jan-2021 22:23:01 GMT', $setCookieHeader->getExpires());
+ $this->assertEquals('/accounts', $setCookieHeader->getPath());
+ $this->assertEquals('docs.foo.com', $setCookieHeader->getDomain());
+ $this->assertTrue($setCookieHeader->isSecure());
+ $this->assertTrue($setCookieHeader->isHttpOnly());
+ $this->assertEquals(99, $setCookieHeader->getMaxAge());
+ $this->assertEquals(9, $setCookieHeader->getVersion());
+ }
+
+ public function testSetCookieFromStringCreatesValidSetCookieHeader()
+ {
+ $setCookieHeader = Zend_Http_Header_SetCookie::fromString('Set-Cookie: xxx');
+ $this->assertType('Zend_Http_Header_SetCookie', $setCookieHeader);
+ }
+
+ public function testSetCookieFromStringCanCreateSingleHeader()
+ {
+ $setCookieHeader = Zend_Http_Header_SetCookie::fromString('Set-Cookie: myname=myvalue');
+ $this->assertType('Zend_Http_Header_SetCookie', $setCookieHeader);
+ $this->assertEquals('myname', $setCookieHeader->getName());
+ $this->assertEquals('myvalue', $setCookieHeader->getValue());
+
+ $setCookieHeader = Zend_Http_Header_SetCookie::fromString(
+ 'set-cookie: myname=myvalue; Domain=docs.foo.com; Path=/accounts;'
+ . 'Expires=Wed, 13-Jan-2021 22:23:01 GMT; Secure; HttpOnly'
+ );
+ $this->assertType('Zend_Http_Header_SetCookie', $setCookieHeader);
+ $this->assertEquals('myname', $setCookieHeader->getName());
+ $this->assertEquals('myvalue', $setCookieHeader->getValue());
+ $this->assertEquals('docs.foo.com', $setCookieHeader->getDomain());
+ $this->assertEquals('/accounts', $setCookieHeader->getPath());
+ $this->assertEquals('Wed, 13-Jan-2021 22:23:01 GMT', $setCookieHeader->getExpires());
+ $this->assertTrue($setCookieHeader->isSecure());
+ $this->assertTrue($setCookieHeader->isHttponly());
+ }
+
+ public function testSetCookieFromStringCanCreateMultipleHeaders()
+ {
+ $setCookieHeaders = Zend_Http_Header_SetCookie::fromString(
+ 'Set-Cookie: myname=myvalue, '
+ . 'someothername=someothervalue; Domain=docs.foo.com; Path=/accounts;'
+ . 'Expires=Wed, 13-Jan-2021 22:23:01 GMT; Secure; HttpOnly'
+ );
+ $this->assertType('array', $setCookieHeaders);
+
+ $setCookieHeader = $setCookieHeaders[0];
+ $this->assertType('Zend_Http_Header_SetCookie', $setCookieHeader);
+ $this->assertEquals('myname', $setCookieHeader->getName());
+ $this->assertEquals('myvalue', $setCookieHeader->getValue());
+
+ $setCookieHeader = $setCookieHeaders[1];
+ $this->assertType('Zend_Http_Header_SetCookie', $setCookieHeader);
+ $this->assertEquals('someothername', $setCookieHeader->getName());
+ $this->assertEquals('someothervalue', $setCookieHeader->getValue());
+ $this->assertEquals('Wed, 13-Jan-2021 22:23:01 GMT', $setCookieHeader->getExpires());
+ $this->assertEquals('docs.foo.com', $setCookieHeader->getDomain());
+ $this->assertEquals('/accounts', $setCookieHeader->getPath());
+ $this->assertTrue($setCookieHeader->isSecure());
+ $this->assertTrue($setCookieHeader->isHttponly());
+
+ }
+
+ public function testSetCookieGetFieldNameReturnsHeaderName()
+ {
+ $setCookieHeader = new Zend_Http_Header_SetCookie();
+ $this->assertEquals('Set-Cookie', $setCookieHeader->getFieldName());
+
+ }
+
+ public function testSetCookieGetFieldValueReturnsProperValue()
+ {
+ $setCookieHeader = new Zend_Http_Header_SetCookie();
+ $setCookieHeader->setName('myname');
+ $setCookieHeader->setValue('myvalue');
+ $setCookieHeader->setExpires('Wed, 13-Jan-2021 22:23:01 GMT');
+ $setCookieHeader->setDomain('docs.foo.com');
+ $setCookieHeader->setPath('/accounts');
+ $setCookieHeader->setSecure(true);
+ $setCookieHeader->setHttponly(true);
+
+ $target = 'myname=myvalue; Expires=Wed, 13-Jan-2021 22:23:01 GMT;'
+ . ' Domain=docs.foo.com; Path=/accounts;'
+ . ' Secure; HttpOnly';
+
+ $this->assertEquals($target, $setCookieHeader->getFieldValue());
+ }
+
+ public function testSetCookieToStringReturnsHeaderFormattedString()
+ {
+ $setCookieHeader = new Zend_Http_Header_SetCookie();
+ $setCookieHeader->setName('myname');
+ $setCookieHeader->setValue('myvalue');
+ $setCookieHeader->setExpires('Wed, 13-Jan-2021 22:23:01 GMT');
+ $setCookieHeader->setDomain('docs.foo.com');
+ $setCookieHeader->setPath('/accounts');
+ $setCookieHeader->setSecure(true);
+ $setCookieHeader->setHttponly(true);
+
+ $target = 'Set-Cookie: myname=myvalue; Expires=Wed, 13-Jan-2021 22:23:01 GMT;'
+ . ' Domain=docs.foo.com; Path=/accounts;'
+ . ' Secure; HttpOnly';
+
+ $this->assertEquals($target, $setCookieHeader->toString());
+ }
+
+ public function testSetCookieCanAppendOtherHeadersInWhenCreatingString()
+ {
+ $setCookieHeader = new Zend_Http_Header_SetCookie();
+ $setCookieHeader->setName('myname');
+ $setCookieHeader->setValue('myvalue');
+ $setCookieHeader->setExpires('Wed, 13-Jan-2021 22:23:01 GMT');
+ $setCookieHeader->setDomain('docs.foo.com');
+ $setCookieHeader->setPath('/accounts');
+ $setCookieHeader->setSecure(true);
+ $setCookieHeader->setHttponly(true);
+
+ $appendCookie = new Zend_Http_Header_SetCookie('othername', 'othervalue');
+ $headerLine = $setCookieHeader->toStringMultipleHeaders(array($appendCookie));
+
+ $target = 'Set-Cookie: myname=myvalue; Expires=Wed, 13-Jan-2021 22:23:01 GMT;'
+ . ' Domain=docs.foo.com; Path=/accounts;'
+ . ' Secure; HttpOnly, othername=othervalue';
+ $this->assertEquals($target, $headerLine);
+ }
+
+ /** Implmentation specific tests here */
+
+ /**
+ * ZF2-169
+ *
+ * @see http://framework.zend.com/issues/browse/ZF2-169
+ */
+ public function testZF2_169()
+ {
+ $cookie = 'Set-Cookie: leo_auth_token="example"; Version=1; Max-Age=1799; Expires=Mon, 20-Feb-2012 02:49:57 GMT; Path=/';
+ $setCookieHeader = Zend_Http_Header_SetCookie::fromString($cookie);
+ $this->assertEquals($cookie, $setCookieHeader->toString());
+ }
+
+ public function testGetFieldName()
+ {
+ $c = new Zend_Http_Header_SetCookie();
+ $this->assertEquals('Set-Cookie', $c->getFieldName());
+ }
+
+ /**
+ * @dataProvider validCookieWithInfoProvider
+ */
+ public function testGetFieldValue($cStr, $info, $expected)
+ {
+ $cookie = Zend_Http_Header_SetCookie::fromString($cStr);
+ if (! $cookie instanceof Zend_Http_Header_SetCookie) {
+ $this->fail("Failed creating a cookie object from '$cStr'");
+ }
+ $this->assertEquals($expected, $cookie->getFieldValue());
+ $this->assertEquals($cookie->getFieldName() . ': ' . $expected, (string)$cookie);
+ }
+
+ /**
+ * @dataProvider validCookieWithInfoProvider
+ */
+ public function testToString($cStr, $info, $expected)
+ {
+ $cookie = Zend_Http_Header_SetCookie::fromString($cStr);
+ if (! $cookie instanceof Zend_Http_Header_SetCookie) {
+ $this->fail("Failed creating a cookie object from '$cStr'");
+ }
+ $this->assertEquals($cookie->getFieldName() . ': ' . $expected, $cookie->toString());
+ }
+
+ /**
+ * @dataProvider validCookieWithInfoProvider
+ */
+ public function testAddingAsRawHeaderToResponseObject($cStr, $info, $expected)
+ {
+ $response = new Zend_Controller_Response_HttpTestCase();
+ $cookie = Zend_Http_Header_SetCookie::fromString($cStr);
+ $response->setRawHeader($cookie);
+ $this->assertContains((string)$cookie, $response->sendHeaders());
+ }
+
+ /**
+ * Provide valid cookie strings with information about them
+ *
+ * @return array
+ */
+ public static function validCookieWithInfoProvider()
+ {
+ $now = time();
+ $yesterday = $now - (3600 * 24);
+
+ return array(
+ array(
+ 'Set-Cookie: justacookie=foo; domain=example.com',
+ array(
+ 'name' => 'justacookie',
+ 'value' => 'foo',
+ 'domain' => 'example.com',
+ 'path' => '/',
+ 'expires' => null,
+ 'secure' => false,
+ 'httponly'=> false
+ ),
+ 'justacookie=foo; Domain=example.com'
+ ),
+ array(
+ 'Set-Cookie: expires=tomorrow; secure; path=/Space Out/; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=.example.com',
+ array(
+ 'name' => 'expires',
+ 'value' => 'tomorrow',
+ 'domain' => '.example.com',
+ 'path' => '/Space Out/',
+ 'expires' => strtotime('Tue, 21-Nov-2006 08:33:44 GMT'),
+ 'secure' => true,
+ 'httponly'=> false
+ ),
+ 'expires=tomorrow; Expires=Tue, 21-Nov-2006 08:33:44 GMT; Domain=.example.com; Path=/Space Out/; Secure'
+ ),
+ array(
+ 'Set-Cookie: domain=unittests; expires=' . gmdate('D, d-M-Y H:i:s', $now) . ' GMT; domain=example.com; path=/some%20value/',
+ array(
+ 'name' => 'domain',
+ 'value' => 'unittests',
+ 'domain' => 'example.com',
+ 'path' => '/some%20value/',
+ 'expires' => $now,
+ 'secure' => false,
+ 'httponly'=> false
+ ),
+ 'domain=unittests; Expires=' . gmdate('D, d-M-Y H:i:s', $now) . ' GMT; Domain=example.com; Path=/some%20value/'
+ ),
+ array(
+ 'Set-Cookie: path=indexAction; path=/; domain=.foo.com; expires=' . gmdate('D, d-M-Y H:i:s', $yesterday) . ' GMT',
+ array(
+ 'name' => 'path',
+ 'value' => 'indexAction',
+ 'domain' => '.foo.com',
+ 'path' => '/',
+ 'expires' => $yesterday,
+ 'secure' => false,
+ 'httponly'=> false
+ ),
+ 'path=indexAction; Expires=' . gmdate('D, d-M-Y H:i:s', $yesterday) . ' GMT; Domain=.foo.com; Path=/'
+ ),
+
+ array(
+ 'Set-Cookie: secure=sha1; secure; SECURE; domain=some.really.deep.domain.com',
+ array(
+ 'name' => 'secure',
+ 'value' => 'sha1',
+ 'domain' => 'some.really.deep.domain.com',
+ 'path' => '/',
+ 'expires' => null,
+ 'secure' => true,
+ 'httponly'=> false
+ ),
+ 'secure=sha1; Domain=some.really.deep.domain.com; Secure'
+ ),
+ array(
+ 'Set-Cookie: justacookie=foo; domain=example.com; httpOnly',
+ array(
+ 'name' => 'justacookie',
+ 'value' => 'foo',
+ 'domain' => 'example.com',
+ 'path' => '/',
+ 'expires' => null,
+ 'secure' => false,
+ 'httponly'=> true
+ ),
+ 'justacookie=foo; Domain=example.com; HttpOnly'
+ ),
+ array(
+ 'Set-Cookie: PHPSESSID=123456789+abcd%2Cef; secure; domain=.localdomain; path=/foo/baz; expires=Tue, 21-Nov-2006 08:33:44 GMT;',
+ array(
+ 'name' => 'PHPSESSID',
+ 'value' => '123456789+abcd%2Cef',
+ 'domain' => '.localdomain',
+ 'path' => '/foo/baz',
+ 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT',
+ 'secure' => true,
+ 'httponly'=> false
+ ),
+ 'PHPSESSID=123456789%2Babcd%252Cef; Expires=Tue, 21-Nov-2006 08:33:44 GMT; Domain=.localdomain; Path=/foo/baz; Secure'
+ ),
+ array(
+ 'Set-Cookie: myname=myvalue; Domain=docs.foo.com; Path=/accounts; Expires=Wed, 13-Jan-2021 22:23:01 GMT; Secure; HttpOnly',
+ array(
+ 'name' => 'myname',
+ 'value' => 'myvalue',
+ 'domain' => 'docs.foo.com',
+ 'path' => '/accounts',
+ 'expires' => 'Wed, 13-Jan-2021 22:23:01 GMT',
+ 'secure' => true,
+ 'httponly'=> true
+ ),
+ 'myname=myvalue; Expires=Wed, 13-Jan-2021 22:23:01 GMT; Domain=docs.foo.com; Path=/accounts; Secure; HttpOnly'
+ ),
+ );
+ }
+}
+
Index: tests/Zend/Http/Header/AllTests.php
===================================================================
--- tests/Zend/Http/Header/AllTests.php (revision 0)
+++ tests/Zend/Http/Header/AllTests.php (revision 0)
@@ -0,0 +1,60 @@
+addTestSuite('Zend_Http_Header_SetCookieTest');
+
+ return $suite;
+ }
+}
+
+if (PHPUnit_MAIN_METHOD == 'Zend_Http_Header_AllTests::main') {
+ Zend_Http_Header_AllTests::main();
+}
Index: documentation/manual/en/module_specs/Zend_Controller-Response.xml
===================================================================
--- documentation/manual/en/module_specs/Zend_Controller-Response.xml (revision 24774)
+++ documentation/manual/en/module_specs/Zend_Controller-Response.xml (working copy)
@@ -236,6 +236,105 @@
setHttpResponseCode() and
getHttpResponseCode().
+
+
+