Skip to content

Commit

Permalink
Store headers internally as arrays and refactor
Browse files Browse the repository at this point in the history
This lets us do more magical stuff in other places, since we might need
case-insensitive dictionaries elsewhere. The internal use of arrays
breaks one test at the moment, this will need to be fixed.
  • Loading branch information
rmccue committed Aug 18, 2013
1 parent ae0a043 commit dec780a
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 52 deletions.
83 changes: 31 additions & 52 deletions library/Requests/Response/Headers.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,85 +10,64 @@
*
* @package Requests
*/
class Requests_Response_Headers implements ArrayAccess, IteratorAggregate {
class Requests_Response_Headers extends Requests_Utility_CaseInsensitiveDictionary {
/**
* Actual header data
* Get the given header
*
* @var array
*/
protected $data = array();

/**
* Check if the given header exists
* Unlike {@see self::getValues()}, this returns a string. If there are
* multiple values, it concatenates them with a comma as per RFC2616.
*
* @param string $key
* @return boolean
*/
public function offsetExists($key) {
$key = strtolower($key);
return isset($this->data[$key]);
}

/**
* Get the given header
* Avoid using this where commas may be used unquoted in values, such as
* Set-Cookie headers.
*
* @param string $key
* @return string Header value
*/
public function offsetGet($key) {
$key = strtolower($key);
return isset($this->data[$key]) ? $this->data[$key] : null;
if (!isset($this->data[$key]))
return null;

return $this->flatten($this->data[$key]);
}

/**
* Set the given header
* Get all values for a given header
*
* @throws Requests_Exception On attempting to use headers dictionary as list (`invalidset`)
*
* @param string $key Header name
* @param string $value Header value
* @param string $key
* @return array Header values
*/
public function offsetSet($key, $value) {
if ($key === null) {
throw new Requests_Exception('Headers is a dictionary, not a list', 'invalidset');
}

public function getValues($key) {
$key = strtolower($key);
if (!isset($this->data[$key]))
return null;

if (isset($this->data[$key])) {
// RFC2616 notes that multiple headers must be able to
// be combined like this. We should use a smarter way though (arrays
// internally, e.g.)
$value = $this->data[$key] . ',' . $value;
}

$this->data[$key] = $value;
return $this->data[$key];
}

/**
* Unset the given header
* Flattens a value into a string
*
* @param string $key
* Converts an array into a string by imploding values with a comma, as per
* RFC2616's rules for folding headers.
*
* @param string|array $value Value to flatten
* @return string Flattened value
*/
public function offsetUnset($key) {
unset($this->data[strtolower($key)]);
public function flatten($value) {
if (is_array($value))
$value = implode(',', $value);

return $value;
}

/**
* Get an interator for the data
* Get an iterator for the data
*
* Converts the internal
* @return ArrayIterator
*/
public function getIterator() {
return new ArrayIterator($this->data);
}

/**
* Get the headers as an array
*
* @return array Header data
*/
public function getAll() {
return $this->data;
return new Requests_Utility_FilteredIterator($this->data, array($this, 'flatten'));
}
}
96 changes: 96 additions & 0 deletions library/Requests/Utility/CaseInsensitiveDictionary.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php
/**
* Case-insensitive dictionary, suitable for HTTP headers
*
* @package Requests
* @subpackage Utilities
*/

/**
* Case-insensitive dictionary, suitable for HTTP headers
*
* @package Requests
* @subpackage Utilities
*/
class Requests_Utility_CaseInsensitiveDictionary implements ArrayAccess, IteratorAggregate {
/**
* Actual item data
*
* @var array
*/
protected $data = array();

/**
* Check if the given item exists
*
* @param string $key Item key
* @return boolean Does the item exist?
*/
public function offsetExists($key) {
$key = strtolower($key);
return isset($this->data[$key]);
}

/**
* Get the value for the item
*
* @param string $key Item key
* @return string Item value
*/
public function offsetGet($key) {
$key = strtolower($key);
if (!isset($this->data[$key]))
return null;

return $this->data[$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');
}

$key = strtolower($key);

if (!isset($this->data[$key])) {
$this->data[$key] = array();
}

$this->data[$key][] = $value;
}

/**
* Unset the given header
*
* @param string $key
*/
public function offsetUnset($key) {
unset($this->data[strtolower($key)]);
}

/**
* Get an iterator for the data
*
* @return ArrayIterator
*/
public function getIterator() {
return new ArrayIterator($this->data);
}

/**
* Get the headers as an array
*
* @return array Header data
*/
public function getAll() {
return $this->data;
}
}
38 changes: 38 additions & 0 deletions library/Requests/Utility/FilteredIterator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
/**
* Iterator for arrays requiring filtered values
*
* @package Requests
* @subpackage Utilities
*/

/**
* Iterator for arrays requiring filtered values
*
* @package Requests
* @subpackage Utilities
*/
class Requests_Utility_FilteredIterator extends ArrayIterator {
/**
* Create a new iterator
*
* @param array $data
* @param callable $callback Callback to be called on each value
*/
public function __construct($data, $callback) {
parent::__construct($data);

$this->callback = $callback;
}

/**
* Get the current item's value after filtering
*
* @return string
*/
public function current() {
$value = parent::current();
$value = call_user_func($this->callback, $value);
return $value;
}
}

0 comments on commit dec780a

Please sign in to comment.