-
Notifications
You must be signed in to change notification settings - Fork 502
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #64 from rmccue/cookies
Add cookie support
- Loading branch information
Showing
8 changed files
with
535 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
<?php | ||
/** | ||
* Cookie storage object | ||
* | ||
* @package Requests | ||
* @subpackage Cookies | ||
*/ | ||
|
||
/** | ||
* Cookie storage object | ||
* | ||
* @package Requests | ||
* @subpackage Cookies | ||
*/ | ||
class Requests_Cookie { | ||
/** | ||
* | ||
* @var string | ||
*/ | ||
public $name; | ||
|
||
/** | ||
* @var string | ||
*/ | ||
public $value; | ||
|
||
/** | ||
* Cookie attributes | ||
* | ||
* Valid keys are (currently) path, domain, expires, max-age, secure and | ||
* httponly. | ||
* | ||
* @var array | ||
*/ | ||
public $attributes = array(); | ||
|
||
/** | ||
* Create a new cookie object | ||
* | ||
* @param string $name | ||
* @param string $value | ||
* @param array $attributes Associative array of attribute data | ||
*/ | ||
public function __construct($name, $value, $attributes = array()) { | ||
$this->name = $name; | ||
$this->value = $value; | ||
$this->attributes = $attributes; | ||
} | ||
|
||
/** | ||
* Format a cookie for a Cookie header | ||
* | ||
* This is used when sending cookies to a server. | ||
* | ||
* @return string Cookie formatted for Cookie header | ||
*/ | ||
public function formatForHeader() { | ||
return sprintf('%s=%s', $this->name, $this->value); | ||
} | ||
|
||
/** | ||
* Format a cookie for a Set-Cookie header | ||
* | ||
* This is used when sending cookies to clients. This isn't really | ||
* applicable to client-side usage, but might be handy for debugging. | ||
* | ||
* @return string Cookie formatted for Set-Cookie header | ||
*/ | ||
public function formatForSetCookie() { | ||
$header_value = $this->formatForHeader(); | ||
if (!empty($this->attributes)) { | ||
$parts = array(); | ||
foreach ($this->attributes as $key => $value) { | ||
// Ignore non-associative attributes | ||
if (is_numeric($key)) { | ||
$parts[] = $value; | ||
} | ||
else { | ||
$parts[] = sprintf('%s=%s', $key, $value); | ||
} | ||
} | ||
|
||
$header_value .= '; ' . implode('; ', $parts); | ||
} | ||
return $header_value; | ||
} | ||
|
||
/** | ||
* Get the cookie value | ||
* | ||
* Attributes and other data can be accessed via methods. | ||
*/ | ||
public function __toString() { | ||
return $this->value; | ||
} | ||
|
||
/** | ||
* Parse a cookie string into a cookie object | ||
* | ||
* Based on Mozilla's parsing code in Firefox and related projects, which | ||
* is an intentional deviation from RFC 2109 and RFC 2616. RFC 6265 | ||
* specifies some of this handling, but not in a thorough manner. | ||
* | ||
* @param string Cookie header value (from a Set-Cookie header) | ||
* @return Requests_Cookie Parsed cookie object | ||
*/ | ||
public static function parse($string, $name = '') { | ||
$parts = explode(';', $string); | ||
$kvparts = array_shift($parts); | ||
|
||
if (!empty($name)) { | ||
$value = $string; | ||
} | ||
elseif (strpos($kvparts, '=') === false) { | ||
// Some sites might only have a value without the equals separator. | ||
// Deviate from RFC 6265 and pretend it was actually a blank name | ||
// (`=foo`) | ||
// | ||
// https://bugzilla.mozilla.org/show_bug.cgi?id=169091 | ||
$name = ''; | ||
$value = $kvparts; | ||
} | ||
else { | ||
list($name, $value) = explode('=', $kvparts, 2); | ||
} | ||
$name = trim($name); | ||
$value = trim($value); | ||
|
||
// Attribute key are handled case-insensitively | ||
$attributes = new Requests_Utility_CaseInsensitiveDictionary(); | ||
|
||
if (!empty($parts)) { | ||
foreach ($parts as $part) { | ||
if (strpos($part, '=') === false) { | ||
$part_key = $part; | ||
$part_value = true; | ||
} | ||
else { | ||
list($part_key, $part_value) = explode('=', $part, 2); | ||
$part_value = trim($part_value); | ||
} | ||
|
||
$part_key = trim($part_key); | ||
$attributes[$part_key] = $part_value; | ||
} | ||
} | ||
|
||
return new Requests_Cookie($name, $value, $attributes); | ||
} | ||
|
||
/** | ||
* Parse all Set-Cookie headers from request headers | ||
* | ||
* @param Requests_Response_Headers $headers | ||
* @return array | ||
*/ | ||
public static function parseFromHeaders(Requests_Response_Headers $headers) { | ||
$cookie_headers = $headers->getValues('Set-Cookie'); | ||
if (empty($cookie_headers)) { | ||
return array(); | ||
} | ||
|
||
$cookies = array(); | ||
foreach ($cookie_headers as $header) { | ||
$parsed = self::parse($header); | ||
$cookies[$parsed->name] = $parsed; | ||
} | ||
|
||
return $cookies; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
<?php | ||
/** | ||
* Cookie holder object | ||
* | ||
* @package Requests | ||
* @subpackage Cookies | ||
*/ | ||
|
||
/** | ||
* Cookie holder object | ||
* | ||
* @package Requests | ||
* @subpackage Cookies | ||
*/ | ||
class Requests_Cookie_Jar implements ArrayAccess, IteratorAggregate { | ||
/** | ||
* Actual item data | ||
* | ||
* @var array | ||
*/ | ||
protected $cookies = array(); | ||
|
||
/** | ||
* Create a new jar | ||
* | ||
* @param array $cookies Existing cookie values | ||
*/ | ||
public function __construct($cookies = array()) { | ||
$this->cookies = $cookies; | ||
} | ||
|
||
/** | ||
* Normalise cookie data into a Requests_Cookie | ||
* | ||
* @param string|Requests_Cookie $cookie | ||
* @return Requests_Cookie | ||
*/ | ||
public function normalizeCookie($cookie, $key = null) { | ||
if ($cookie instanceof Requests_Cookie) { | ||
return $cookie; | ||
} | ||
|
||
return Requests_Cookie::parse($cookie, $key); | ||
} | ||
|
||
/** | ||
* Check if the given item exists | ||
* | ||
* @param string $key Item key | ||
* @return boolean Does the item exist? | ||
*/ | ||
public function offsetExists($key) { | ||
return isset($this->cookies[$key]); | ||
} | ||
|
||
/** | ||
* Get the value for the item | ||
* | ||
* @param string $key Item key | ||
* @return string Item value | ||
*/ | ||
public function offsetGet($key) { | ||
if (!isset($this->cookies[$key])) | ||
return null; | ||
|
||
return $this->cookies[$key]; | ||
} | ||
|
||
/** | ||
* Set the given item | ||
* | ||
* @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) | ||
* | ||
* @param string $key Item name | ||
* @param string $value Item value | ||
*/ | ||
public function offsetSet($key, $value) { | ||
if ($key === null) { | ||
throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); | ||
} | ||
|
||
$this->cookies[$key] = $value; | ||
} | ||
|
||
/** | ||
* Unset the given header | ||
* | ||
* @param string $key | ||
*/ | ||
public function offsetUnset($key) { | ||
unset($this->cookies[$key]); | ||
} | ||
|
||
/** | ||
* Get an iterator for the data | ||
* | ||
* @return ArrayIterator | ||
*/ | ||
public function getIterator() { | ||
return new ArrayIterator($this->cookies); | ||
} | ||
|
||
/** | ||
* Register the cookie handler with the request's hooking system | ||
* | ||
* @param Requests_Hooker $hooks Hooking system | ||
*/ | ||
public function register(Requests_Hooker $hooks) { | ||
$hooks->register('requests.before_request', array($this, 'before_request')); | ||
$hooks->register('requests.before_redirect_check', array($this, 'before_redirect_check')); | ||
} | ||
|
||
/** | ||
* Add Cookie header to a request if we have any | ||
* | ||
* As per RFC 6265, cookies are separated by '; ' | ||
* | ||
* @param string $url | ||
* @param array $headers | ||
* @param array $data | ||
* @param string $type | ||
* @param array $options | ||
*/ | ||
public function before_request(&$url, &$headers, &$data, &$type, &$options) { | ||
if (!empty($this->cookies)) { | ||
$cookies = array(); | ||
foreach ($this->cookies as $key => $cookie) { | ||
$cookie = $this->normalizeCookie($cookie, $key); | ||
$cookies[] = $cookie->formatForHeader(); | ||
} | ||
|
||
$headers['Cookie'] = implode('; ', $cookies); | ||
} | ||
} | ||
|
||
/** | ||
* Parse all cookies from a response and attach them to the response | ||
* | ||
* @var Requests_Response $response | ||
*/ | ||
public function before_redirect_check(Requests_Response &$return) { | ||
$cookies = Requests_Cookie::parseFromHeaders($return->headers); | ||
$this->cookies = array_merge($this->cookies, $cookies); | ||
$return->cookies = $this; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.