Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
33.33% |
7 / 21 |
CRAP | |
32.91% |
52 / 158 |
Headers | |
0.00% |
0 / 1 |
|
33.33% |
7 / 21 |
1381.33 | |
32.91% |
52 / 158 |
fromString($string) | |
0.00% |
0 / 1 |
56 | |
0.00% |
0 / 24 |
|||
setPluginClassLoader(PluginClassLocator $pluginClassLoader) | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
getPluginClassLoader() | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
addHeaders($headers) | |
0.00% |
0 / 1 |
53.09 | |
38.10% |
8 / 21 |
|||
addHeaderLine($headerFieldNameOrLine, $fieldValue = null) | |
0.00% |
0 / 1 |
6.10 | |
64.71% |
11 / 17 |
|||
addHeader(Header\HeaderInterface $header) | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
removeHeader(Header\HeaderInterface $header) | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 6 |
|||
clearHeaders() | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
get($name) | |
0.00% |
0 / 1 |
20.64 | |
47.62% |
10 / 21 |
|||
has($name) | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
next() | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
key() | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
valid() | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
rewind() | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
current() | |
100.00% |
1 / 1 |
2 | |
100.00% |
5 / 5 |
|||
count() | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
toString() | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 10 |
|||
toArray() | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 18 |
|||
forceLoading() | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 3 |
|||
lazyLoadHeader($index) | |
0.00% |
0 / 1 |
5.57 | |
53.85% |
7 / 13 |
|||
createKey($name) | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
<?php | |
/** | |
* Zend Framework (http://framework.zend.com/) | |
* | |
* @link http://github.com/zendframework/zf2 for the canonical source repository | |
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) | |
* @license http://framework.zend.com/license/new-bsd New BSD License | |
*/ | |
namespace Zend\Http; | |
use ArrayIterator; | |
use Countable; | |
use Iterator; | |
use Traversable; | |
use Zend\Loader\PluginClassLocator; | |
/** | |
* Basic HTTP headers collection functionality | |
* Handles aggregation of headers | |
* | |
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 | |
*/ | |
class Headers implements Countable, Iterator | |
{ | |
/** | |
* @var PluginClassLoader | |
*/ | |
protected $pluginClassLoader = null; | |
/** | |
* @var array key names for $headers array | |
*/ | |
protected $headersKeys = array(); | |
/** | |
* @var array Array of header array information or Header instances | |
*/ | |
protected $headers = array(); | |
/** | |
* Populates headers from string representation | |
* | |
* Parses a string for headers, and aggregates them, in order, in the | |
* current instance, primarily as strings until they are needed (they | |
* will be lazy loaded) | |
* | |
* @param string $string | |
* @return Headers | |
* @throws Exception\RuntimeException | |
*/ | |
public static function fromString($string) | |
{ | |
$headers = new static(); | |
$current = array(); | |
// iterate the header lines, some might be continuations | |
foreach (explode("\r\n", $string) as $line) { | |
// check if a header name is present | |
if (preg_match('/^(?P<name>[^()><@,;:\"\\/\[\]?=}{ \t]+):.*$/', $line, $matches)) { | |
if ($current) { | |
// a header name was present, then store the current complete line | |
$headers->headersKeys[] = static::createKey($current['name']); | |
$headers->headers[] = $current; | |
} | |
$current = array( | |
'name' => $matches['name'], | |
'line' => trim($line) | |
); | |
} elseif (preg_match('/^(?P<ws>\s+).*$/', $line, $matches)) { | |
// continuation: append to current line | |
$current['line'] .= "\r\n" . $matches['ws'] . trim($line); | |
} elseif (preg_match('/^\s*$/', $line)) { | |
// empty line indicates end of headers | |
break; | |
} else { | |
// Line does not match header format! | |
throw new Exception\RuntimeException(sprintf( | |
'Line "%s" does not match header format!', | |
$line | |
)); | |
} | |
} | |
if ($current) { | |
$headers->headersKeys[] = static::createKey($current['name']); | |
$headers->headers[] = $current; | |
} | |
return $headers; | |
} | |
/** | |
* Set an alternate implementation for the PluginClassLoader | |
* | |
* @param \Zend\Loader\PluginClassLocator $pluginClassLoader | |
* @return Headers | |
*/ | |
public function setPluginClassLoader(PluginClassLocator $pluginClassLoader) | |
{ | |
$this->pluginClassLoader = $pluginClassLoader; | |
return $this; | |
} | |
/** | |
* Return an instance of a PluginClassLocator, lazyload and inject map if necessary | |
* | |
* @return PluginClassLocator | |
*/ | |
public function getPluginClassLoader() | |
{ | |
if ($this->pluginClassLoader === null) { | |
$this->pluginClassLoader = new HeaderLoader(); | |
} | |
return $this->pluginClassLoader; | |
} | |
/** | |
* Add many headers at once | |
* | |
* Expects an array (or Traversable object) of type/value pairs. | |
* | |
* @param array|Traversable $headers | |
* @return Headers | |
* @throws Exception\InvalidArgumentException | |
*/ | |
public function addHeaders($headers) | |
{ | |
if (!is_array($headers) && !$headers instanceof Traversable) { | |
throw new Exception\InvalidArgumentException(sprintf( | |
'Expected array or Traversable; received "%s"', | |
(is_object($headers) ? get_class($headers) : gettype($headers)) | |
)); | |
} | |
foreach ($headers as $name => $value) { | |
if (is_int($name)) { | |
if (is_string($value)) { | |
$this->addHeaderLine($value); | |
} elseif (is_array($value) && count($value) == 1) { | |
$this->addHeaderLine(key($value), current($value)); | |
} elseif (is_array($value) && count($value) == 2) { | |
$this->addHeaderLine($value[0], $value[1]); | |
} elseif ($value instanceof Header\HeaderInterface) { | |
$this->addHeader($value); | |
} | |
} elseif (is_string($name)) { | |
$this->addHeaderLine($name, $value); | |
} | |
} | |
return $this; | |
} | |
/** | |
* Add a raw header line, either in name => value, or as a single string 'name: value' | |
* | |
* This method allows for lazy-loading in that the parsing and instantiation of Header object | |
* will be delayed until they are retrieved by either get() or current() | |
* | |
* @throws Exception\InvalidArgumentException | |
* @param string $headerFieldNameOrLine | |
* @param string $fieldValue optional | |
* @return Headers | |
*/ | |
public function addHeaderLine($headerFieldNameOrLine, $fieldValue = null) | |
{ | |
$matches = null; | |
if (preg_match('/^(?P<name>[^()><@,;:\"\\/\[\]?=}{ \t]+):.*$/', $headerFieldNameOrLine, $matches) | |
&& $fieldValue === null) { | |
// is a header | |
$headerName = $matches['name']; | |
$headerKey = static::createKey($matches['name']); | |
$line = $headerFieldNameOrLine; | |
} elseif ($fieldValue === null) { | |
throw new Exception\InvalidArgumentException('A field name was provided without a field value'); | |
} else { | |
$headerName = $headerFieldNameOrLine; | |
$headerKey = static::createKey($headerFieldNameOrLine); | |
if (is_array($fieldValue)) { | |
$fieldValue = implode(', ', $fieldValue); | |
} | |
$line = $headerFieldNameOrLine . ': ' . $fieldValue; | |
} | |
$this->headersKeys[] = $headerKey; | |
$this->headers[] = array('name' => $headerName, 'line' => $line); | |
return $this; | |
} | |
/** | |
* Add a Header to this container, for raw values @see addHeaderLine() and addHeaders() | |
* | |
* @param Header\HeaderInterface $header | |
* @return Headers | |
*/ | |
public function addHeader(Header\HeaderInterface $header) | |
{ | |
$this->headersKeys[] = static::createKey($header->getFieldName()); | |
$this->headers[] = $header; | |
return $this; | |
} | |
/** | |
* Remove a Header from the container | |
* | |
* @param Header\HeaderInterface $header | |
* @return bool | |
*/ | |
public function removeHeader(Header\HeaderInterface $header) | |
{ | |
$index = array_search($header, $this->headers, true); | |
if ($index !== false) { | |
unset($this->headersKeys[$index]); | |
unset($this->headers[$index]); | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Clear all headers | |
* | |
* Removes all headers from queue | |
* | |
* @return Headers | |
*/ | |
public function clearHeaders() | |
{ | |
$this->headers = $this->headersKeys = array(); | |
return $this; | |
} | |
/** | |
* Get all headers of a certain name/type | |
* | |
* @param string $name | |
* @return bool|Header\HeaderInterface|ArrayIterator | |
*/ | |
public function get($name) | |
{ | |
$key = static::createKey($name); | |
if (!in_array($key, $this->headersKeys)) { | |
return false; | |
} | |
$class = ($this->getPluginClassLoader()->load($key)) ?: 'Zend\Http\Header\GenericHeader'; | |
if (in_array('Zend\Http\Header\MultipleHeaderInterface', class_implements($class, true))) { | |
$headers = array(); | |
foreach (array_keys($this->headersKeys, $key) as $index) { | |
if (is_array($this->headers[$index])) { | |
$this->lazyLoadHeader($index); | |
} | |
} | |
foreach (array_keys($this->headersKeys, $key) as $index) { | |
$headers[] = $this->headers[$index]; | |
} | |
return new ArrayIterator($headers); | |
} | |
$index = array_search($key, $this->headersKeys); | |
if ($index === false) { | |
return false; | |
} | |
if (is_array($this->headers[$index])) { | |
return $this->lazyLoadHeader($index); | |
} | |
return $this->headers[$index]; | |
} | |
/** | |
* Test for existence of a type of header | |
* | |
* @param string $name | |
* @return bool | |
*/ | |
public function has($name) | |
{ | |
return (in_array(static::createKey($name), $this->headersKeys)); | |
} | |
/** | |
* Advance the pointer for this object as an iterator | |
* | |
* @return void | |
*/ | |
public function next() | |
{ | |
next($this->headers); | |
} | |
/** | |
* Return the current key for this object as an iterator | |
* | |
* @return mixed | |
*/ | |
public function key() | |
{ | |
return (key($this->headers)); | |
} | |
/** | |
* Is this iterator still valid? | |
* | |
* @return bool | |
*/ | |
public function valid() | |
{ | |
return (current($this->headers) !== false); | |
} | |
/** | |
* Reset the internal pointer for this object as an iterator | |
* | |
* @return void | |
*/ | |
public function rewind() | |
{ | |
reset($this->headers); | |
} | |
/** | |
* Return the current value for this iterator, lazy loading it if need be | |
* | |
* @return array|Header\HeaderInterface | |
*/ | |
public function current() | |
{ | |
$current = current($this->headers); | |
if (is_array($current)) { | |
$current = $this->lazyLoadHeader(key($this->headers)); | |
} | |
return $current; | |
} | |
/** | |
* Return the number of headers in this contain, if all headers have not been parsed, actual count could | |
* increase if MultipleHeader objects exist in the Request/Response. If you need an exact count, iterate | |
* | |
* @return int count of currently known headers | |
*/ | |
public function count() | |
{ | |
return count($this->headers); | |
} | |
/** | |
* Render all headers at once | |
* | |
* This method handles the normal iteration of headers; it is up to the | |
* concrete classes to prepend with the appropriate status/request line. | |
* | |
* @return string | |
*/ | |
public function toString() | |
{ | |
$headers = ''; | |
foreach ($this->toArray() as $fieldName => $fieldValue) { | |
if (is_array($fieldValue)) { | |
// Handle multi-value headers | |
foreach ($fieldValue as $value) { | |
$headers .= $fieldName . ': ' . $value . "\r\n"; | |
} | |
continue; | |
} | |
// Handle single-value headers | |
$headers .= $fieldName . ': ' . $fieldValue . "\r\n"; | |
} | |
return $headers; | |
} | |
/** | |
* Return the headers container as an array | |
* | |
* @todo determine how to produce single line headers, if they are supported | |
* @return array | |
*/ | |
public function toArray() | |
{ | |
$headers = array(); | |
/* @var $header Header\HeaderInterface */ | |
foreach ($this->headers as $header) { | |
if ($header instanceof Header\MultipleHeaderInterface) { | |
$name = $header->getFieldName(); | |
if (!isset($headers[$name])) { | |
$headers[$name] = array(); | |
} | |
$headers[$name][] = $header->getFieldValue(); | |
} elseif ($header instanceof Header\HeaderInterface) { | |
$headers[$header->getFieldName()] = $header->getFieldValue(); | |
} else { | |
$matches = null; | |
preg_match('/^(?P<name>[^()><@,;:\"\\/\[\]?=}{ \t]+):\s*(?P<value>.*)$/', $header['line'], $matches); | |
if ($matches) { | |
$headers[$matches['name']] = $matches['value']; | |
} | |
} | |
} | |
return $headers; | |
} | |
/** | |
* By calling this, it will force parsing and loading of all headers, after this count() will be accurate | |
* | |
* @return bool | |
*/ | |
public function forceLoading() | |
{ | |
foreach ($this as $item) { | |
// $item should now be loaded | |
} | |
return true; | |
} | |
/** | |
* @param $index | |
* @return mixed|void | |
*/ | |
protected function lazyLoadHeader($index) | |
{ | |
$current = $this->headers[$index]; | |
$key = $this->headersKeys[$index]; | |
/* @var $class Header\HeaderInterface */ | |
$class = ($this->getPluginClassLoader()->load($key)) ?: 'Zend\Http\Header\GenericHeader'; | |
$headers = $class::fromString($current['line']); | |
if (is_array($headers)) { | |
$this->headers[$index] = $current = array_shift($headers); | |
foreach ($headers as $header) { | |
$this->headersKeys[] = $key; | |
$this->headers[] = $header; | |
} | |
return $current; | |
} | |
$this->headers[$index] = $current = $headers; | |
return $current; | |
} | |
/** | |
* Create array key from header name | |
* | |
* @param string $name | |
* @return string | |
*/ | |
protected static function createKey($name) | |
{ | |
return str_replace(array('-', '_', ' ', '.'), '', strtolower($name)); | |
} | |
} |