From 66a18f3239f7c0679c503018efbf98c5848afdcc Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 20 Sep 2011 20:51:19 +0200 Subject: [PATCH] [BrowserKit] first attempt at fixing CookieJar (closes #2209) --- CHANGELOG-2.1.md | 4 + .../Component/BrowserKit/CookieJar.php | 81 +++++++++++++------ .../Component/BrowserKit/CookieJarTest.php | 26 +++++- 3 files changed, 84 insertions(+), 27 deletions(-) diff --git a/CHANGELOG-2.1.md b/CHANGELOG-2.1.md index d7e8f1ca2d..52aa1c7a3b 100644 --- a/CHANGELOG-2.1.md +++ b/CHANGELOG-2.1.md @@ -98,6 +98,10 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * added a timeline panel * The toolbar position can now be configured via the `position` option (can be `top` or `bottom`) +### BrowserKit + + * [BC BREAK] The CookieJar internals have changed to allow cookies with the same name on different sub-domains/sub-paths + ### Config * added a way to add documentation on configuration diff --git a/src/Symfony/Component/BrowserKit/CookieJar.php b/src/Symfony/Component/BrowserKit/CookieJar.php index 8c75f8ae05..a25f761851 100644 --- a/src/Symfony/Component/BrowserKit/CookieJar.php +++ b/src/Symfony/Component/BrowserKit/CookieJar.php @@ -31,35 +31,51 @@ class CookieJar */ public function set(Cookie $cookie) { - $this->cookieJar[$cookie->getName()] = $cookie; + $this->cookieJar[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie; } /** * Gets a cookie by name. * - * @param string $name The cookie name + * @param string $name The cookie name + * @param string $path The cookie path + * @param string $domain The cookie domain * * @return Cookie|null A Cookie instance or null if the cookie does not exist * * @api */ - public function get($name) + public function get($name, $path = '/', $domain = null) { $this->flushExpiredCookies(); - return isset($this->cookieJar[$name]) ? $this->cookieJar[$name] : null; + return isset($this->cookieJar[$domain][$path][$name]) ? $this->cookieJar[$domain][$path][$name] : null; } /** * Removes a cookie by name. * - * @param string $name The cookie name + * @param string $name The cookie name + * @param string $path The cookie path + * @param string $domain The cookie domain * * @api */ - public function expire($name) + public function expire($name, $path = '/', $domain = null) { - unset($this->cookieJar[$name]); + if (null === $path) { + $path = '/'; + } + + unset($this->cookieJar[$domain][$path][$name]); + + if (empty($this->cookieJar[$domain][$path])) { + unset($this->cookieJar[$domain][$path]); + + if (empty($this->cookieJar[$domain])) { + unset($this->cookieJar[$domain]); + } + } } /** @@ -76,7 +92,7 @@ class CookieJar * Updates the cookie jar from a Response object. * * @param Response $response A Response object - * @param string $uri The base URL + * @param string $uri The base URL */ public function updateFromResponse(Response $response, $uri = null) { @@ -94,13 +110,22 @@ class CookieJar { $this->flushExpiredCookies(); - return $this->cookieJar; + $flattenedCookies = array(); + foreach ($this->cookieJar as $path) { + foreach ($path as $cookies) { + foreach ($cookies as $cookie) { + $flattenedCookies[] = $cookie; + } + } + } + + return $flattenedCookies; } /** * Returns not yet expired cookie values for the given URI. * - * @param string $uri A URI + * @param string $uri A URI * @param Boolean $returnsRawValue Returns raw value or urldecoded value * * @return array An array of cookie values @@ -110,25 +135,28 @@ class CookieJar $this->flushExpiredCookies(); $parts = array_replace(array('path' => '/'), parse_url($uri)); - $cookies = array(); - foreach ($this->cookieJar as $cookie) { - if ($cookie->getDomain()) { - $domain = ltrim($cookie->getDomain(), '.'); + foreach ($this->cookieJar as $domain => $pathCookies) { + if ($domain) { + $domain = ltrim($domain, '.'); if ($domain != substr($parts['host'], -strlen($domain))) { continue; } } - if ($cookie->getPath() != substr($parts['path'], 0, strlen($cookie->getPath()))) { - continue; - } + foreach ($pathCookies as $path => $namedCookies) { + if ($path != substr($parts['path'], 0, strlen($path))) { + continue; + } - if ($cookie->isSecure() && 'https' != $parts['scheme']) { - continue; - } + foreach ($namedCookies as $name => $cookie) { + if ($cookie->isSecure() && 'https' != $parts['scheme']) { + continue; + } - $cookies[$cookie->getName()] = $returnsRawValue ? $cookie->getRawValue() : $cookie->getValue(); + $cookies[$cookie->getName()] = $returnsRawValue ? $cookie->getRawValue() : $cookie->getValue(); + } + } } return $cookies; @@ -151,10 +179,13 @@ class CookieJar */ public function flushExpiredCookies() { - $cookies = $this->cookieJar; - foreach ($cookies as $name => $cookie) { - if ($cookie->isExpired()) { - unset($this->cookieJar[$name]); + foreach ($this->cookieJar as $domain => $pathCookies) { + foreach ($pathCookies as $path => $namedCookies) { + foreach ($namedCookies as $name => $cookie) { + if ($cookie->isExpired()) { + unset($this->cookieJar[$domain][$path][$name]); + } + } } } } diff --git a/tests/Symfony/Tests/Component/BrowserKit/CookieJarTest.php b/tests/Symfony/Tests/Component/BrowserKit/CookieJarTest.php index 0d553ef745..47f6e4be46 100644 --- a/tests/Symfony/Tests/Component/BrowserKit/CookieJarTest.php +++ b/tests/Symfony/Tests/Component/BrowserKit/CookieJarTest.php @@ -44,7 +44,7 @@ class CookieJarTest extends \PHPUnit_Framework_TestCase $cookieJar->set($cookie1 = new Cookie('foo', 'bar')); $cookieJar->set($cookie2 = new Cookie('bar', 'foo')); - $this->assertEquals(array('foo' => $cookie1, 'bar' => $cookie2), $cookieJar->all(), '->all() returns all cookies in the jar'); + $this->assertEquals(array($cookie1, $cookie2), $cookieJar->all(), '->all() returns all cookies in the jar'); } public function testClear() @@ -93,7 +93,7 @@ class CookieJarTest extends \PHPUnit_Framework_TestCase array('http://www.example.com/', array('foo_nothing', 'foo_domain')), array('http://foo.example.com/', array('foo_nothing', 'foo_domain')), array('http://foo.example1.com/', array('foo_nothing')), - array('https://foo.example.com/', array('foo_nothing', 'foo_domain', 'foo_secure')), + array('https://foo.example.com/', array('foo_nothing', 'foo_secure', 'foo_domain')), array('http://www.example.com/foo/bar', array('foo_nothing', 'foo_path', 'foo_domain')), array('http://www4.example.com/', array('foo_nothing', 'foo_domain', 'foo_strict_domain')), ); @@ -107,4 +107,26 @@ class CookieJarTest extends \PHPUnit_Framework_TestCase $this->assertEquals(array('foo' => 'bar=baz'), $cookieJar->allValues('/')); $this->assertEquals(array('foo' => 'bar%3Dbaz'), $cookieJar->allRawValues('/')); } + + public function testCookieWithSameNameButDifferentPaths() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/foo')); + $cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/bar')); + + $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/'))); + $this->assertEquals(array('foo' => 'bar1'), $cookieJar->allValues('http://example.com/foo')); + $this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://example.com/bar')); + } + + public function testCookieWithSameNameButDifferentDomains() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/', 'foo.example.com')); + $cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/', 'bar.example.com')); + + $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/'))); + $this->assertEquals(array('foo' => 'bar1'), $cookieJar->allValues('http://foo.example.com/')); + $this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://bar.example.com/')); + } }