Skip to content

Commit

Permalink
refactored cookie management
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Jun 23, 2010
1 parent a05a82a commit 97162cf
Show file tree
Hide file tree
Showing 13 changed files with 370 additions and 196 deletions.
2 changes: 1 addition & 1 deletion src/Symfony/Components/BrowserKit/Client.php
Expand Up @@ -210,7 +210,7 @@ public function request($method, $uri, array $parameters = array(), array $heade

$response = $this->filterResponse($this->response);

$this->cookieJar->updateFromResponse($response);
$this->cookieJar->updateFromResponse($response, $uri);

$this->redirect = $response->getHeader('Location');

Expand Down
161 changes: 142 additions & 19 deletions src/Symfony/Components/BrowserKit/Cookie.php
Expand Up @@ -20,31 +20,144 @@
*/
class Cookie
{
const DATE_FORMAT = 'D, d-M-Y H:i:s T';

protected $name;
protected $value;
protected $expire;
protected $expires;
protected $path;
protected $domain;
protected $secure;
protected $httponly;

/**
* Sets a cookie.
*
* @param string $name The cookie name
* @param string $value The value of the cookie
* @param string $expire The time the cookie expires
* @param string $path The path on the server in which the cookie will be available on
* @param string $domain The domain that the cookie is available
* @param bool $secure Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client
* @param string $name The cookie name
* @param string $value The value of the cookie
* @param string $expires The time the cookie expires
* @param string $path The path on the server in which the cookie will be available on
* @param string $domain The domain that the cookie is available
* @param bool $secure Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client
* @param bool $httponly The cookie httponly flag
*/
public function __construct($name, $value, $expires = null, $path = '/', $domain = '', $secure = false, $httponly = false)
{
$this->name = $name;
$this->value = $value;
$this->expires = null === $expires ? null : (integer) $expires;
$this->path = empty($path) ? '/' : $path;
$this->domain = $domain;
$this->secure = (Boolean) $secure;
$this->httponly = (Boolean) $httponly;
}

/**
* Returns the HTTP representation of the Cookie.
*
* @return string The HTTP representation of the Cookie
*/
public function __toString()
{
$cookie = sprintf('%s=%s', $this->name, urlencode($this->value));

if (null !== $this->expires) {
$cookie .= '; expires='.substr(\DateTime::createFromFormat('U', $this->expires, new \DateTimeZone('UTC'))->format(static::DATE_FORMAT), 0, -5);
}

if ('/' !== $this->path) {
$cookie .= '; path='.$this->path;
}

if ('' !== $this->domain) {
$cookie .= '; domain='.$this->domain;
}

if ($this->secure) {
$cookie .= '; secure';
}

if ($this->httponly) {
$cookie .= '; httponly';
}

return $cookie;
}

/**
* Creates a Cookie instance from a Set-Cookie header value.
*
* @param string $cookie A Set-Cookie header value
* @param string $url The base URL
*
* @param Symfony\Components\BrowserKit\Cookie A Cookie instance
*/
public function __construct($name, $value, $expire = 0, $path = '/', $domain = '', $secure = false)
static public function fromString($cookie, $url = null)
{
$this->name = $name;
$this->value = $value;
$this->expire = (integer) $expire;
$this->path = empty($path) ? '/' : $path;
$this->domain = $domain;
$this->secure = (Boolean) $secure;
$parts = explode(';', $cookie);

if (false === strpos($parts[0], '=')) {
throw new \InvalidArgumentException('The cookie string "%s" is not valid.');
}

list($name, $value) = explode('=', array_shift($parts), 2);

$values = array(
'name' => trim($name),
'value' => urldecode(trim($value)),
'expires' => null,
'path' => '/',
'domain' => '',
'secure' => false,
'httponly' => false,
);

if (null !== $url) {
if ((false === $parts = parse_url($url)) || !isset($parts['host']) || !isset($parts['path'])) {
throw new \InvalidArgumentException(sprintf('The URL "%s" is not valid.', $url));
}

$values['domain'] = $parts['host'];
$values['path'] = substr($parts['path'], 0, strrpos($parts['path'], '/'));
}

foreach ($parts as $part) {
$part = trim($part);

if ('secure' === strtolower($part)) {
$values['secure'] = true;

continue;
}

if ('httponly' === strtolower($part)) {
$values['httponly'] = true;

continue;
}

if (2 === count($elements = explode('=', $part, 2))) {
if ('expires' === $elements[0]) {
if (false === $date = \DateTime::createFromFormat(static::DATE_FORMAT, $elements[1], new \DateTimeZone('UTC'))) {
throw new \InvalidArgumentException(sprintf('The expires part of cookie is not valid (%s).', $elements[1]));
}

$elements[1] = $date->getTimestamp();
}

$values[strtolower($elements[0])] = $elements[1];
}
}

return new static(
$values['name'],
$values['value'],
$values['expires'],
$values['path'],
$values['domain'],
$values['secure'],
$values['httponly']
);
}

/**
Expand All @@ -68,13 +181,13 @@ public function getValue()
}

/**
* Gets the expire time of the cookie.
* Gets the expires time of the cookie.
*
* @return string The cookie expire time
* @return string The cookie expires time
*/
public function getExpireTime()
public function getExpiresTime()
{
return $this->expire;
return $this->expires;
}

/**
Expand Down Expand Up @@ -107,13 +220,23 @@ public function isSecure()
return $this->secure;
}

/**
* Returns the httponly flag of the cookie.
*
* @return Boolean The cookie httponly flag
*/
public function isHttponly()
{
return $this->httponly;
}

/**
* Returns true if the cookie has expired.
*
* @return Boolean true if the cookie has expired, false otherwise
*/
public function isExpired()
{
return $this->expire && $this->expire < time();
return (null !== $this->expires) && $this->expires < time();
}
}
14 changes: 4 additions & 10 deletions src/Symfony/Components/BrowserKit/CookieJar.php
Expand Up @@ -68,18 +68,12 @@ public function clear()
* Updates the cookie jar from a Response object.
*
* @param Symfony\Components\BrowserKit\Response $response A Response object
* @param string $url The base URL
*/
public function updateFromResponse(Response $response)
public function updateFromResponse(Response $response, $uri = null)
{
foreach ($response->getCookies() as $name => $cookie) {
$this->set(new Cookie(
$name,
isset($cookie['value']) ? $cookie['value'] : '',
isset($cookie['expire']) ? $cookie['expire'] : 0,
isset($cookie['path']) ? $cookie['path'] : '/',
isset($cookie['domain']) ? $cookie['domain'] : '',
isset($cookie['secure']) ? $cookie['secure'] : false
));
foreach ($response->getHeader('Set-Cookie', false) as $cookie) {
$this->set(Cookie::fromString($cookie), $uri);
}
}

Expand Down
34 changes: 14 additions & 20 deletions src/Symfony/Components/BrowserKit/Response.php
Expand Up @@ -23,22 +23,22 @@ class Response
protected $content;
protected $status;
protected $headers;
protected $cookies;

/**
* Constructor.
*
* The headers array is a set of key/value pairs. If a header is present multiple times
* then the value is an array of all the values.
*
* @param string $content The content of the response
* @param integer $status The response status code
* @param array $headers An array of headers
* @param array $cookies An array of cookies
*/
public function __construct($content = '', $status = 200, array $headers = array(), array $cookies = array())
public function __construct($content = '', $status = 200, array $headers = array())
{
$this->content = $content;
$this->status = $status;
$this->headers = $headers;
$this->cookies = $cookies;
}

public function __toString()
Expand All @@ -47,9 +47,6 @@ public function __toString()
foreach ($this->headers as $name => $value) {
$headers .= sprintf("%s: %s\n", $name, $value);
}
foreach ($this->cookies as $name => $cookie) {
$headers .= sprintf("Set-Cookie: %s=%s\n", $name, $cookie['value']);
}

return $headers."\n".$this->content;
}
Expand Down Expand Up @@ -87,26 +84,23 @@ public function getHeaders()
/**
* Gets a response header.
*
* @param string $header The header name
* @param string $header The header name
* @param Boolean $first Whether to return the first value or all header values
*
* @return string The header value
* @return string|array The first header value if $first is true, an array of values otherwise
*/
public function getHeader($header)
public function getHeader($header, $first = true)
{
foreach ($this->headers as $key => $value) {
if (str_replace('-', '_', strtolower($key)) == str_replace('-', '_', strtolower($header))) {
return $value;
if ($first) {
return is_array($value) ? (count($value) ? $value[0] : '') : $value;
} else {
return is_array($value) ? $value : array($value);
}
}
}
}

/**
* Gets the response cookies.
*
* @return array The response cookies
*/
public function getCookies()
{
return $this->cookies;
return $first ? null : array();
}
}
2 changes: 1 addition & 1 deletion src/Symfony/Components/HttpKernel/Client.php
Expand Up @@ -114,6 +114,6 @@ protected function filterRequest(DomRequest $request)
*/
protected function filterResponse($response)
{
return new DomResponse($response->getContent(), $response->getStatusCode(), $response->headers->all(), $response->getCookies());
return new DomResponse($response->getContent(), $response->getStatusCode(), $response->headers->all());
}
}

0 comments on commit 97162cf

Please sign in to comment.