update v1.0.7.9 R.C.

This is a Release Candidate. We are still testing.
This commit is contained in:
Sujit Prasad
2016-08-03 20:04:36 +05:30
parent 8b6b924d09
commit ffa56a43cb
3830 changed files with 181529 additions and 495353 deletions

View File

@@ -0,0 +1,105 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http;
use Zend\Stdlib\Message;
/**
* HTTP standard message (Request/Response)
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4
*/
abstract class AbstractMessage extends Message
{
/**#@+
* @const string Version constant numbers
*/
const VERSION_10 = '1.0';
const VERSION_11 = '1.1';
/**#@-*/
/**
* @var string
*/
protected $version = self::VERSION_11;
/**
* @var Headers|null
*/
protected $headers = null;
/**
* Set the HTTP version for this object, one of 1.0 or 1.1
* (AbstractMessage::VERSION_10, AbstractMessage::VERSION_11)
*
* @param string $version (Must be 1.0 or 1.1)
* @return AbstractMessage
* @throws Exception\InvalidArgumentException
*/
public function setVersion($version)
{
if ($version != self::VERSION_10 && $version != self::VERSION_11) {
throw new Exception\InvalidArgumentException(
'Not valid or not supported HTTP version: ' . $version
);
}
$this->version = $version;
return $this;
}
/**
* Return the HTTP version for this request
*
* @return string
*/
public function getVersion()
{
return $this->version;
}
/**
* Provide an alternate Parameter Container implementation for headers in this object,
* (this is NOT the primary API for value setting, for that see getHeaders())
*
* @see getHeaders()
* @param Headers $headers
* @return AbstractMessage
*/
public function setHeaders(Headers $headers)
{
$this->headers = $headers;
return $this;
}
/**
* Return the header container responsible for headers
*
* @return Headers
*/
public function getHeaders()
{
if ($this->headers === null || is_string($this->headers)) {
// this is only here for fromString lazy loading
$this->headers = (is_string($this->headers)) ? Headers::fromString($this->headers) : new Headers();
}
return $this->headers;
}
/**
* Allow PHP casting of this object
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter;
/**
* An interface description for Zend\Http\Client\Adapter classes.
*
* These classes are used as connectors for Zend\Http\Client, performing the
* tasks of connecting, writing, reading and closing connection to the server.
*/
interface AdapterInterface
{
/**
* Set the configuration array for the adapter
*
* @param array $options
*/
public function setOptions($options = []);
/**
* Connect to the remote server
*
* @param string $host
* @param int $port
* @param bool $secure
*/
public function connect($host, $port = 80, $secure = false);
/**
* Send request to the remote server
*
* @param string $method
* @param \Zend\Uri\Uri $url
* @param string $httpVer
* @param array $headers
* @param string $body
* @return string Request as text
*/
public function write($method, $url, $httpVer = '1.1', $headers = [], $body = '');
/**
* Read response from server
*
* @return string
*/
public function read();
/**
* Close the connection to the server
*
*/
public function close();
}

View File

@@ -0,0 +1,527 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter;
use Traversable;
use Zend\Http\Client\Adapter\AdapterInterface as HttpAdapter;
use Zend\Http\Client\Adapter\Exception as AdapterException;
use Zend\Stdlib\ArrayUtils;
/**
* An adapter class for Zend\Http\Client based on the curl extension.
* Curl requires libcurl. See for full requirements the PHP manual: http://php.net/curl
*/
class Curl implements HttpAdapter, StreamInterface
{
/**
* Parameters array
*
* @var array
*/
protected $config = [];
/**
* What host/port are we connected to?
*
* @var array
*/
protected $connectedTo = [null, null];
/**
* The curl session handle
*
* @var resource|null
*/
protected $curl = null;
/**
* List of cURL options that should never be overwritten
*
* @var array
*/
protected $invalidOverwritableCurlOptions;
/**
* Response gotten from server
*
* @var string
*/
protected $response = null;
/**
* Stream for storing output
*
* @var resource
*/
protected $outputStream;
/**
* Adapter constructor
*
* Config is set using setOptions()
*
* @throws AdapterException\InitializationException
*/
public function __construct()
{
if (!extension_loaded('curl')) {
throw new AdapterException\InitializationException(
'cURL extension has to be loaded to use this Zend\Http\Client adapter'
);
}
$this->invalidOverwritableCurlOptions = [
CURLOPT_HTTPGET,
CURLOPT_POST,
CURLOPT_UPLOAD,
CURLOPT_CUSTOMREQUEST,
CURLOPT_HEADER,
CURLOPT_RETURNTRANSFER,
CURLOPT_HTTPHEADER,
CURLOPT_INFILE,
CURLOPT_INFILESIZE,
CURLOPT_PORT,
CURLOPT_MAXREDIRS,
CURLOPT_CONNECTTIMEOUT,
];
}
/**
* Set the configuration array for the adapter
*
* @param array|Traversable $options
* @return Curl
* @throws AdapterException\InvalidArgumentException
*/
public function setOptions($options = [])
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
if (!is_array($options)) {
throw new AdapterException\InvalidArgumentException(
'Array or Traversable object expected, got ' . gettype($options)
);
}
/** Config Key Normalization */
foreach ($options as $k => $v) {
unset($options[$k]); // unset original value
$options[str_replace(['-', '_', ' ', '.'], '', strtolower($k))] = $v; // replace w/ normalized
}
if (isset($options['proxyuser']) && isset($options['proxypass'])) {
$this->setCurlOption(CURLOPT_PROXYUSERPWD, $options['proxyuser'] . ":" . $options['proxypass']);
unset($options['proxyuser'], $options['proxypass']);
}
if (isset($options['sslverifypeer'])) {
$this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $options['sslverifypeer']);
unset($options['sslverifypeer']);
}
foreach ($options as $k => $v) {
$option = strtolower($k);
switch ($option) {
case 'proxyhost':
$this->setCurlOption(CURLOPT_PROXY, $v);
break;
case 'proxyport':
$this->setCurlOption(CURLOPT_PROXYPORT, $v);
break;
default:
if (is_array($v) && isset($this->config[$option]) && is_array($this->config[$option])) {
$v = ArrayUtils::merge($this->config[$option], $v);
}
$this->config[$option] = $v;
break;
}
}
return $this;
}
/**
* Retrieve the array of all configuration options
*
* @return array
*/
public function getConfig()
{
return $this->config;
}
/**
* Direct setter for cURL adapter related options.
*
* @param string|int $option
* @param mixed $value
* @return Curl
*/
public function setCurlOption($option, $value)
{
if (!isset($this->config['curloptions'])) {
$this->config['curloptions'] = [];
}
$this->config['curloptions'][$option] = $value;
return $this;
}
/**
* Initialize curl
*
* @param string $host
* @param int $port
* @param bool $secure
* @return void
* @throws AdapterException\RuntimeException if unable to connect
*/
public function connect($host, $port = 80, $secure = false)
{
// If we're already connected, disconnect first
if ($this->curl) {
$this->close();
}
// Do the actual connection
$this->curl = curl_init();
if ($port != 80) {
curl_setopt($this->curl, CURLOPT_PORT, intval($port));
}
if (isset($this->config['timeout'])) {
if (defined('CURLOPT_CONNECTTIMEOUT_MS')) {
curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT_MS, $this->config['timeout'] * 1000);
} else {
curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, $this->config['timeout']);
}
if (defined('CURLOPT_TIMEOUT_MS')) {
curl_setopt($this->curl, CURLOPT_TIMEOUT_MS, $this->config['timeout'] * 1000);
} else {
curl_setopt($this->curl, CURLOPT_TIMEOUT, $this->config['timeout']);
}
}
if (isset($this->config['maxredirects'])) {
// Set Max redirects
curl_setopt($this->curl, CURLOPT_MAXREDIRS, $this->config['maxredirects']);
}
if (!$this->curl) {
$this->close();
throw new AdapterException\RuntimeException('Unable to Connect to ' . $host . ':' . $port);
}
if ($secure !== false) {
// Behave the same like Zend\Http\Adapter\Socket on SSL options.
if (isset($this->config['sslcert'])) {
curl_setopt($this->curl, CURLOPT_SSLCERT, $this->config['sslcert']);
}
if (isset($this->config['sslpassphrase'])) {
curl_setopt($this->curl, CURLOPT_SSLCERTPASSWD, $this->config['sslpassphrase']);
}
}
// Update connected_to
$this->connectedTo = [$host, $port];
}
/**
* Send request to the remote server
*
* @param string $method
* @param \Zend\Uri\Uri $uri
* @param float $httpVersion
* @param array $headers
* @param string $body
* @return string $request
* @throws AdapterException\RuntimeException If connection fails, connected
* to wrong host, no PUT file defined, unsupported method, or unsupported
* cURL option.
* @throws AdapterException\InvalidArgumentException if $method is currently not supported
*/
public function write($method, $uri, $httpVersion = 1.1, $headers = [], $body = '')
{
// Make sure we're properly connected
if (!$this->curl) {
throw new AdapterException\RuntimeException("Trying to write but we are not connected");
}
if ($this->connectedTo[0] != $uri->getHost() || $this->connectedTo[1] != $uri->getPort()) {
throw new AdapterException\RuntimeException("Trying to write but we are connected to the wrong host");
}
// set URL
curl_setopt($this->curl, CURLOPT_URL, $uri->__toString());
// ensure correct curl call
$curlValue = true;
switch ($method) {
case 'GET':
$curlMethod = CURLOPT_HTTPGET;
break;
case 'POST':
$curlMethod = CURLOPT_POST;
break;
case 'PUT':
// There are two different types of PUT request, either a Raw Data string has been set
// or CURLOPT_INFILE and CURLOPT_INFILESIZE are used.
if (is_resource($body)) {
$this->config['curloptions'][CURLOPT_INFILE] = $body;
}
if (isset($this->config['curloptions'][CURLOPT_INFILE])) {
// Now we will probably already have Content-Length set, so that we have to delete it
// from $headers at this point:
if (!isset($headers['Content-Length'])
&& !isset($this->config['curloptions'][CURLOPT_INFILESIZE])
) {
throw new AdapterException\RuntimeException(
'Cannot set a file-handle for cURL option CURLOPT_INFILE'
. ' without also setting its size in CURLOPT_INFILESIZE.'
);
}
if (isset($headers['Content-Length'])) {
$this->config['curloptions'][CURLOPT_INFILESIZE] = (int) $headers['Content-Length'];
unset($headers['Content-Length']);
}
if (is_resource($body)) {
$body = '';
}
$curlMethod = CURLOPT_UPLOAD;
} else {
$curlMethod = CURLOPT_CUSTOMREQUEST;
$curlValue = "PUT";
}
break;
case 'PATCH':
$curlMethod = CURLOPT_CUSTOMREQUEST;
$curlValue = "PATCH";
break;
case 'DELETE':
$curlMethod = CURLOPT_CUSTOMREQUEST;
$curlValue = "DELETE";
break;
case 'OPTIONS':
$curlMethod = CURLOPT_CUSTOMREQUEST;
$curlValue = "OPTIONS";
break;
case 'TRACE':
$curlMethod = CURLOPT_CUSTOMREQUEST;
$curlValue = "TRACE";
break;
case 'HEAD':
$curlMethod = CURLOPT_CUSTOMREQUEST;
$curlValue = "HEAD";
break;
default:
// For now, through an exception for unsupported request methods
throw new AdapterException\InvalidArgumentException("Method '$method' currently not supported");
}
if (is_resource($body) && $curlMethod != CURLOPT_UPLOAD) {
throw new AdapterException\RuntimeException("Streaming requests are allowed only with PUT");
}
// get http version to use
$curlHttp = ($httpVersion == 1.1) ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0;
// mark as HTTP request and set HTTP method
curl_setopt($this->curl, CURLOPT_HTTP_VERSION, $curlHttp);
curl_setopt($this->curl, $curlMethod, $curlValue);
// Set the CURLINFO_HEADER_OUT flag so that we can retrieve the full request string later
curl_setopt($this->curl, CURLINFO_HEADER_OUT, true);
if ($this->outputStream) {
// headers will be read into the response
curl_setopt($this->curl, CURLOPT_HEADER, false);
curl_setopt($this->curl, CURLOPT_HEADERFUNCTION, [$this, "readHeader"]);
// and data will be written into the file
curl_setopt($this->curl, CURLOPT_FILE, $this->outputStream);
} else {
// ensure headers are also returned
curl_setopt($this->curl, CURLOPT_HEADER, true);
// ensure actual response is returned
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
}
// Treating basic auth headers in a special way
if (array_key_exists('Authorization', $headers) && 'Basic' == substr($headers['Authorization'], 0, 5)) {
curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($this->curl, CURLOPT_USERPWD, base64_decode(substr($headers['Authorization'], 6)));
unset($headers['Authorization']);
}
// set additional headers
if (!isset($headers['Accept'])) {
$headers['Accept'] = '';
}
$curlHeaders = [];
foreach ($headers as $key => $value) {
$curlHeaders[] = $key . ': ' . $value;
}
curl_setopt($this->curl, CURLOPT_HTTPHEADER, $curlHeaders);
/**
* Make sure POSTFIELDS is set after $curlMethod is set:
* @link http://de2.php.net/manual/en/function.curl-setopt.php#81161
*/
if (in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], true)) {
curl_setopt($this->curl, CURLOPT_POSTFIELDS, $body);
} elseif ($curlMethod == CURLOPT_UPLOAD) {
// this covers a PUT by file-handle:
// Make the setting of this options explicit (rather than setting it through the loop following a bit lower)
// to group common functionality together.
curl_setopt($this->curl, CURLOPT_INFILE, $this->config['curloptions'][CURLOPT_INFILE]);
curl_setopt($this->curl, CURLOPT_INFILESIZE, $this->config['curloptions'][CURLOPT_INFILESIZE]);
unset($this->config['curloptions'][CURLOPT_INFILE]);
unset($this->config['curloptions'][CURLOPT_INFILESIZE]);
}
// set additional curl options
if (isset($this->config['curloptions'])) {
foreach ((array) $this->config['curloptions'] as $k => $v) {
if (!in_array($k, $this->invalidOverwritableCurlOptions)) {
if (curl_setopt($this->curl, $k, $v) == false) {
throw new AdapterException\RuntimeException(sprintf(
'Unknown or erroreous cURL option "%s" set',
$k
));
}
}
}
}
// send the request
$response = curl_exec($this->curl);
// if we used streaming, headers are already there
if (!is_resource($this->outputStream)) {
$this->response = $response;
}
$request = curl_getinfo($this->curl, CURLINFO_HEADER_OUT);
$request .= $body;
if (empty($this->response)) {
throw new AdapterException\RuntimeException("Error in cURL request: " . curl_error($this->curl));
}
// separating header from body because it is dangerous to accidentially replace strings in the body
$responseHeaderSize = curl_getinfo($this->curl, CURLINFO_HEADER_SIZE);
$responseHeaders = substr($this->response, 0, $responseHeaderSize);
// cURL automatically decodes chunked-messages, this means we have to
// disallow the Zend\Http\Response to do it again.
$responseHeaders = preg_replace("/Transfer-Encoding:\s*chunked\\r\\n/", "", $responseHeaders);
// cURL can automatically handle content encoding; prevent double-decoding from occurring
if (isset($this->config['curloptions'][CURLOPT_ENCODING])
&& '' == $this->config['curloptions'][CURLOPT_ENCODING]
) {
$responseHeaders = preg_replace("/Content-Encoding:\s*gzip\\r\\n/", '', $responseHeaders);
}
// cURL automatically handles Proxy rewrites, remove the "HTTP/1.0 200 Connection established" string:
$responseHeaders = preg_replace(
"/HTTP\/1.0\s*200\s*Connection\s*established\\r\\n\\r\\n/",
'',
$responseHeaders
);
// replace old header with new, cleaned up, header
$this->response = substr_replace($this->response, $responseHeaders, 0, $responseHeaderSize);
// Eliminate multiple HTTP responses.
do {
$parts = preg_split('|(?:\r?\n){2}|m', $this->response, 2);
$again = false;
if (isset($parts[1]) && preg_match("|^HTTP/1\.[01](.*?)\r\n|mi", $parts[1])) {
$this->response = $parts[1];
$again = true;
}
} while ($again);
return $request;
}
/**
* Return read response from server
*
* @return string
*/
public function read()
{
return $this->response;
}
/**
* Close the connection to the server
*
*/
public function close()
{
if (is_resource($this->curl)) {
curl_close($this->curl);
}
$this->curl = null;
$this->connectedTo = [null, null];
}
/**
* Get cUrl Handle
*
* @return resource
*/
public function getHandle()
{
return $this->curl;
}
/**
* Set output stream for the response
*
* @param resource $stream
* @return Curl
*/
public function setOutputStream($stream)
{
$this->outputStream = $stream;
return $this;
}
/**
* Header reader function for CURL
*
* @param resource $curl
* @param string $header
* @return int
*/
public function readHeader($curl, $header)
{
$this->response .= $header;
return strlen($header);
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter\Exception;
use Zend\Http\Client\Exception\ExceptionInterface as HttpClientException;
interface ExceptionInterface extends HttpClientException
{
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter\Exception;
/**
*/
class InitializationException extends RuntimeException
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter\Exception;
use Zend\Http\Client\Exception;
/**
*/
class InvalidArgumentException extends Exception\InvalidArgumentException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter\Exception;
use Zend\Http\Client\Exception;
/**
*/
class OutOfRangeException extends Exception\OutOfRangeException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter\Exception;
use Zend\Http\Client\Exception;
/**
*/
class RuntimeException extends Exception\RuntimeException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter\Exception;
/**
*/
class TimeoutException extends RuntimeException implements ExceptionInterface
{
const READ_TIMEOUT = 1000;
}

View File

@@ -0,0 +1,294 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter;
use Zend\Http\Client;
use Zend\Http\Client\Adapter\Exception as AdapterException;
use Zend\Http\Response;
use Zend\Stdlib\ErrorHandler;
/**
* HTTP Proxy-supporting Zend\Http\Client adapter class, based on the default
* socket based adapter.
*
* Should be used if proxy HTTP access is required. If no proxy is set, will
* fall back to Zend\Http\Client\Adapter\Socket behavior. Just like the
* default Socket adapter, this adapter does not require any special extensions
* installed.
*/
class Proxy extends Socket
{
/**
* Parameters array
*
* @var array
*/
protected $config = [
'ssltransport' => 'ssl',
'sslcert' => null,
'sslpassphrase' => null,
'sslverifypeer' => true,
'sslcapath' => null,
'sslallowselfsigned' => false,
'sslusecontext' => false,
'proxy_host' => '',
'proxy_port' => 8080,
'proxy_user' => '',
'proxy_pass' => '',
'proxy_auth' => Client::AUTH_BASIC,
'persistent' => false
];
/**
* Whether HTTPS CONNECT was already negotiated with the proxy or not
*
* @var bool
*/
protected $negotiated = false;
/**
* Set the configuration array for the adapter
*
* @param array $options
*/
public function setOptions($options = [])
{
//enforcing that the proxy keys are set in the form proxy_*
foreach ($options as $k => $v) {
if (preg_match("/^proxy[a-z]+/", $k)) {
$options['proxy_' . substr($k, 5, strlen($k))] = $v;
unset($options[$k]);
}
}
parent::setOptions($options);
}
/**
* Connect to the remote server
*
* Will try to connect to the proxy server. If no proxy was set, will
* fall back to the target server (behave like regular Socket adapter)
*
* @param string $host
* @param int $port
* @param bool $secure
* @throws AdapterException\RuntimeException
*/
public function connect($host, $port = 80, $secure = false)
{
// If no proxy is set, fall back to Socket adapter
if (! $this->config['proxy_host']) {
parent::connect($host, $port, $secure);
return;
}
/* Url might require stream context even if proxy connection doesn't */
if ($secure) {
$this->config['sslusecontext'] = true;
}
// Connect (a non-secure connection) to the proxy server
parent::connect(
$this->config['proxy_host'],
$this->config['proxy_port'],
false
);
}
/**
* Send request to the proxy server
*
* @param string $method
* @param \Zend\Uri\Uri $uri
* @param string $httpVer
* @param array $headers
* @param string $body
* @throws AdapterException\RuntimeException
* @return string Request as string
*/
public function write($method, $uri, $httpVer = '1.1', $headers = [], $body = '')
{
// If no proxy is set, fall back to default Socket adapter
if (! $this->config['proxy_host']) {
return parent::write($method, $uri, $httpVer, $headers, $body);
}
// Make sure we're properly connected
if (! $this->socket) {
throw new AdapterException\RuntimeException("Trying to write but we are not connected");
}
$host = $this->config['proxy_host'];
$port = $this->config['proxy_port'];
if ($this->connectedTo[0] != "tcp://$host" || $this->connectedTo[1] != $port) {
throw new AdapterException\RuntimeException("Trying to write but we are connected to the wrong proxy server");
}
// Add Proxy-Authorization header
if ($this->config['proxy_user'] && ! isset($headers['proxy-authorization'])) {
$headers['proxy-authorization'] = Client::encodeAuthHeader(
$this->config['proxy_user'], $this->config['proxy_pass'], $this->config['proxy_auth']
);
}
// if we are proxying HTTPS, preform CONNECT handshake with the proxy
if ($uri->getScheme() == 'https' && (! $this->negotiated)) {
$this->connectHandshake($uri->getHost(), $uri->getPort(), $httpVer, $headers);
$this->negotiated = true;
}
// Save request method for later
$this->method = $method;
// Build request headers
if ($this->negotiated) {
$path = $uri->getPath();
if ($uri->getQuery()) {
$path .= '?' . $uri->getQuery();
}
$request = "$method $path HTTP/$httpVer\r\n";
} else {
$request = "$method $uri HTTP/$httpVer\r\n";
}
// Add all headers to the request string
foreach ($headers as $k => $v) {
if (is_string($k)) {
$v = "$k: $v";
}
$request .= "$v\r\n";
}
if (is_resource($body)) {
$request .= "\r\n";
} else {
// Add the request body
$request .= "\r\n" . $body;
}
// Send the request
ErrorHandler::start();
$test = fwrite($this->socket, $request);
$error = ErrorHandler::stop();
if (!$test) {
throw new AdapterException\RuntimeException("Error writing request to proxy server", 0, $error);
}
if (is_resource($body)) {
if (stream_copy_to_stream($body, $this->socket) == 0) {
throw new AdapterException\RuntimeException('Error writing request to server');
}
}
return $request;
}
/**
* Preform handshaking with HTTPS proxy using CONNECT method
*
* @param string $host
* @param int $port
* @param string $httpVer
* @param array $headers
* @throws AdapterException\RuntimeException
*/
protected function connectHandshake($host, $port = 443, $httpVer = '1.1', array &$headers = [])
{
$request = "CONNECT $host:$port HTTP/$httpVer\r\n" .
"Host: " . $host . "\r\n";
// Add the user-agent header
if (isset($this->config['useragent'])) {
$request .= "User-agent: " . $this->config['useragent'] . "\r\n";
}
// If the proxy-authorization header is set, send it to proxy but remove
// it from headers sent to target host
if (isset($headers['proxy-authorization'])) {
$request .= "Proxy-authorization: " . $headers['proxy-authorization'] . "\r\n";
unset($headers['proxy-authorization']);
}
$request .= "\r\n";
// Send the request
ErrorHandler::start();
$test = fwrite($this->socket, $request);
$error = ErrorHandler::stop();
if (!$test) {
throw new AdapterException\RuntimeException("Error writing request to proxy server", 0, $error);
}
// Read response headers only
$response = '';
$gotStatus = false;
ErrorHandler::start();
while ($line = fgets($this->socket)) {
$gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
if ($gotStatus) {
$response .= $line;
if (!rtrim($line)) {
break;
}
}
}
ErrorHandler::stop();
// Check that the response from the proxy is 200
if (Response::fromString($response)->getStatusCode() != 200) {
throw new AdapterException\RuntimeException("Unable to connect to HTTPS proxy. Server response: " . $response);
}
// If all is good, switch socket to secure mode. We have to fall back
// through the different modes
$modes = [
STREAM_CRYPTO_METHOD_TLS_CLIENT,
STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
STREAM_CRYPTO_METHOD_SSLv2_CLIENT
];
$success = false;
foreach ($modes as $mode) {
$success = stream_socket_enable_crypto($this->socket, true, $mode);
if ($success) {
break;
}
}
if (! $success) {
throw new AdapterException\RuntimeException("Unable to connect to" .
" HTTPS server through proxy: could not negotiate secure connection.");
}
}
/**
* Close the connection to the server
*
*/
public function close()
{
parent::close();
$this->negotiated = false;
}
/**
* Destructor: make sure the socket is disconnected
*
*/
public function __destruct()
{
if ($this->socket) {
$this->close();
}
}
}

View File

@@ -0,0 +1,634 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter;
use Traversable;
use Zend\Http\Client\Adapter\AdapterInterface as HttpAdapter;
use Zend\Http\Client\Adapter\Exception as AdapterException;
use Zend\Http\Response;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\ErrorHandler;
/**
* A sockets based (stream\socket\client) adapter class for Zend\Http\Client. Can be used
* on almost every PHP environment, and does not require any special extensions.
*/
class Socket implements HttpAdapter, StreamInterface
{
/**
* Map SSL transport wrappers to stream crypto method constants
*
* @var array
*/
protected static $sslCryptoTypes = [
'ssl' => STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
'sslv2' => STREAM_CRYPTO_METHOD_SSLv2_CLIENT,
'sslv3' => STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
'tls' => STREAM_CRYPTO_METHOD_TLS_CLIENT,
];
/**
* The socket for server connection
*
* @var resource|null
*/
protected $socket = null;
/**
* What host/port are we connected to?
*
* @var array
*/
protected $connectedTo = [null, null];
/**
* Stream for storing output
*
* @var resource
*/
protected $outStream = null;
/**
* Parameters array
*
* @var array
*/
protected $config = [
'persistent' => false,
'ssltransport' => 'ssl',
'sslcert' => null,
'sslpassphrase' => null,
'sslverifypeer' => true,
'sslcafile' => null,
'sslcapath' => null,
'sslallowselfsigned' => false,
'sslusecontext' => false,
];
/**
* Request method - will be set by write() and might be used by read()
*
* @var string
*/
protected $method = null;
/**
* Stream context
*
* @var resource
*/
protected $context = null;
/**
* Adapter constructor, currently empty. Config is set using setOptions()
*
*/
public function __construct()
{
}
/**
* Set the configuration array for the adapter
*
* @param array|Traversable $options
* @throws AdapterException\InvalidArgumentException
*/
public function setOptions($options = [])
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
if (!is_array($options)) {
throw new AdapterException\InvalidArgumentException(
'Array or Zend\Config object expected, got ' . gettype($options)
);
}
foreach ($options as $k => $v) {
$this->config[strtolower($k)] = $v;
}
}
/**
* Retrieve the array of all configuration options
*
* @return array
*/
public function getConfig()
{
return $this->config;
}
/**
* Set the stream context for the TCP connection to the server
*
* Can accept either a pre-existing stream context resource, or an array
* of stream options, similar to the options array passed to the
* stream_context_create() PHP function. In such case a new stream context
* will be created using the passed options.
*
* @since Zend Framework 1.9
*
* @param mixed $context Stream context or array of context options
* @throws Exception\InvalidArgumentException
* @return Socket
*/
public function setStreamContext($context)
{
if (is_resource($context) && get_resource_type($context) == 'stream-context') {
$this->context = $context;
} elseif (is_array($context)) {
$this->context = stream_context_create($context);
} else {
// Invalid parameter
throw new AdapterException\InvalidArgumentException(
"Expecting either a stream context resource or array, got " . gettype($context)
);
}
return $this;
}
/**
* Get the stream context for the TCP connection to the server.
*
* If no stream context is set, will create a default one.
*
* @return resource
*/
public function getStreamContext()
{
if (! $this->context) {
$this->context = stream_context_create();
}
return $this->context;
}
/**
* Connect to the remote server
*
* @param string $host
* @param int $port
* @param bool $secure
* @throws AdapterException\RuntimeException
*/
public function connect($host, $port = 80, $secure = false)
{
// If we are connected to the wrong host, disconnect first
$connectedHost = (strpos($this->connectedTo[0], '://'))
? substr($this->connectedTo[0], (strpos($this->connectedTo[0], '://') + 3), strlen($this->connectedTo[0]))
: $this->connectedTo[0];
if ($connectedHost != $host || $this->connectedTo[1] != $port) {
if (is_resource($this->socket)) {
$this->close();
}
}
// Now, if we are not connected, connect
if (!is_resource($this->socket) || ! $this->config['keepalive']) {
$context = $this->getStreamContext();
if ($secure || $this->config['sslusecontext']) {
if ($this->config['sslverifypeer'] !== null) {
if (!stream_context_set_option($context, 'ssl', 'verify_peer', $this->config['sslverifypeer'])) {
throw new AdapterException\RuntimeException('Unable to set sslverifypeer option');
}
}
if ($this->config['sslcafile']) {
if (!stream_context_set_option($context, 'ssl', 'cafile', $this->config['sslcafile'])) {
throw new AdapterException\RuntimeException('Unable to set sslcafile option');
}
}
if ($this->config['sslcapath']) {
if (!stream_context_set_option($context, 'ssl', 'capath', $this->config['sslcapath'])) {
throw new AdapterException\RuntimeException('Unable to set sslcapath option');
}
}
if ($this->config['sslallowselfsigned'] !== null) {
if (!stream_context_set_option($context, 'ssl', 'allow_self_signed', $this->config['sslallowselfsigned'])) {
throw new AdapterException\RuntimeException('Unable to set sslallowselfsigned option');
}
}
if ($this->config['sslcert'] !== null) {
if (!stream_context_set_option($context, 'ssl', 'local_cert', $this->config['sslcert'])) {
throw new AdapterException\RuntimeException('Unable to set sslcert option');
}
}
if ($this->config['sslpassphrase'] !== null) {
if (!stream_context_set_option($context, 'ssl', 'passphrase', $this->config['sslpassphrase'])) {
throw new AdapterException\RuntimeException('Unable to set sslpassphrase option');
}
}
}
$flags = STREAM_CLIENT_CONNECT;
if ($this->config['persistent']) {
$flags |= STREAM_CLIENT_PERSISTENT;
}
ErrorHandler::start();
$this->socket = stream_socket_client(
$host . ':' . $port,
$errno,
$errstr,
(int) $this->config['timeout'],
$flags,
$context
);
$error = ErrorHandler::stop();
if (!$this->socket) {
$this->close();
throw new AdapterException\RuntimeException(
sprintf(
'Unable to connect to %s:%d%s',
$host,
$port,
($error ? ' . Error #' . $error->getCode() . ': ' . $error->getMessage() : '')
),
0,
$error
);
}
// Set the stream timeout
if (!stream_set_timeout($this->socket, (int) $this->config['timeout'])) {
throw new AdapterException\RuntimeException('Unable to set the connection timeout');
}
if ($secure || $this->config['sslusecontext']) {
if ($this->config['ssltransport'] && isset(static::$sslCryptoTypes[$this->config['ssltransport']])) {
$sslCryptoMethod = static::$sslCryptoTypes[$this->config['ssltransport']];
} else {
$sslCryptoMethod = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
}
ErrorHandler::start();
$test = stream_socket_enable_crypto($this->socket, true, $sslCryptoMethod);
$error = ErrorHandler::stop();
if (!$test || $error) {
// Error handling is kind of difficult when it comes to SSL
$errorString = '';
if (extension_loaded('openssl')) {
while (($sslError = openssl_error_string()) != false) {
$errorString .= "; SSL error: $sslError";
}
}
$this->close();
if ((! $errorString) && $this->config['sslverifypeer']) {
// There's good chance our error is due to sslcapath not being properly set
if (! ($this->config['sslcafile'] || $this->config['sslcapath'])) {
$errorString = 'make sure the "sslcafile" or "sslcapath" option are properly set for the environment.';
} elseif ($this->config['sslcafile'] && !is_file($this->config['sslcafile'])) {
$errorString = 'make sure the "sslcafile" option points to a valid SSL certificate file';
} elseif ($this->config['sslcapath'] && !is_dir($this->config['sslcapath'])) {
$errorString = 'make sure the "sslcapath" option points to a valid SSL certificate directory';
}
}
if ($errorString) {
$errorString = ": $errorString";
}
throw new AdapterException\RuntimeException(sprintf(
'Unable to enable crypto on TCP connection %s%s',
$host,
$errorString
), 0, $error);
}
$host = $this->config['ssltransport'] . "://" . $host;
} else {
$host = 'tcp://' . $host;
}
// Update connectedTo
$this->connectedTo = [$host, $port];
}
}
/**
* Send request to the remote server
*
* @param string $method
* @param \Zend\Uri\Uri $uri
* @param string $httpVer
* @param array $headers
* @param string $body
* @throws AdapterException\RuntimeException
* @return string Request as string
*/
public function write($method, $uri, $httpVer = '1.1', $headers = [], $body = '')
{
// Make sure we're properly connected
if (! $this->socket) {
throw new AdapterException\RuntimeException('Trying to write but we are not connected');
}
$host = $uri->getHost();
$host = (strtolower($uri->getScheme()) == 'https' ? $this->config['ssltransport'] : 'tcp') . '://' . $host;
if ($this->connectedTo[0] != $host || $this->connectedTo[1] != $uri->getPort()) {
throw new AdapterException\RuntimeException('Trying to write but we are connected to the wrong host');
}
// Save request method for later
$this->method = $method;
// Build request headers
$path = $uri->getPath();
if ($uri->getQuery()) {
$path .= '?' . $uri->getQuery();
}
$request = "{$method} {$path} HTTP/{$httpVer}\r\n";
foreach ($headers as $k => $v) {
if (is_string($k)) {
$v = ucfirst($k) . ": $v";
}
$request .= "$v\r\n";
}
if (is_resource($body)) {
$request .= "\r\n";
} else {
// Add the request body
$request .= "\r\n" . $body;
}
// Send the request
ErrorHandler::start();
$test = fwrite($this->socket, $request);
$error = ErrorHandler::stop();
if (false === $test) {
throw new AdapterException\RuntimeException('Error writing request to server', 0, $error);
}
if (is_resource($body)) {
if (stream_copy_to_stream($body, $this->socket) == 0) {
throw new AdapterException\RuntimeException('Error writing request to server');
}
}
return $request;
}
/**
* Read response from server
*
* @throws AdapterException\RuntimeException
* @return string
*/
public function read()
{
// First, read headers only
$response = '';
$gotStatus = false;
while (($line = fgets($this->socket)) !== false) {
$gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
if ($gotStatus) {
$response .= $line;
if (rtrim($line) === '') {
break;
}
}
}
$this->_checkSocketReadTimeout();
$responseObj= Response::fromString($response);
$statusCode = $responseObj->getStatusCode();
// Handle 100 and 101 responses internally by restarting the read again
if ($statusCode == 100 || $statusCode == 101) {
return $this->read();
}
// Check headers to see what kind of connection / transfer encoding we have
$headers = $responseObj->getHeaders();
/**
* Responses to HEAD requests and 204 or 304 responses are not expected
* to have a body - stop reading here
*/
if ($statusCode == 304 || $statusCode == 204 ||
$this->method == \Zend\Http\Request::METHOD_HEAD) {
// Close the connection if requested to do so by the server
$connection = $headers->get('connection');
if ($connection && $connection->getFieldValue() == 'close') {
$this->close();
}
return $response;
}
// If we got a 'transfer-encoding: chunked' header
$transferEncoding = $headers->get('transfer-encoding');
$contentLength = $headers->get('content-length');
if ($transferEncoding !== false) {
if (strtolower($transferEncoding->getFieldValue()) == 'chunked') {
do {
$line = fgets($this->socket);
$this->_checkSocketReadTimeout();
$chunk = $line;
// Figure out the next chunk size
$chunksize = trim($line);
if (! ctype_xdigit($chunksize)) {
$this->close();
throw new AdapterException\RuntimeException('Invalid chunk size "' .
$chunksize . '" unable to read chunked body');
}
// Convert the hexadecimal value to plain integer
$chunksize = hexdec($chunksize);
// Read next chunk
$readTo = ftell($this->socket) + $chunksize;
do {
$currentPos = ftell($this->socket);
if ($currentPos >= $readTo) {
break;
}
if ($this->outStream) {
if (stream_copy_to_stream($this->socket, $this->outStream, $readTo - $currentPos) == 0) {
$this->_checkSocketReadTimeout();
break;
}
} else {
$line = fread($this->socket, $readTo - $currentPos);
if ($line === false || strlen($line) === 0) {
$this->_checkSocketReadTimeout();
break;
}
$chunk .= $line;
}
} while (! feof($this->socket));
ErrorHandler::start();
$chunk .= fgets($this->socket);
ErrorHandler::stop();
$this->_checkSocketReadTimeout();
if (!$this->outStream) {
$response .= $chunk;
}
} while ($chunksize > 0);
} else {
$this->close();
throw new AdapterException\RuntimeException('Cannot handle "' .
$transferEncoding->getFieldValue() . '" transfer encoding');
}
// We automatically decode chunked-messages when writing to a stream
// this means we have to disallow the Zend\Http\Response to do it again
if ($this->outStream) {
$response = str_ireplace("Transfer-Encoding: chunked\r\n", '', $response);
}
// Else, if we got the content-length header, read this number of bytes
} elseif ($contentLength !== false) {
// If we got more than one Content-Length header (see ZF-9404) use
// the last value sent
if (is_array($contentLength)) {
$contentLength = $contentLength[count($contentLength) - 1];
}
$contentLength = $contentLength->getFieldValue();
$currentPos = ftell($this->socket);
for ($readTo = $currentPos + $contentLength;
$readTo > $currentPos;
$currentPos = ftell($this->socket)) {
if ($this->outStream) {
if (stream_copy_to_stream($this->socket, $this->outStream, $readTo - $currentPos) == 0) {
$this->_checkSocketReadTimeout();
break;
}
} else {
$chunk = fread($this->socket, $readTo - $currentPos);
if ($chunk === false || strlen($chunk) === 0) {
$this->_checkSocketReadTimeout();
break;
}
$response .= $chunk;
}
// Break if the connection ended prematurely
if (feof($this->socket)) {
break;
}
}
// Fallback: just read the response until EOF
} else {
do {
if ($this->outStream) {
if (stream_copy_to_stream($this->socket, $this->outStream) == 0) {
$this->_checkSocketReadTimeout();
break;
}
} else {
$buff = fread($this->socket, 8192);
if ($buff === false || strlen($buff) === 0) {
$this->_checkSocketReadTimeout();
break;
} else {
$response .= $buff;
}
}
} while (feof($this->socket) === false);
$this->close();
}
// Close the connection if requested to do so by the server
$connection = $headers->get('connection');
if ($connection && $connection->getFieldValue() == 'close') {
$this->close();
}
return $response;
}
/**
* Close the connection to the server
*
*/
public function close()
{
if (is_resource($this->socket)) {
ErrorHandler::start();
fclose($this->socket);
ErrorHandler::stop();
}
$this->socket = null;
$this->connectedTo = [null, null];
}
/**
* Check if the socket has timed out - if so close connection and throw
* an exception
*
* @throws AdapterException\TimeoutException with READ_TIMEOUT code
*/
protected function _checkSocketReadTimeout()
{
if ($this->socket) {
$info = stream_get_meta_data($this->socket);
$timedout = $info['timed_out'];
if ($timedout) {
$this->close();
throw new AdapterException\TimeoutException(
"Read timed out after {$this->config['timeout']} seconds",
AdapterException\TimeoutException::READ_TIMEOUT
);
}
}
}
/**
* Set output stream for the response
*
* @param resource $stream
* @return \Zend\Http\Client\Adapter\Socket
*/
public function setOutputStream($stream)
{
$this->outStream = $stream;
return $this;
}
/**
* Destructor: make sure the socket is disconnected
*
* If we are in persistent TCP mode, will not close the connection
*
*/
public function __destruct()
{
if (! $this->config['persistent']) {
if ($this->socket) {
$this->close();
}
}
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter;
/**
* An interface description for Zend\Http\Client\Adapter\Stream classes.
*
* This interface describes Zend\Http\Client\Adapter which supports streaming.
*/
interface StreamInterface
{
/**
* Set output stream
*
* This function sets output stream where the result will be stored.
*
* @param resource $stream Stream to write the output to
*
*/
public function setOutputStream($stream);
}

View File

@@ -0,0 +1,216 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter;
use Traversable;
use Zend\Http\Response;
use Zend\Stdlib\ArrayUtils;
/**
* A testing-purposes adapter.
*
* Should be used to test all components that rely on Zend\Http\Client,
* without actually performing an HTTP request. You should instantiate this
* object manually, and then set it as the client's adapter. Then, you can
* set the expected response using the setResponse() method.
*/
class Test implements AdapterInterface
{
/**
* Parameters array
*
* @var array
*/
protected $config = [];
/**
* Buffer of responses to be returned by the read() method. Can be
* set using setResponse() and addResponse().
*
* @var array
*/
protected $responses = ["HTTP/1.1 400 Bad Request\r\n\r\n"];
/**
* Current position in the response buffer
*
* @var int
*/
protected $responseIndex = 0;
/**
* Whether or not the next request will fail with an exception
*
* @var bool
*/
protected $nextRequestWillFail = false;
/**
* Adapter constructor, currently empty. Config is set using setOptions()
*/
public function __construct()
{
}
/**
* Set the nextRequestWillFail flag
*
* @param bool $flag
* @return \Zend\Http\Client\Adapter\Test
*/
public function setNextRequestWillFail($flag)
{
$this->nextRequestWillFail = (bool) $flag;
return $this;
}
/**
* Set the configuration array for the adapter
*
* @param array|Traversable $options
* @throws Exception\InvalidArgumentException
*/
public function setOptions($options = [])
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
if (! is_array($options)) {
throw new Exception\InvalidArgumentException(
'Array or Traversable object expected, got ' . gettype($options)
);
}
foreach ($options as $k => $v) {
$this->config[strtolower($k)] = $v;
}
}
/**
* Connect to the remote server
*
* @param string $host
* @param int $port
* @param bool $secure
* @throws Exception\RuntimeException
*/
public function connect($host, $port = 80, $secure = false)
{
if ($this->nextRequestWillFail) {
$this->nextRequestWillFail = false;
throw new Exception\RuntimeException('Request failed');
}
}
/**
* Send request to the remote server
*
* @param string $method
* @param \Zend\Uri\Uri $uri
* @param string $httpVer
* @param array $headers
* @param string $body
* @return string Request as string
*/
public function write($method, $uri, $httpVer = '1.1', $headers = [], $body = '')
{
// Build request headers
$path = $uri->getPath();
if (empty($path)) {
$path = '/';
}
if ($uri->getQuery()) {
$path .= '?' . $uri->getQuery();
}
$request = "{$method} {$path} HTTP/{$httpVer}\r\n";
foreach ($headers as $k => $v) {
if (is_string($k)) {
$v = ucfirst($k) . ": $v";
}
$request .= "$v\r\n";
}
// Add the request body
$request .= "\r\n" . $body;
// Do nothing - just return the request as string
return $request;
}
/**
* Return the response set in $this->setResponse()
*
* @return string
*/
public function read()
{
if ($this->responseIndex >= count($this->responses)) {
$this->responseIndex = 0;
}
return $this->responses[$this->responseIndex++];
}
/**
* Close the connection (dummy)
*
*/
public function close()
{
}
/**
* Set the HTTP response(s) to be returned by this adapter
*
* @param \Zend\Http\Response|array|string $response
*/
public function setResponse($response)
{
if ($response instanceof Response) {
$response = $response->toString();
}
$this->responses = (array) $response;
$this->responseIndex = 0;
}
/**
* Add another response to the response buffer.
*
* @param string|Response $response
*/
public function addResponse($response)
{
if ($response instanceof Response) {
$response = $response->toString();
}
$this->responses[] = $response;
}
/**
* Sets the position of the response buffer. Selects which
* response will be returned on the next call to read().
*
* @param int $index
* @throws Exception\OutOfRangeException
*/
public function setResponseIndex($index)
{
if ($index < 0 || $index >= count($this->responses)) {
throw new Exception\OutOfRangeException(
'Index out of range of response buffer size');
}
$this->responseIndex = $index;
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Exception;
use Zend\Http\Exception\ExceptionInterface as HttpException;
interface ExceptionInterface extends HttpException
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Exception;
use Zend\Http\Exception;
/**
*/
class InvalidArgumentException extends Exception\InvalidArgumentException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Exception;
use Zend\Http\Exception;
class OutOfRangeException extends Exception\OutOfRangeException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Exception;
use Zend\Http\Exception;
/**
*/
class RuntimeException extends Exception\RuntimeException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,112 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http;
/**
* Http static client
*/
class ClientStatic
{
/**
* @var Client
*/
protected static $client;
/**
* Get the static HTTP client
*
* @param array|Traversable $options
* @return Client
*/
protected static function getStaticClient($options = null)
{
if (!isset(static::$client) || $options !== null) {
static::$client = new Client(null, $options);
}
return static::$client;
}
/**
* HTTP GET METHOD (static)
*
* @param string $url
* @param array $query
* @param array $headers
* @param mixed $body
* @param array|Traversable $clientOptions
* @return Response|bool
*/
public static function get($url, $query = [], $headers = [], $body = null, $clientOptions = null)
{
if (empty($url)) {
return false;
}
$request= new Request();
$request->setUri($url);
$request->setMethod(Request::METHOD_GET);
if (!empty($query) && is_array($query)) {
$request->getQuery()->fromArray($query);
}
if (!empty($headers) && is_array($headers)) {
$request->getHeaders()->addHeaders($headers);
}
if (!empty($body)) {
$request->setContent($body);
}
return static::getStaticClient($clientOptions)->send($request);
}
/**
* HTTP POST METHOD (static)
*
* @param string $url
* @param array $params
* @param array $headers
* @param mixed $body
* @param array|Traversable $clientOptions
* @throws Exception\InvalidArgumentException
* @return Response|bool
*/
public static function post($url, $params, $headers = [], $body = null, $clientOptions = null)
{
if (empty($url)) {
return false;
}
$request= new Request();
$request->setUri($url);
$request->setMethod(Request::METHOD_POST);
if (!empty($params) && is_array($params)) {
$request->getPost()->fromArray($params);
} else {
throw new Exception\InvalidArgumentException('The array of post parameters is empty');
}
if (!isset($headers['Content-Type'])) {
$headers['Content-Type'] = Client::ENC_URLENCODED;
}
if (!empty($headers) && is_array($headers)) {
$request->getHeaders()->addHeaders($headers);
}
if (!empty($body)) {
$request->setContent($body);
}
return static::getStaticClient($clientOptions)->send($request);
}
}

View File

@@ -0,0 +1,367 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 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 Zend\Http\Header\SetCookie;
use Zend\Uri;
/**
* A Zend\Http\Cookies object is designed to contain and maintain HTTP cookies, and should
* be used along with Zend\Http\Client in order to manage cookies across HTTP requests and
* responses.
*
* The class contains an array of Zend\Http\Header\Cookie objects. Cookies can be added
* automatically from a request or manually. Then, the Cookies class can find and return the
* cookies needed for a specific HTTP request.
*
* A special parameter can be passed to all methods of this class that return cookies: Cookies
* can be returned either in their native form (as Zend\Http\Header\Cookie objects) or as strings -
* the later is suitable for sending as the value of the "Cookie" header in an HTTP request.
* You can also choose, when returning more than one cookie, whether to get an array of strings
* (by passing Zend\Http\Client\Cookies::COOKIE_STRING_ARRAY) or one unified string for all cookies
* (by passing Zend\Http\Client\Cookies::COOKIE_STRING_CONCAT).
*
* @link http://wp.netscape.com/newsref/std/cookie_spec.html for some specs.
*/
class Cookies extends Headers
{
/**
* Return cookie(s) as a Zend\Http\Cookie object
*
*/
const COOKIE_OBJECT = 0;
/**
* Return cookie(s) as a string (suitable for sending in an HTTP request)
*
*/
const COOKIE_STRING_ARRAY = 1;
/**
* Return all cookies as one long string (suitable for sending in an HTTP request)
*
*/
const COOKIE_STRING_CONCAT = 2;
/**
* Return all cookies as one long string (strict mode)
* - Single space after the semi-colon separating each cookie
* - Remove trailing semi-colon, if any
*/
const COOKIE_STRING_CONCAT_STRICT = 3;
/**
* @var array
*/
protected $cookies = [];
/**
* @var \Zend\Http\Headers
*/
protected $headers = null;
/**
* @var array
*/
protected $rawCookies;
/**
* @static
* @throws Exception\RuntimeException
* @param $string
* @return void
*/
public static function fromString($string)
{
throw new Exception\RuntimeException(
__CLASS__ . '::' . __FUNCTION__ . ' should not be used as a factory, use '
. __NAMESPACE__ . '\Headers::fromtString() instead.'
);
}
/**
* Add a cookie to the class. Cookie should be passed either as a Zend\Http\Header\SetCookie object
* or as a string - in which case an object is created from the string.
*
* @param SetCookie|string $cookie
* @param Uri\Uri|string $refUri Optional reference URI (for domain, path, secure)
* @throws Exception\InvalidArgumentException
*/
public function addCookie($cookie, $refUri = null)
{
if (is_string($cookie)) {
$cookie = SetCookie::fromString($cookie, $refUri);
}
if ($cookie instanceof SetCookie) {
$domain = $cookie->getDomain();
$path = $cookie->getPath();
if (!isset($this->cookies[$domain])) {
$this->cookies[$domain] = [];
}
if (!isset($this->cookies[$domain][$path])) {
$this->cookies[$domain][$path] = [];
}
$this->cookies[$domain][$path][$cookie->getName()] = $cookie;
$this->rawCookies[] = $cookie;
} else {
throw new Exception\InvalidArgumentException('Supplient argument is not a valid cookie string or object');
}
}
/**
* Parse an HTTP response, adding all the cookies set in that response
*
* @param Response $response
* @param Uri\Uri|string $refUri Requested URI
*/
public function addCookiesFromResponse(Response $response, $refUri)
{
$cookieHdrs = $response->getHeaders()->get('Set-Cookie');
if (is_array($cookieHdrs) || $cookieHdrs instanceof ArrayIterator) {
foreach ($cookieHdrs as $cookie) {
$this->addCookie($cookie, $refUri);
}
} elseif (is_string($cookieHdrs)) {
$this->addCookie($cookieHdrs, $refUri);
}
}
/**
* Get all cookies in the cookie jar as an array
*
* @param int $retAs Whether to return cookies as objects of \Zend\Http\Header\SetCookie or as strings
* @return array|string
*/
public function getAllCookies($retAs = self::COOKIE_OBJECT)
{
$cookies = $this->_flattenCookiesArray($this->cookies, $retAs);
return $cookies;
}
/**
* Return an array of all cookies matching a specific request according to the request URI,
* whether session cookies should be sent or not, and the time to consider as "now" when
* checking cookie expiry time.
*
* @param string|Uri\Uri $uri URI to check against (secure, domain, path)
* @param bool $matchSessionCookies Whether to send session cookies
* @param int $retAs Whether to return cookies as objects of \Zend\Http\Header\Cookie or as strings
* @param int $now Override the current time when checking for expiry time
* @throws Exception\InvalidArgumentException if invalid URI specified
* @return array|string
*/
public function getMatchingCookies(
$uri,
$matchSessionCookies = true,
$retAs = self::COOKIE_OBJECT,
$now = null
) {
if (is_string($uri)) {
$uri = Uri\UriFactory::factory($uri, 'http');
} elseif (!$uri instanceof Uri\Uri) {
throw new Exception\InvalidArgumentException("Invalid URI string or object passed");
}
$host = $uri->getHost();
if (empty($host)) {
throw new Exception\InvalidArgumentException('Invalid URI specified; does not contain a host');
}
// First, reduce the array of cookies to only those matching domain and path
$cookies = $this->_matchDomain($host);
$cookies = $this->_matchPath($cookies, $uri->getPath());
$cookies = $this->_flattenCookiesArray($cookies, self::COOKIE_OBJECT);
// Next, run Cookie->match on all cookies to check secure, time and session matching
$ret = [];
foreach ($cookies as $cookie) {
if ($cookie->match($uri, $matchSessionCookies, $now)) {
$ret[] = $cookie;
}
}
// Now, use self::_flattenCookiesArray again - only to convert to the return format ;)
$ret = $this->_flattenCookiesArray($ret, $retAs);
return $ret;
}
/**
* Get a specific cookie according to a URI and name
*
* @param Uri\Uri|string $uri The uri (domain and path) to match
* @param string $cookieName The cookie's name
* @param int $retAs Whether to return cookies as objects of \Zend\Http\Header\SetCookie or as strings
* @throws Exception\InvalidArgumentException if invalid URI specified or invalid $retAs value
* @return SetCookie|string
*/
public function getCookie($uri, $cookieName, $retAs = self::COOKIE_OBJECT)
{
if (is_string($uri)) {
$uri = Uri\UriFactory::factory($uri, 'http');
} elseif (!$uri instanceof Uri\Uri) {
throw new Exception\InvalidArgumentException('Invalid URI specified');
}
$host = $uri->getHost();
if (empty($host)) {
throw new Exception\InvalidArgumentException('Invalid URI specified; host missing');
}
// Get correct cookie path
$path = $uri->getPath();
$lastSlashPos = strrpos($path, '/') ?: 0;
$path = substr($path, 0, $lastSlashPos);
if (! $path) {
$path = '/';
}
if (isset($this->cookies[$uri->getHost()][$path][$cookieName])) {
$cookie = $this->cookies[$uri->getHost()][$path][$cookieName];
switch ($retAs) {
case self::COOKIE_OBJECT:
return $cookie;
case self::COOKIE_STRING_ARRAY:
case self::COOKIE_STRING_CONCAT:
return $cookie->__toString();
default:
throw new Exception\InvalidArgumentException("Invalid value passed for \$retAs: {$retAs}");
}
}
return false;
}
/**
* Helper function to recursively flatten an array. Should be used when exporting the
* cookies array (or parts of it)
*
* @param \Zend\Http\Header\SetCookie|array $ptr
* @param int $retAs What value to return
* @return array|string
*/
protected function _flattenCookiesArray($ptr, $retAs = self::COOKIE_OBJECT)
{
if (is_array($ptr)) {
$ret = ($retAs == self::COOKIE_STRING_CONCAT ? '' : []);
foreach ($ptr as $item) {
if ($retAs == self::COOKIE_STRING_CONCAT) {
$ret .= $this->_flattenCookiesArray($item, $retAs);
} else {
$ret = array_merge($ret, $this->_flattenCookiesArray($item, $retAs));
}
}
return $ret;
} elseif ($ptr instanceof SetCookie) {
switch ($retAs) {
case self::COOKIE_STRING_ARRAY:
return [$ptr->__toString()];
case self::COOKIE_STRING_CONCAT:
return $ptr->__toString();
case self::COOKIE_OBJECT:
default:
return [$ptr];
}
}
return;
}
/**
* Return a subset of the cookies array matching a specific domain
*
* @param string $domain
* @return array
*/
protected function _matchDomain($domain)
{
$ret = [];
foreach (array_keys($this->cookies) as $cdom) {
if (SetCookie::matchCookieDomain($cdom, $domain)) {
$ret[$cdom] = $this->cookies[$cdom];
}
}
return $ret;
}
/**
* Return a subset of a domain-matching cookies that also match a specified path
*
* @param array $domains
* @param string $path
* @return array
*/
protected function _matchPath($domains, $path)
{
$ret = [];
foreach ($domains as $dom => $pathsArray) {
foreach (array_keys($pathsArray) as $cpath) {
if (SetCookie::matchCookiePath($cpath, $path)) {
if (! isset($ret[$dom])) {
$ret[$dom] = [];
}
$ret[$dom][$cpath] = $pathsArray[$cpath];
}
}
}
return $ret;
}
/**
* Create a new Cookies object and automatically load into it all the
* cookies set in a Response object. If $uri is set, it will be
* considered as the requested URI for setting default domain and path
* of the cookie.
*
* @param Response $response HTTP Response object
* @param Uri\Uri|string $refUri The requested URI
* @return Cookies
* @todo Add the $uri functionality.
*/
public static function fromResponse(Response $response, $refUri)
{
$jar = new static();
$jar->addCookiesFromResponse($response, $refUri);
return $jar;
}
/**
* Tells if the array of cookies is empty
*
* @return bool
*/
public function isEmpty()
{
return count($this) == 0;
}
/**
* Empties the cookieJar of any cookie
*
* @return Cookies
*/
public function reset()
{
$this->cookies = $this->rawCookies = [];
return $this;
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Exception;
interface ExceptionInterface
{
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Exception;
class OutOfRangeException extends \OutOfRangeException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Exception;
class RuntimeException extends \RuntimeException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,456 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use stdClass;
/**
* Abstract Accept Header
*
* Naming conventions:
*
* Accept: audio/mp3; q=0.2; version=0.5, audio/basic+mp3
* |------------------------------------------------------| header line
* |------| field name
* |-----------------------------------------------| field value
* |-------------------------------| field value part
* |------| type
* |--| subtype
* |--| format
* |----| subtype
* |---| format
* |-------------------| parameter set
* |-----------| parameter
* |-----| parameter key
* |--| parameter value
* |---| priority
*
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
* @author Dolf Schimmel - Freeaqingme
*/
abstract class AbstractAccept implements HeaderInterface
{
/**
*
* @var stdClass[]
*/
protected $fieldValueParts = [];
protected $regexAddType;
/**
* Determines if since last mutation the stack was sorted
*
* @var bool
*/
protected $sorted = false;
/**
* Parse a full header line or just the field value part.
*
* @param string $headerLine
*/
public function parseHeaderLine($headerLine)
{
if (strpos($headerLine, ':') !== false) {
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
if (strtolower($name) !== strtolower($this->getFieldName())) {
$value = $headerLine; // This is just for preserve the BC.
}
} else {
$value = $headerLine;
}
HeaderValue::assertValid($value);
foreach ($this->getFieldValuePartsFromHeaderLine($value) as $value) {
$this->addFieldValuePartToQueue($value);
}
}
/**
* Factory method: parse Accept header string
*
* @param string $headerLine
* @return Accept
*/
public static function fromString($headerLine)
{
$obj = new static();
$obj->parseHeaderLine($headerLine);
return $obj;
}
/**
* Parse the Field Value Parts represented by a header line
*
* @param string $headerLine
* @throws Exception\InvalidArgumentException If header is invalid
* @return array
*/
public function getFieldValuePartsFromHeaderLine($headerLine)
{
// process multiple accept values, they may be between quotes
if (!preg_match_all('/(?:[^,"]|"(?:[^\\\"]|\\\.)*")+/', $headerLine, $values)
|| !isset($values[0])
) {
throw new Exception\InvalidArgumentException(
'Invalid header line for ' . $this->getFieldName() . ' header string'
);
}
$out = [];
foreach ($values[0] as $value) {
$value = trim($value);
$out[] = $this->parseFieldValuePart($value);
}
return $out;
}
/**
* Parse the accept params belonging to a media range
*
* @param string $fieldValuePart
* @return stdClass
*/
protected function parseFieldValuePart($fieldValuePart)
{
$raw = $subtypeWhole = $type = $fieldValuePart;
if ($pos = strpos($fieldValuePart, ';')) {
$type = substr($fieldValuePart, 0, $pos);
}
$params = $this->getParametersFromFieldValuePart($fieldValuePart);
if ($pos = strpos($fieldValuePart, ';')) {
$fieldValuePart = trim(substr($fieldValuePart, 0, $pos));
}
$format = '*';
$subtype = '*';
return (object) [
'typeString' => trim($fieldValuePart),
'type' => $type,
'subtype' => $subtype,
'subtypeRaw' => $subtypeWhole,
'format' => $format,
'priority' => isset($params['q']) ? $params['q'] : 1,
'params' => $params,
'raw' => trim($raw)
];
}
/**
* Parse the keys contained in the header line
*
* @param string $fieldValuePart
* @return array
*/
protected function getParametersFromFieldValuePart($fieldValuePart)
{
$params = [];
if ((($pos = strpos($fieldValuePart, ';')) !== false)) {
preg_match_all('/(?:[^;"]|"(?:[^\\\"]|\\\.)*")+/', $fieldValuePart, $paramsStrings);
if (isset($paramsStrings[0])) {
array_shift($paramsStrings[0]);
$paramsStrings = $paramsStrings[0];
}
foreach ($paramsStrings as $param) {
$explode = explode('=', $param, 2);
$value = trim($explode[1]);
if (isset($value[0]) && $value[0] == '"' && substr($value, -1) == '"') {
$value = substr(substr($value, 1), 0, -1);
}
$params[trim($explode[0])] = stripslashes($value);
}
}
return $params;
}
/**
* Get field value
*
* @param array|null $values
* @return string
*/
public function getFieldValue($values = null)
{
if (!$values) {
return $this->getFieldValue($this->fieldValueParts);
}
$strings = [];
foreach ($values as $value) {
$params = $value->params;
array_walk($params, [$this, 'assembleAcceptParam']);
$strings[] = implode(';', [$value->typeString] + $params);
}
return implode(', ', $strings);
}
/**
* Assemble and escape the field value parameters based on RFC 2616 section 2.1
*
* @todo someone should review this thoroughly
* @param string $value
* @param string $key
* @return string
*/
protected function assembleAcceptParam(&$value, $key)
{
$separators = ['(', ')', '<', '>', '@', ',', ';', ':',
'/', '[', ']', '?', '=', '{', '}', ' ', "\t"];
$escaped = preg_replace_callback(
'/[[:cntrl:]"\\\\]/', // escape cntrl, ", \
function ($v) {
return '\\' . $v[0];
},
$value
);
if ($escaped == $value && !array_intersect(str_split($value), $separators)) {
$value = $key . '=' . $value;
} else {
$value = $key . '="' . $escaped . '"';
}
return $value;
}
/**
* Add a type, with the given priority
*
* @param string $type
* @param int|float $priority
* @param array (optional) $params
* @throws Exception\InvalidArgumentException
* @return Accept
*/
protected function addType($type, $priority = 1, array $params = [])
{
if (!preg_match($this->regexAddType, $type)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a valid type; received "%s"',
__METHOD__,
(string) $type
));
}
if (!is_int($priority) && !is_float($priority) && !is_numeric($priority)
|| $priority > 1 || $priority < 0
) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a numeric priority; received %s',
__METHOD__,
(string) $priority
));
}
if ($priority != 1) {
$params = ['q' => sprintf('%01.1f', $priority)] + $params;
}
$assembledString = $this->getFieldValue(
[(object) ['typeString' => $type, 'params' => $params]]
);
$value = $this->parseFieldValuePart($assembledString);
$this->addFieldValuePartToQueue($value);
return $this;
}
/**
* Does the header have the requested type?
*
* @param array|string $matchAgainst
* @return bool
*/
protected function hasType($matchAgainst)
{
return (bool) $this->match($matchAgainst);
}
/**
* Match a media string against this header
*
* @param array|string $matchAgainst
* @return Accept\FieldValuePArt\AcceptFieldValuePart|bool The matched value or false
*/
public function match($matchAgainst)
{
if (is_string($matchAgainst)) {
$matchAgainst = $this->getFieldValuePartsFromHeaderLine($matchAgainst);
}
foreach ($this->getPrioritized() as $left) {
foreach ($matchAgainst as $right) {
if ($right->type == '*' || $left->type == '*') {
if ($this->matchAcceptParams($left, $right)) {
$left->setMatchedAgainst($right);
return $left;
}
}
if ($left->type == $right->type) {
if (($left->subtype == $right->subtype || ($right->subtype == '*' || $left->subtype == '*')) &&
($left->format == $right->format || $right->format == '*' || $left->format == '*')
) {
if ($this->matchAcceptParams($left, $right)) {
$left->setMatchedAgainst($right);
return $left;
}
}
}
}
}
return false;
}
/**
* Return a match where all parameters in argument #1 match those in argument #2
*
* @param array $match1
* @param array $match2
* @return bool|array
*/
protected function matchAcceptParams($match1, $match2)
{
foreach ($match2->params as $key => $value) {
if (isset($match1->params[$key])) {
if (strpos($value, '-')) {
preg_match(
'/^(?|([^"-]*)|"([^"]*)")-(?|([^"-]*)|"([^"]*)")\z/',
$value,
$pieces
);
if (count($pieces) == 3 &&
(version_compare($pieces[1], $match1->params[$key], '<=') xor
version_compare($pieces[2], $match1->params[$key], '>=')
)
) {
return false;
}
} elseif (strpos($value, '|')) {
$options = explode('|', $value);
$good = false;
foreach ($options as $option) {
if ($option == $match1->params[$key]) {
$good = true;
break;
}
}
if (!$good) {
return false;
}
} elseif ($match1->params[$key] != $value) {
return false;
}
}
}
return $match1;
}
/**
* Add a key/value combination to the internal queue
*
* @param stdClass $value
* @return number
*/
protected function addFieldValuePartToQueue($value)
{
$this->fieldValueParts[] = $value;
$this->sorted = false;
}
/**
* Sort the internal Field Value Parts
*
* @See rfc2616 sect 14.1
* Media ranges can be overridden by more specific media ranges or
* specific media types. If more than one media range applies to a given
* type, the most specific reference has precedence. For example,
*
* Accept: text/*, text/html, text/html;level=1, * /*
*
* have the following precedence:
*
* 1) text/html;level=1
* 2) text/html
* 3) text/*
* 4) * /*
*
* @return number
*/
protected function sortFieldValueParts()
{
$sort = function ($a, $b) { // If A has higher precedence than B, return -1.
if ($a->priority > $b->priority) {
return -1;
} elseif ($a->priority < $b->priority) {
return 1;
}
// Asterisks
$values = ['type', 'subtype', 'format'];
foreach ($values as $value) {
if ($a->$value == '*' && $b->$value != '*') {
return 1;
} elseif ($b->$value == '*' && $a->$value != '*') {
return -1;
}
}
if ($a->type == 'application' && $b->type != 'application') {
return -1;
} elseif ($b->type == 'application' && $a->type != 'application') {
return 1;
}
//@todo count number of dots in case of type==application in subtype
// So far they're still the same. Longest string length may be more specific
if (strlen($a->raw) == strlen($b->raw)) {
return 0;
}
return (strlen($a->raw) > strlen($b->raw)) ? -1 : 1;
};
usort($this->fieldValueParts, $sort);
$this->sorted = true;
}
/**
* @return array with all the keys, values and parameters this header represents:
*/
public function getPrioritized()
{
if (!$this->sorted) {
$this->sortFieldValueParts();
}
return $this->fieldValueParts;
}
}

View File

@@ -0,0 +1,268 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use DateTime;
use DateTimeZone;
/**
* Abstract Date/Time Header
* Supports headers that have date/time as value
*
* @see Zend\Http\Header\Date
* @see Zend\Http\Header\Expires
* @see Zend\Http\Header\IfModifiedSince
* @see Zend\Http\Header\IfUnmodifiedSince
* @see Zend\Http\Header\LastModified
*
* Note for 'Location' header:
* While RFC 1945 requires an absolute URI, most of the browsers also support relative URI
* This class allows relative URIs, and let user retrieve URI instance if strict validation needed
*/
abstract class AbstractDate implements HeaderInterface
{
/**
* Date formats according to RFC 2616
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
*/
const DATE_RFC1123 = 0;
const DATE_RFC1036 = 1;
const DATE_ANSIC = 2;
/**
* Date instance for this header
*
* @var DateTime
*/
protected $date = null;
/**
* Date output format
*
* @var string
*/
protected static $dateFormat = 'D, d M Y H:i:s \G\M\T';
/**
* Date formats defined by RFC 2616. RFC 1123 date is required
* RFC 1036 and ANSI C formats are provided for compatibility with old servers/clients
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
*
* @var array
*/
protected static $dateFormats = [
self::DATE_RFC1123 => 'D, d M Y H:i:s \G\M\T',
self::DATE_RFC1036 => 'D, d M y H:i:s \G\M\T',
self::DATE_ANSIC => 'D M j H:i:s Y',
];
/**
* Create date-based header from string
*
* @param string $headerLine
* @return AbstractDate
* @throws Exception\InvalidArgumentException
*/
public static function fromString($headerLine)
{
$dateHeader = new static();
list($name, $date) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== strtolower($dateHeader->getFieldName())) {
throw new Exception\InvalidArgumentException(
'Invalid header line for "' . $dateHeader->getFieldName() . '" header string'
);
}
$dateHeader->setDate($date);
return $dateHeader;
}
/**
* Create date-based header from strtotime()-compatible string
*
* @param int|string $time
*
* @return self
*
* @throws Exception\InvalidArgumentException
*/
public static function fromTimeString($time)
{
return static::fromTimestamp(strtotime($time));
}
/**
* Create date-based header from Unix timestamp
*
* @param int $time
*
* @return self
*
* @throws Exception\InvalidArgumentException
*/
public static function fromTimestamp($time)
{
$dateHeader = new static();
if (! $time || ! is_numeric($time)) {
throw new Exception\InvalidArgumentException(
'Invalid time for "' . $dateHeader->getFieldName() . '" header string'
);
}
$dateHeader->setDate(new DateTime('@' . $time));
return $dateHeader;
}
/**
* Set date output format
*
* @param int $format
* @throws Exception\InvalidArgumentException
*/
public static function setDateFormat($format)
{
if (!isset(static::$dateFormats[$format])) {
throw new Exception\InvalidArgumentException(
"No constant defined for provided date format: {$format}"
);
}
static::$dateFormat = static::$dateFormats[$format];
}
/**
* Return current date output format
*
* @return string
*/
public static function getDateFormat()
{
return static::$dateFormat;
}
/**
* Set the date for this header, this can be a string or an instance of \DateTime
*
* @param string|DateTime $date
* @return AbstractDate
* @throws Exception\InvalidArgumentException
*/
public function setDate($date)
{
if (is_string($date)) {
try {
$date = new DateTime($date, new DateTimeZone('GMT'));
} catch (\Exception $e) {
throw new Exception\InvalidArgumentException(
sprintf('Invalid date passed as string (%s)', (string) $date),
$e->getCode(),
$e
);
}
} elseif (!($date instanceof DateTime)) {
throw new Exception\InvalidArgumentException('Date must be an instance of \DateTime or a string');
}
$date->setTimezone(new DateTimeZone('GMT'));
$this->date = $date;
return $this;
}
/**
* Return date for this header
*
* @return string
*/
public function getDate()
{
return $this->date()->format(static::$dateFormat);
}
/**
* Return date for this header as an instance of \DateTime
*
* @return DateTime
*/
public function date()
{
if ($this->date === null) {
$this->date = new DateTime(null, new DateTimeZone('GMT'));
}
return $this->date;
}
/**
* Compare provided date to date for this header
* Returns < 0 if date in header is less than $date; > 0 if it's greater, and 0 if they are equal.
* @see \strcmp()
*
* @param string|DateTime $date
* @return int
* @throws Exception\InvalidArgumentException
*/
public function compareTo($date)
{
if (is_string($date)) {
try {
$date = new DateTime($date, new DateTimeZone('GMT'));
} catch (\Exception $e) {
throw new Exception\InvalidArgumentException(
sprintf('Invalid Date passed as string (%s)', (string) $date),
$e->getCode(),
$e
);
}
} elseif (!($date instanceof DateTime)) {
throw new Exception\InvalidArgumentException('Date must be an instance of \DateTime or a string');
}
$dateTimestamp = $date->getTimestamp();
$thisTimestamp = $this->date()->getTimestamp();
return ($thisTimestamp === $dateTimestamp) ? 0 : (($thisTimestamp > $dateTimestamp) ? 1 : -1);
}
/**
* Get header value as formatted date
*
* @return string
*/
public function getFieldValue()
{
return $this->getDate();
}
/**
* Return header line
*
* @return string
*/
public function toString()
{
return $this->getFieldName() . ': ' . $this->getDate();
}
/**
* Allow casting to string
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
}

View File

@@ -0,0 +1,145 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Uri\Exception as UriException;
use Zend\Uri\UriFactory;
use Zend\Uri\UriInterface;
/**
* Abstract Location Header
* Supports headers that have URI as value
* @see Zend\Http\Header\Location
* @see Zend\Http\Header\ContentLocation
* @see Zend\Http\Header\Referer
*
* Note for 'Location' header:
* While RFC 1945 requires an absolute URI, most of the browsers also support relative URI
* This class allows relative URIs, and let user retrieve URI instance if strict validation needed
*/
abstract class AbstractLocation implements HeaderInterface
{
/**
* URI for this header
*
* @var UriInterface
*/
protected $uri = null;
/**
* Create location-based header from string
*
* @param string $headerLine
* @return AbstractLocation
* @throws Exception\InvalidArgumentException
*/
public static function fromString($headerLine)
{
$locationHeader = new static();
// ZF-5520 - IIS bug, no space after colon
list($name, $uri) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== strtolower($locationHeader->getFieldName())) {
throw new Exception\InvalidArgumentException(
'Invalid header line for "' . $locationHeader->getFieldName() . '" header string'
);
}
HeaderValue::assertValid($uri);
$locationHeader->setUri(trim($uri));
return $locationHeader;
}
/**
* Set the URI/URL for this header, this can be a string or an instance of Zend\Uri\Http
*
* @param string|UriInterface $uri
* @return AbstractLocation
* @throws Exception\InvalidArgumentException
*/
public function setUri($uri)
{
if (is_string($uri)) {
try {
$uri = UriFactory::factory($uri);
} catch (UriException\InvalidUriPartException $e) {
throw new Exception\InvalidArgumentException(
sprintf('Invalid URI passed as string (%s)', (string) $uri),
$e->getCode(),
$e
);
}
} elseif (!($uri instanceof UriInterface)) {
throw new Exception\InvalidArgumentException('URI must be an instance of Zend\Uri\Http or a string');
}
$this->uri = $uri;
return $this;
}
/**
* Return the URI for this header
*
* @return string
*/
public function getUri()
{
if ($this->uri instanceof UriInterface) {
return $this->uri->toString();
}
return $this->uri;
}
/**
* Return the URI for this header as an instance of Zend\Uri\Http
*
* @return UriInterface
*/
public function uri()
{
if ($this->uri === null || is_string($this->uri)) {
$this->uri = UriFactory::factory($this->uri);
}
return $this->uri;
}
/**
* Get header value as URI string
*
* @return string
*/
public function getFieldValue()
{
return $this->getUri();
}
/**
* Output header line
*
* @return string
*/
public function toString()
{
return $this->getFieldName() . ': ' . $this->getUri();
}
/**
* Allow casting to string
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
}

View File

@@ -0,0 +1,119 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Http\Header\Accept\FieldValuePart;
/**
* Accept Header
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
*/
class Accept extends AbstractAccept
{
/**
* @var string
*/
protected $regexAddType = '#^([a-zA-Z+-]+|\*)/(\*|[a-zA-Z0-9+-]+)$#';
/**
* Get field name
*
* @return string
*/
public function getFieldName()
{
return 'Accept';
}
/**
* Cast to string
*
* @return string
*/
public function toString()
{
return 'Accept: ' . $this->getFieldValue();
}
/**
* Add a media type, with the given priority
*
* @param string $type
* @param int|float $priority
* @param array $params
* @return Accept
*/
public function addMediaType($type, $priority = 1, array $params = [])
{
return $this->addType($type, $priority, $params);
}
/**
* Does the header have the requested media type?
*
* @param string $type
* @return bool
*/
public function hasMediaType($type)
{
return $this->hasType($type);
}
/**
* Parse the keys contained in the header line
*
* @param string $fieldValuePart
* @return FieldValuePart\AcceptFieldValuePart
* @see \Zend\Http\Header\AbstractAccept::parseFieldValuePart()
*/
protected function parseFieldValuePart($fieldValuePart)
{
$raw = $fieldValuePart;
if ($pos = strpos($fieldValuePart, '/')) {
$type = trim(substr($fieldValuePart, 0, $pos));
} else {
$type = trim($fieldValuePart);
}
$params = $this->getParametersFromFieldValuePart($fieldValuePart);
if ($pos = strpos($fieldValuePart, ';')) {
$fieldValuePart = trim(substr($fieldValuePart, 0, $pos));
}
if (strpos($fieldValuePart, '/')) {
$subtypeWhole = $format = $subtype = trim(substr($fieldValuePart, strpos($fieldValuePart, '/') + 1));
} else {
$subtypeWhole = '';
$format = '*';
$subtype = '*';
}
$pos = strpos($subtype, '+');
if (false !== $pos) {
$format = trim(substr($subtype, $pos + 1));
$subtype = trim(substr($subtype, 0, $pos));
}
$aggregated = [
'typeString' => trim($fieldValuePart),
'type' => $type,
'subtype' => $subtype,
'subtypeRaw' => $subtypeWhole,
'format' => $format,
'priority' => isset($params['q']) ? $params['q'] : 1,
'params' => $params,
'raw' => trim($raw),
];
return new FieldValuePart\AcceptFieldValuePart((object) $aggregated);
}
}

View File

@@ -0,0 +1,113 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Accept\FieldValuePart;
/**
* Field Value Part
*
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
*/
abstract class AbstractFieldValuePart
{
/**
* Internal object used for value retrieval
* @var object
*/
private $internalValues;
/**
* A Field Value Part this Field Value Part matched against.
* @var AbstractFieldValuePart
*/
protected $matchedAgainst;
/**
*
* @param object $internalValues
*/
public function __construct($internalValues)
{
$this->internalValues = $internalValues;
}
/**
* Set a Field Value Part this Field Value Part matched against.
*
* @param AbstractFieldValuePart $matchedAgainst
* @return AbstractFieldValuePart provides fluent interface
*/
public function setMatchedAgainst(AbstractFieldValuePart $matchedAgainst)
{
$this->matchedAgainst = $matchedAgainst;
return $this;
}
/**
* Get a Field Value Part this Field Value Part matched against.
*
* @return AbstractFieldValuePart|null
*/
public function getMatchedAgainst()
{
return $this->matchedAgainst;
}
/**
*
* @return object
*/
protected function getInternalValues()
{
return $this->internalValues;
}
/**
* @return string $typeString
*/
public function getTypeString()
{
return $this->getInternalValues()->typeString;
}
/**
* @return float $priority
*/
public function getPriority()
{
return (float) $this->getInternalValues()->priority;
}
/**
* @return \stdClass $params
*/
public function getParams()
{
return (object) $this->getInternalValues()->params;
}
/**
* @return string $raw
*/
public function getRaw()
{
return $this->getInternalValues()->raw;
}
/**
*
* @param mixed
* @return mixed
*/
public function __get($key)
{
return $this->getInternalValues()->$key;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Accept\FieldValuePart;
/**
* Field Value Part
*
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
*/
class AcceptFieldValuePart extends AbstractFieldValuePart
{
/**
* @return string
*/
public function getSubtype()
{
return $this->getInternalValues()->subtype;
}
/**
* @return string
*/
public function getSubtypeRaw()
{
return $this->getInternalValues()->subtypeRaw;
}
/**
* @return string
*/
public function getFormat()
{
return $this->getInternalValues()->format;
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Accept\FieldValuePart;
/**
* Field Value Part
*
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
*/
class CharsetFieldValuePart extends AbstractFieldValuePart
{
/**
*
* @return string
*/
public function getCharset()
{
return $this->getInternalValues()->type;
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Accept\FieldValuePart;
/**
* Field Value Part
*
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
*/
class EncodingFieldValuePart extends AbstractFieldValuePart
{
/**
*
* @return string
*/
public function getEncoding()
{
return $this->getInternalValues()->type;
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Accept\FieldValuePart;
/**
* Field Value Part
*
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
*/
class LanguageFieldValuePart extends AbstractFieldValuePart
{
public function getLanguage()
{
return $this->getInternalValues()->typeString;
}
public function getPrimaryTag()
{
return $this->getInternalValues()->type;
}
public function getSubTag()
{
return $this->getInternalValues()->subtype;
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Http\Header\Accept\FieldValuePart;
/**
* Accept Charset Header
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2
*/
class AcceptCharset extends AbstractAccept
{
protected $regexAddType = '#^([a-zA-Z0-9+-]+|\*)$#';
/**
* Get field name
*
* @return string
*/
public function getFieldName()
{
return 'Accept-Charset';
}
/**
* Cast to string
*
* @return string
*/
public function toString()
{
return 'Accept-Charset: ' . $this->getFieldValue();
}
/**
* Add a charset, with the given priority
*
* @param string $type
* @param int|float $priority
* @return Accept
*/
public function addCharset($type, $priority = 1)
{
return $this->addType($type, $priority);
}
/**
* Does the header have the requested charset?
*
* @param string $type
* @return bool
*/
public function hasCharset($type)
{
return $this->hasType($type);
}
/**
* Parse the keys contained in the header line
*
* @param string $fieldValuePart
* @return \Zend\Http\Header\Accept\FieldValuePart\CharsetFieldValuePart
* @see \Zend\Http\Header\AbstractAccept::parseFieldValuePart()
*/
protected function parseFieldValuePart($fieldValuePart)
{
$internalValues = parent::parseFieldValuePart($fieldValuePart);
return new FieldValuePart\CharsetFieldValuePart($internalValues);
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Http\Header\Accept\FieldValuePart;
/**
* Accept Encoding Header
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
*/
class AcceptEncoding extends AbstractAccept
{
protected $regexAddType = '#^([a-zA-Z0-9+-]+|\*)$#';
/**
* Get field name
*
* @return string
*/
public function getFieldName()
{
return 'Accept-Encoding';
}
/**
* Cast to string
*
* @return string
*/
public function toString()
{
return 'Accept-Encoding: ' . $this->getFieldValue();
}
/**
* Add an encoding, with the given priority
*
* @param string $type
* @param int|float $priority
* @return Accept
*/
public function addEncoding($type, $priority = 1)
{
return $this->addType($type, $priority);
}
/**
* Does the header have the requested encoding?
*
* @param string $type
* @return bool
*/
public function hasEncoding($type)
{
return $this->hasType($type);
}
/**
* Parse the keys contained in the header line
*
* @param string $fieldValuePart
* @return \Zend\Http\Header\Accept\FieldValuePart\EncodingFieldValuePart
* @see \Zend\Http\Header\AbstractAccept::parseFieldValuePart()
*/
protected function parseFieldValuePart($fieldValuePart)
{
$internalValues = parent::parseFieldValuePart($fieldValuePart);
return new FieldValuePart\EncodingFieldValuePart($internalValues);
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Http\Header\Accept\FieldValuePart;
/**
* Accept Language Header
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
*/
class AcceptLanguage extends AbstractAccept
{
protected $regexAddType = '#^([a-zA-Z0-9+-]+|\*)$#';
/**
* Get field name
*
* @return string
*/
public function getFieldName()
{
return 'Accept-Language';
}
/**
* Cast to string
*
* @return string
*/
public function toString()
{
return 'Accept-Language: ' . $this->getFieldValue();
}
/**
* Add a language, with the given priority
*
* @param string $type
* @param int|float $priority
* @return Accept
*/
public function addLanguage($type, $priority = 1)
{
return $this->addType($type, $priority);
}
/**
* Does the header have the requested language?
*
* @param string $type
* @return bool
*/
public function hasLanguage($type)
{
return $this->hasType($type);
}
/**
* Parse the keys contained in the header line
*
* @param string $fieldValuePart
* @return \Zend\Http\Header\Accept\FieldValuePart\LanguageFieldValuePart
* @see \Zend\Http\Header\AbstractAccept::parseFieldValuePart()
*/
protected function parseFieldValuePart($fieldValuePart)
{
$raw = $fieldValuePart;
if ($pos = strpos($fieldValuePart, '-')) {
$type = trim(substr($fieldValuePart, 0, $pos));
} else {
$type = trim(substr($fieldValuePart, 0));
}
$params = $this->getParametersFromFieldValuePart($fieldValuePart);
if ($pos = strpos($fieldValuePart, ';')) {
$fieldValuePart = $type = trim(substr($fieldValuePart, 0, $pos));
}
if (strpos($fieldValuePart, '-')) {
$subtypeWhole = $format = $subtype = trim(substr($fieldValuePart, strpos($fieldValuePart, '-')+1));
} else {
$subtypeWhole = '';
$format = '*';
$subtype = '*';
}
$aggregated = [
'typeString' => trim($fieldValuePart),
'type' => $type,
'subtype' => $subtype,
'subtypeRaw' => $subtypeWhole,
'format' => $format,
'priority' => isset($params['q']) ? $params['q'] : 1,
'params' => $params,
'raw' => trim($raw)
];
return new FieldValuePart\LanguageFieldValuePart((object) $aggregated);
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Accept Ranges Header
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.5
*/
class AcceptRanges implements HeaderInterface
{
protected $rangeUnit;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'accept-ranges') {
throw new Exception\InvalidArgumentException(
'Invalid header line for Accept-Ranges string'
);
}
$header = new static($value);
return $header;
}
public function __construct($rangeUnit = null)
{
if ($rangeUnit) {
$this->setRangeUnit($rangeUnit);
}
}
public function getFieldName()
{
return 'Accept-Ranges';
}
public function getFieldValue()
{
return $this->getRangeUnit();
}
public function setRangeUnit($rangeUnit)
{
HeaderValue::assertValid($rangeUnit);
$this->rangeUnit = $rangeUnit;
return $this;
}
public function getRangeUnit()
{
return $this->rangeUnit;
}
public function toString()
{
return 'Accept-Ranges: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Age HTTP Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.6
*/
class Age implements HeaderInterface
{
/**
* Estimate of the amount of time in seconds since the response
*
* @var int
*/
protected $deltaSeconds;
/**
* Create Age header from string
*
* @param string $headerLine
* @return Age
* @throws Exception\InvalidArgumentException
*/
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'age') {
throw new Exception\InvalidArgumentException('Invalid header line for Age string: "' . $name . '"');
}
$header = new static($value);
return $header;
}
public function __construct($deltaSeconds = null)
{
if ($deltaSeconds) {
$this->setDeltaSeconds($deltaSeconds);
}
}
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Age';
}
/**
* Get header value (number of seconds)
*
* @return int
*/
public function getFieldValue()
{
return $this->getDeltaSeconds();
}
/**
* Set number of seconds
*
* @param int $delta
* @return RetryAfter
*/
public function setDeltaSeconds($delta)
{
if (! is_int($delta) && ! is_numeric($delta)) {
throw new Exception\InvalidArgumentException('Invalid delta provided');
}
$this->deltaSeconds = (int) $delta;
return $this;
}
/**
* Get number of seconds
*
* @return int
*/
public function getDeltaSeconds()
{
return $this->deltaSeconds;
}
/**
* Return header line
* In case of overflow RFC states to set value of 2147483648 (2^31)
*
* @return string
*/
public function toString()
{
return 'Age: ' . (($this->deltaSeconds >= PHP_INT_MAX) ? '2147483648' : $this->deltaSeconds);
}
}

View File

@@ -0,0 +1,185 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Http\Request;
/**
* Allow Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7
*/
class Allow implements HeaderInterface
{
/**
* List of request methods
* true states that method is allowed, false - disallowed
* By default GET and POST are allowed
*
* @var array
*/
protected $methods = [
Request::METHOD_OPTIONS => false,
Request::METHOD_GET => true,
Request::METHOD_HEAD => false,
Request::METHOD_POST => true,
Request::METHOD_PUT => false,
Request::METHOD_DELETE => false,
Request::METHOD_TRACE => false,
Request::METHOD_CONNECT => false,
Request::METHOD_PATCH => false,
];
/**
* Create Allow header from header line
*
* @param string $headerLine
* @return Allow
* @throws Exception\InvalidArgumentException
*/
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'allow') {
throw new Exception\InvalidArgumentException('Invalid header line for Allow string: "' . $name . '"');
}
$header = new static();
$header->disallowMethods(array_keys($header->getAllMethods()));
$header->allowMethods(explode(',', $value));
return $header;
}
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Allow';
}
/**
* Get comma-separated list of allowed methods
*
* @return string
*/
public function getFieldValue()
{
return implode(', ', array_keys($this->methods, true, true));
}
/**
* Get list of all defined methods
*
* @return array
*/
public function getAllMethods()
{
return $this->methods;
}
/**
* Get list of allowed methods
*
* @return array
*/
public function getAllowedMethods()
{
return array_keys($this->methods, true, true);
}
/**
* Allow methods or list of methods
*
* @param array|string $allowedMethods
* @return Allow
*/
public function allowMethods($allowedMethods)
{
foreach ((array) $allowedMethods as $method) {
$method = trim(strtoupper($method));
if (preg_match('/\s/', $method)) {
throw new Exception\InvalidArgumentException(sprintf(
'Unable to whitelist method; "%s" is not a valid method',
$method
));
}
$this->methods[$method] = true;
}
return $this;
}
/**
* Disallow methods or list of methods
*
* @param array|string $disallowedMethods
* @return Allow
*/
public function disallowMethods($disallowedMethods)
{
foreach ((array) $disallowedMethods as $method) {
$method = trim(strtoupper($method));
if (preg_match('/\s/', $method)) {
throw new Exception\InvalidArgumentException(sprintf(
'Unable to blacklist method; "%s" is not a valid method',
$method
));
}
$this->methods[$method] = false;
}
return $this;
}
/**
* Convenience alias for @see disallowMethods()
*
* @param array|string $disallowedMethods
* @return Allow
*/
public function denyMethods($disallowedMethods)
{
return $this->disallowMethods($disallowedMethods);
}
/**
* Check whether method is allowed
*
* @param string $method
* @return bool
*/
public function isAllowedMethod($method)
{
$method = trim(strtoupper($method));
// disallow unknown method
if (! isset($this->methods[$method])) {
$this->methods[$method] = false;
}
return $this->methods[$method];
}
/**
* Return header as string
*
* @return string
*/
public function toString()
{
return 'Allow: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.ietf.org/rfc/rfc2617.txt
*/
class AuthenticationInfo implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'authentication-info') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Authentication-Info string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Authentication-Info';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Authentication-Info: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.8
*/
class Authorization implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'authorization') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Authorization string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Authorization';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Authorization: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,252 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
*/
class CacheControl implements HeaderInterface
{
/**
* @var string
*/
protected $value;
/**
* Array of Cache-Control directives
*
* @var array
*/
protected $directives = [];
/**
* Creates a CacheControl object from a headerLine
*
* @param string $headerLine
* @throws Exception\InvalidArgumentException
* @return CacheControl
*/
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'cache-control') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Cache-Control string: ""',
$name
));
}
HeaderValue::assertValid($value);
$directives = static::parseValue($value);
// @todo implementation details
$header = new static();
foreach ($directives as $key => $value) {
$header->addDirective($key, $value);
}
return $header;
}
/**
* Required from HeaderDescription interface
*
* @return string
*/
public function getFieldName()
{
return 'Cache-Control';
}
/**
* Checks if the internal directives array is empty
*
* @return bool
*/
public function isEmpty()
{
return empty($this->directives);
}
/**
* Add a directive
* For directives like 'max-age=60', $value = '60'
* For directives like 'private', use the default $value = true
*
* @param string $key
* @param string|bool $value
* @return CacheControl - provides the fluent interface
*/
public function addDirective($key, $value = true)
{
HeaderValue::assertValid($key);
if (! is_bool($value)) {
HeaderValue::assertValid($value);
}
$this->directives[$key] = $value;
return $this;
}
/**
* Check the internal directives array for a directive
*
* @param string $key
* @return bool
*/
public function hasDirective($key)
{
return array_key_exists($key, $this->directives);
}
/**
* Fetch the value of a directive from the internal directive array
*
* @param string $key
* @return string|null
*/
public function getDirective($key)
{
return array_key_exists($key, $this->directives) ? $this->directives[$key] : null;
}
/**
* Remove a directive
*
* @param string $key
* @return CacheControl - provides the fluent interface
*/
public function removeDirective($key)
{
unset($this->directives[$key]);
return $this;
}
/**
* Assembles the directives into a comma-delimited string
*
* @return string
*/
public function getFieldValue()
{
$parts = [];
ksort($this->directives);
foreach ($this->directives as $key => $value) {
if (true === $value) {
$parts[] = $key;
} else {
if (preg_match('#[^a-zA-Z0-9._-]#', $value)) {
$value = '"' . $value.'"';
}
$parts[] = "$key=$value";
}
}
return implode(', ', $parts);
}
/**
* Returns a string representation of the HTTP Cache-Control header
*
* @return string
*/
public function toString()
{
return 'Cache-Control: ' . $this->getFieldValue();
}
/**
* Internal function for parsing the value part of a
* HTTP Cache-Control header
*
* @param string $value
* @throws Exception\InvalidArgumentException
* @return array
*/
protected static function parseValue($value)
{
$value = trim($value);
$directives = [];
// handle empty string early so we don't need a separate start state
if ($value == '') {
return $directives;
}
$lastMatch = null;
state_directive:
switch (static::match(['[a-zA-Z][a-zA-Z_-]*'], $value, $lastMatch)) {
case 0:
$directive = $lastMatch;
goto state_value;
// intentional fall-through
default:
throw new Exception\InvalidArgumentException('expected DIRECTIVE');
}
state_value:
switch (static::match(['="[^"]*"', '=[^",\s;]*'], $value, $lastMatch)) {
case 0:
$directives[$directive] = substr($lastMatch, 2, -1);
goto state_separator;
// intentional fall-through
case 1:
$directives[$directive] = rtrim(substr($lastMatch, 1));
goto state_separator;
// intentional fall-through
default:
$directives[$directive] = true;
goto state_separator;
}
state_separator:
switch (static::match(['\s*,\s*', '$'], $value, $lastMatch)) {
case 0:
goto state_directive;
// intentional fall-through
case 1:
return $directives;
default:
throw new Exception\InvalidArgumentException('expected SEPARATOR or END');
}
}
/**
* Internal function used by parseValue to match tokens
*
* @param array $tokens
* @param string $string
* @param string $lastMatch
* @return int
*/
protected static function match($tokens, &$string, &$lastMatch)
{
// Ensure we have a string
$value = (string) $string;
foreach ($tokens as $i => $token) {
if (preg_match('/^' . $token . '/', $value, $matches)) {
$lastMatch = $matches[0];
$string = substr($value, strlen($matches[0]));
return $i;
}
}
return -1;
}
}

View File

@@ -0,0 +1,117 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Connection Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.10
*/
class Connection implements HeaderInterface
{
const CONNECTION_CLOSE = 'close';
const CONNECTION_KEEP_ALIVE = 'keep-alive';
/**
* Value of this header
*
* @var string
*/
protected $value = self::CONNECTION_KEEP_ALIVE;
/**
* @param $headerLine
* @return Connection
* @throws Exception\InvalidArgumentException
*/
public static function fromString($headerLine)
{
$header = new static();
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'connection') {
throw new Exception\InvalidArgumentException('Invalid header line for Connection string: "' . $name . '"');
}
$header->setValue(trim($value));
return $header;
}
/**
* Set Connection header to define persistent connection
*
* @param bool $flag
* @return Connection
*/
public function setPersistent($flag)
{
$this->value = (bool) $flag
? self::CONNECTION_KEEP_ALIVE
: self::CONNECTION_CLOSE;
return $this;
}
/**
* Get whether this connection is persistent
*
* @return bool
*/
public function isPersistent()
{
return ($this->value === self::CONNECTION_KEEP_ALIVE);
}
/**
* Set arbitrary header value
* RFC allows any token as value, 'close' and 'keep-alive' are commonly used
*
* @param string $value
* @return Connection
*/
public function setValue($value)
{
HeaderValue::assertValid($value);
$this->value = strtolower($value);
return $this;
}
/**
* Connection header name
*
* @return string
*/
public function getFieldName()
{
return 'Connection';
}
/**
* Connection header value
*
* @return string
*/
public function getFieldValue()
{
return $this->value;
}
/**
* Return header line
*
* @return string
*/
public function toString()
{
return 'Connection: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1
*/
class ContentDisposition implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-disposition') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Content-Disposition string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Content-Disposition';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Content-Disposition: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
*/
class ContentEncoding implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-encoding') {
throw new Exception\InvalidArgumentException(
'Invalid header line for Content-Encoding string: "' . $name . '"'
);
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Content-Encoding';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Content-Encoding: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.12
*/
class ContentLanguage implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-language') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Content-Language string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Content-Language';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Content-Language: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13
*/
class ContentLength implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-length') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Content-Length string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Content-Length';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Content-Length: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Content-Location Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.14
*/
class ContentLocation extends AbstractLocation
{
/**
* Return header name
*
* @return string
*/
public function getFieldName()
{
return 'Content-Location';
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.15
*/
class ContentMD5 implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-md5') {
throw new Exception\InvalidArgumentException('Invalid header line for Content-MD5 string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Content-MD5';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Content-MD5: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16
*/
class ContentRange implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-range') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Content-Range string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Content-Range';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Content-Range: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,153 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Content Security Policy 1.0 Header
*
* @link http://www.w3.org/TR/CSP/
*/
class ContentSecurityPolicy implements HeaderInterface
{
/**
* Valid directive names
*
* @var array
*/
protected $validDirectiveNames = [
// As per http://www.w3.org/TR/CSP/#directives
'default-src',
'script-src',
'object-src',
'style-src',
'img-src',
'media-src',
'frame-src',
'font-src',
'connect-src',
'sandbox',
'report-uri',
];
/**
* The directives defined for this policy
*
* @var array
*/
protected $directives = [];
/**
* Get the list of defined directives
*
* @return array
*/
public function getDirectives()
{
return $this->directives;
}
/**
* Sets the directive to consist of the source list
*
* Reverses http://www.w3.org/TR/CSP/#parsing-1
*
* @param string $name The directive name.
* @param array $sources The source list.
* @return self
* @throws Exception\InvalidArgumentException If the name is not a valid directive name.
*/
public function setDirective($name, array $sources)
{
if (! in_array($name, $this->validDirectiveNames, true)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a valid directive name; received "%s"',
__METHOD__,
(string) $name
));
}
if (empty($sources)) {
$this->directives[$name] = "'none'";
return $this;
}
array_walk($sources, [__NAMESPACE__ . '\HeaderValue', 'assertValid']);
$this->directives[$name] = implode(' ', $sources);
return $this;
}
/**
* Create Content Security Policy header from a given header line
*
* @param string $headerLine The header line to parse.
* @return self
* @throws Exception\InvalidArgumentException If the name field in the given header line does not match.
*/
public static function fromString($headerLine)
{
$header = new static();
$headerName = $header->getFieldName();
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// Ensure the proper header name
if (strcasecmp($name, $headerName) != 0) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for %s string: "%s"',
$headerName,
$name
));
}
// As per http://www.w3.org/TR/CSP/#parsing
$tokens = explode(';', $value);
foreach ($tokens as $token) {
$token = trim($token);
if ($token) {
list($directiveName, $directiveValue) = explode(' ', $token, 2);
if (!isset($header->directives[$directiveName])) {
$header->setDirective($directiveName, [$directiveValue]);
}
}
}
return $header;
}
/**
* Get the header name
*
* @return string
*/
public function getFieldName()
{
return 'Content-Security-Policy';
}
/**
* Get the header value
*
* @return string
*/
public function getFieldValue()
{
$directives = [];
foreach ($this->directives as $name => $value) {
$directives[] = sprintf('%s %s;', $name, $value);
}
return implode(' ', $directives);
}
/**
* Return the header as a string
*
* @return string
*/
public function toString()
{
return sprintf('%s: %s', $this->getFieldName(), $this->getFieldValue());
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11 @todo find section
*/
class ContentTransferEncoding implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-transfer-encoding') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Content-Transfer-Encoding string: "%s"',
$name
));
}
// @todo implementation details
$header = new static(strtolower($value));
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Content-Transfer-Encoding';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Content-Transfer-Encoding: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,404 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use stdClass;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17
*/
class ContentType implements HeaderInterface
{
/**
* @var string
*/
protected $mediaType;
/**
* @var array
*/
protected $parameters = [];
/**
* @var string
*/
protected $value;
/**
* Factory method: create an object from a string representation
*
* @param string $headerLine
* @return self
*/
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-type') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Content-Type string: "%s"',
$name
));
}
$parts = explode(';', $value);
$mediaType = array_shift($parts);
$header = new static($value, trim($mediaType));
if (count($parts) > 0) {
$parameters = [];
foreach ($parts as $parameter) {
$parameter = trim($parameter);
if (!preg_match('/^(?P<key>[^\s\=]+)\="?(?P<value>[^\s\"]*)"?$/', $parameter, $matches)) {
continue;
}
$parameters[$matches['key']] = $matches['value'];
}
$header->setParameters($parameters);
}
return $header;
}
public function __construct($value = null, $mediaType = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
$this->mediaType = $mediaType;
}
/**
* Determine if the mediatype value in this header matches the provided criteria
*
* @param array|string $matchAgainst
* @return string|bool Matched value or false
*/
public function match($matchAgainst)
{
if (is_string($matchAgainst)) {
$matchAgainst = $this->splitMediaTypesFromString($matchAgainst);
}
$mediaType = $this->getMediaType();
$left = $this->getMediaTypeObjectFromString($mediaType);
foreach ($matchAgainst as $matchType) {
$matchType = strtolower($matchType);
if ($mediaType == $matchType) {
return $matchType;
}
$right = $this->getMediaTypeObjectFromString($matchType);
// Is the right side a wildcard type?
if ($right->type == '*') {
if ($this->validateSubtype($right, $left)) {
return $matchType;
}
}
// Do the types match?
if ($right->type == $left->type) {
if ($this->validateSubtype($right, $left)) {
return $matchType;
}
}
}
return false;
}
/**
* Create a string representation of the header
*
* @return string
*/
public function toString()
{
return 'Content-Type: ' . $this->getFieldValue();
}
/**
* Get the field name
*
* @return string
*/
public function getFieldName()
{
return 'Content-Type';
}
/**
* Get the field value
*
* @return string
*/
public function getFieldValue()
{
if (null !== $this->value) {
return $this->value;
}
return $this->assembleValue();
}
/**
* Set the media type
*
* @param string $mediaType
* @return self
*/
public function setMediaType($mediaType)
{
HeaderValue::assertValid($mediaType);
$this->mediaType = strtolower($mediaType);
$this->value = null;
return $this;
}
/**
* Get the media type
*
* @return string
*/
public function getMediaType()
{
return $this->mediaType;
}
/**
* Set additional content-type parameters
*
* @param array $parameters
* @return self
*/
public function setParameters(array $parameters)
{
foreach ($parameters as $key => $value) {
HeaderValue::assertValid($key);
HeaderValue::assertValid($value);
}
$this->parameters = array_merge($this->parameters, $parameters);
$this->value = null;
return $this;
}
/**
* Get any additional content-type parameters currently set
*
* @return array
*/
public function getParameters()
{
return $this->parameters;
}
/**
* Set the content-type character set encoding
*
* @param string $charset
* @return self
*/
public function setCharset($charset)
{
HeaderValue::assertValid($charset);
$this->parameters['charset'] = $charset;
$this->value = null;
return $this;
}
/**
* Get the content-type character set encoding, if any
*
* @return null|string
*/
public function getCharset()
{
if (isset($this->parameters['charset'])) {
return $this->parameters['charset'];
}
return;
}
/**
* Assemble the value based on the media type and any available parameters
*
* @return string
*/
protected function assembleValue()
{
$mediaType = $this->getMediaType();
if (empty($this->parameters)) {
return $mediaType;
}
$parameters = [];
foreach ($this->parameters as $key => $value) {
$parameters[] = sprintf('%s=%s', $key, $value);
}
return sprintf('%s; %s', $mediaType, implode('; ', $parameters));
}
/**
* Split comma-separated media types into an array
*
* @param string $criteria
* @return array
*/
protected function splitMediaTypesFromString($criteria)
{
$mediaTypes = explode(',', $criteria);
array_walk(
$mediaTypes,
function (&$value) {
$value = trim($value);
}
);
return $mediaTypes;
}
/**
* Split a mediatype string into an object with the following parts:
*
* - type
* - subtype
* - format
*
* @param string $string
* @return stdClass
*/
protected function getMediaTypeObjectFromString($string)
{
if (!is_string($string)) {
throw new Exception\InvalidArgumentException(sprintf(
'Non-string mediatype "%s" provided',
(is_object($string) ? get_class($string) : gettype($string))
));
}
$parts = explode('/', $string, 2);
if (1 == count($parts)) {
throw new Exception\DomainException(sprintf(
'Invalid mediatype "%s" provided',
$string
));
}
$type = array_shift($parts);
$subtype = array_shift($parts);
$format = $subtype;
if (strstr($subtype, '+')) {
$parts = explode('+', $subtype, 2);
$subtype = array_shift($parts);
$format = array_shift($parts);
}
$mediaType = (object) [
'type' => $type,
'subtype' => $subtype,
'format' => $format,
];
return $mediaType;
}
/**
* Validate a subtype
*
* @param stdClass $right
* @param stdClass $left
* @return bool
*/
protected function validateSubtype($right, $left)
{
// Is the right side a wildcard subtype?
if ($right->subtype == '*') {
return $this->validateFormat($right, $left);
}
// Do the right side and left side subtypes match?
if ($right->subtype == $left->subtype) {
return $this->validateFormat($right, $left);
}
// Is the right side a partial wildcard?
if ('*' == substr($right->subtype, -1)) {
// validate partial-wildcard subtype
if (!$this->validatePartialWildcard($right->subtype, $left->subtype)) {
return false;
}
// Finally, verify format is valid
return $this->validateFormat($right, $left);
}
// Does the right side subtype match the left side format?
if ($right->subtype == $left->format) {
return true;
}
// At this point, there is no valid match
return false;
}
/**
* Validate the format
*
* Validate that the right side format matches what the left side defines.
*
* @param string $right
* @param string $left
* @return bool
*/
protected function validateFormat($right, $left)
{
if ($right->format && $left->format) {
if ($right->format == '*') {
return true;
}
if ($right->format == $left->format) {
return true;
}
return false;
}
return true;
}
/**
* Validate a partial wildcard (i.e., string ending in '*')
*
* @param string $right
* @param string $left
* @return bool
*/
protected function validatePartialWildcard($right, $left)
{
$requiredSegment = substr($right, 0, strlen($right) - 1);
if ($requiredSegment == $left) {
return true;
}
if (strlen($requiredSegment) >= strlen($left)) {
return false;
}
if (0 === strpos($left, $requiredSegment)) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,122 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use ArrayObject;
/**
* @see http://www.ietf.org/rfc/rfc2109.txt
* @see http://www.w3.org/Protocols/rfc2109/rfc2109
*/
class Cookie extends ArrayObject implements HeaderInterface
{
protected $encodeValue = true;
public static function fromSetCookieArray(array $setCookies)
{
$nvPairs = [];
foreach ($setCookies as $setCookie) {
if (! $setCookie instanceof SetCookie) {
throw new Exception\InvalidArgumentException(sprintf(
'%s requires an array of SetCookie objects',
__METHOD__
));
}
if (array_key_exists($setCookie->getName(), $nvPairs)) {
throw new Exception\InvalidArgumentException(sprintf(
'Two cookies with the same name were provided to %s',
__METHOD__
));
}
$nvPairs[$setCookie->getName()] = $setCookie->getValue();
}
return new static($nvPairs);
}
public static function fromString($headerLine)
{
$header = new static();
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'cookie') {
throw new Exception\InvalidArgumentException('Invalid header line for Server string: "' . $name . '"');
}
$nvPairs = preg_split('#;\s*#', $value);
$arrayInfo = [];
foreach ($nvPairs as $nvPair) {
$parts = explode('=', $nvPair, 2);
if (count($parts) != 2) {
throw new Exception\RuntimeException('Malformed Cookie header found');
}
list($name, $value) = $parts;
$arrayInfo[$name] = urldecode($value);
}
$header->exchangeArray($arrayInfo);
return $header;
}
public function __construct(array $array = [])
{
parent::__construct($array, ArrayObject::ARRAY_AS_PROPS);
}
public function setEncodeValue($encodeValue)
{
$this->encodeValue = (bool) $encodeValue;
return $this;
}
public function getEncodeValue()
{
return $this->encodeValue;
}
public function getFieldName()
{
return 'Cookie';
}
public function getFieldValue()
{
$nvPairs = [];
foreach ($this as $name => $value) {
$nvPairs[] = $name . '=' . (($this->encodeValue) ? urlencode($value) : $value);
}
return implode('; ', $nvPairs);
}
public function toString()
{
return 'Cookie: ' . $this->getFieldValue();
}
/**
* Get the cookie as a string, suitable for sending as a "Cookie" header in an
* HTTP request
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Date Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18
*/
class Date extends AbstractDate
{
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Date';
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19
*/
class Etag implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'etag') {
throw new Exception\InvalidArgumentException('Invalid header line for Etag string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Etag';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Etag: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Exception;
class DomainException extends \DomainException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Exception;
use Zend\Http\Exception\ExceptionInterface as HttpException;
interface ExceptionInterface extends HttpException
{
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Exception;
use Zend\Http\Exception;
class InvalidArgumentException extends Exception\InvalidArgumentException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Exception;
use Zend\Http\Exception;
class RuntimeException extends Exception\RuntimeException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.20
*/
class Expect implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'expect') {
throw new Exception\InvalidArgumentException('Invalid header line for Expect string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Expect';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Expect: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Expires Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21
*/
class Expires extends AbstractDate
{
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Expires';
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.22
*/
class From implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'from') {
throw new Exception\InvalidArgumentException('Invalid header line for From string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'From';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'From: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,164 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Content-Location Header
*
*/
class GenericHeader implements HeaderInterface
{
/**
* @var string
*/
protected $fieldName = null;
/**
* @var string
*/
protected $fieldValue = null;
/**
* Factory to generate a header object from a string
*
* @static
* @param string $headerLine
* @return GenericHeader
*/
public static function fromString($headerLine)
{
list($fieldName, $fieldValue) = GenericHeader::splitHeaderLine($headerLine);
$header = new static($fieldName, $fieldValue);
return $header;
}
/**
* Splits the header line in `name` and `value` parts.
*
* @param string $headerLine
* @return string[] `name` in the first index and `value` in the second.
* @throws Exception\InvalidArgumentException If header does not match with the format ``name:value``
*/
public static function splitHeaderLine($headerLine)
{
$parts = explode(':', $headerLine, 2);
if (count($parts) !== 2) {
throw new Exception\InvalidArgumentException('Header must match with the format "name:value"');
}
if (! HeaderValue::isValid($parts[1])) {
throw new Exception\InvalidArgumentException('Invalid header value detected');
}
$parts[1] = ltrim($parts[1]);
return $parts;
}
/**
* Constructor
*
* @param null|string $fieldName
* @param null|string $fieldValue
*/
public function __construct($fieldName = null, $fieldValue = null)
{
if ($fieldName) {
$this->setFieldName($fieldName);
}
if ($fieldValue !== null) {
$this->setFieldValue($fieldValue);
}
}
/**
* Set header field name
*
* @param string $fieldName
* @return GenericHeader
* @throws Exception\InvalidArgumentException If the name does not match with RFC 2616 format.
*/
public function setFieldName($fieldName)
{
if (!is_string($fieldName) || empty($fieldName)) {
throw new Exception\InvalidArgumentException('Header name must be a string');
}
/*
* Following RFC 7230 section 3.2
*
* header-field = field-name ":" [ field-value ]
* field-name = token
* token = 1*tchar
* tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
* "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
*/
if (!preg_match('/^[!#$%&\'*+\-\.\^_`|~0-9a-zA-Z]+$/', $fieldName)) {
throw new Exception\InvalidArgumentException(
'Header name must be a valid RFC 7230 (section 3.2) field-name.'
);
}
$this->fieldName = $fieldName;
return $this;
}
/**
* Retrieve header field name
*
* @return string
*/
public function getFieldName()
{
return $this->fieldName;
}
/**
* Set header field value
*
* @param string $fieldValue
* @return GenericHeader
*/
public function setFieldValue($fieldValue)
{
$fieldValue = (string) $fieldValue;
HeaderValue::assertValid($fieldValue);
if (preg_match('/^\s+$/', $fieldValue)) {
$fieldValue = '';
}
$this->fieldValue = $fieldValue;
return $this;
}
/**
* Retrieve header field value
*
* @return string
*/
public function getFieldValue()
{
return $this->fieldValue;
}
/**
* Cast to string as a well formed HTTP header line
*
* Returns in form of "NAME: VALUE\r\n"
*
* @return string
*/
public function toString()
{
return $this->getFieldName() . ': ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
class GenericMultiHeader extends GenericHeader implements MultipleHeaderInterface
{
public static function fromString($headerLine)
{
list($fieldName, $fieldValue) = GenericHeader::splitHeaderLine($headerLine);
if (strpos($fieldValue, ',')) {
$headers = [];
foreach (explode(',', $fieldValue) as $multiValue) {
$headers[] = new static($fieldName, $multiValue);
}
return $headers;
} else {
$header = new static($fieldName, $fieldValue);
return $header;
}
}
public function toStringMultipleHeaders(array $headers)
{
$name = $this->getFieldName();
$values = [$this->getFieldValue()];
foreach ($headers as $header) {
if (!$header instanceof static) {
throw new Exception\InvalidArgumentException('This method toStringMultipleHeaders was expecting an array of headers of the same type');
}
$values[] = $header->getFieldValue();
}
return $name . ': ' . implode(',', $values) . "\r\n";
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Interface for HTTP Header classes.
*/
interface HeaderInterface
{
/**
* Factory to generate a header object from a string
*
* @param string $headerLine
* @return self
* @throws Exception\InvalidArgumentException If the header does not match RFC 2616 definition.
* @see http://tools.ietf.org/html/rfc2616#section-4.2
*/
public static function fromString($headerLine);
/**
* Retrieve header name
*
* @return string
*/
public function getFieldName();
/**
* Retrieve header value
*
* @return string
*/
public function getFieldValue();
/**
* Cast to string
*
* Returns in form of "NAME: VALUE"
*
* @return string
*/
public function toString();
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
final class HeaderValue
{
/**
* Private constructor; non-instantiable.
*/
private function __construct()
{
}
/**
* Filter a header value
*
* Ensures CRLF header injection vectors are filtered.
*
* Per RFC 7230, only VISIBLE ASCII characters, spaces, and horizontal
* tabs are allowed in values; only one whitespace character is allowed
* between visible characters.
*
* @see http://en.wikipedia.org/wiki/HTTP_response_splitting
* @param string $value
* @return string
*/
public static function filter($value)
{
$value = (string) $value;
$length = strlen($value);
$string = '';
for ($i = 0; $i < $length; $i += 1) {
$ascii = ord($value[$i]);
// Non-visible, non-whitespace characters
// 9 === horizontal tab
// 32-126, 128-254 === visible
// 127 === DEL
// 255 === null byte
if (($ascii < 32 && $ascii !== 9)
|| $ascii === 127
|| $ascii > 254
) {
continue;
}
$string .= $value[$i];
}
return $string;
}
/**
* Validate a header value.
*
* Per RFC 7230, only VISIBLE ASCII characters, spaces, and horizontal
* tabs are allowed in values; only one whitespace character is allowed
* between visible characters.
*
* @see http://en.wikipedia.org/wiki/HTTP_response_splitting
* @param string $value
* @return bool
*/
public static function isValid($value)
{
$value = (string) $value;
$length = strlen($value);
for ($i = 0; $i < $length; $i += 1) {
$ascii = ord($value[$i]);
// Non-visible, non-whitespace characters
// 9 === horizontal tab
// 32-126, 128-254 === visible
// 127 === DEL
// 255 === null byte
if (($ascii < 32 && $ascii !== 9)
|| $ascii === 127
|| $ascii > 254
) {
return false;
}
}
return true;
}
/**
* Assert a header value is valid.
*
* @param string $value
* @throws Exception\RuntimeException for invalid values
* @return void
*/
public static function assertValid($value)
{
if (! self::isValid($value)) {
throw new Exception\InvalidArgumentException('Invalid header value');
}
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.23
*/
class Host implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'host') {
throw new Exception\InvalidArgumentException('Invalid header line for Host string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Host';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Host: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.24
*/
class IfMatch implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'if-match') {
throw new Exception\InvalidArgumentException('Invalid header line for If-Match string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'If-Match';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'If-Match: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* If-Modified-Since Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25
*/
class IfModifiedSince extends AbstractDate
{
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'If-Modified-Since';
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
*/
class IfNoneMatch implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'if-none-match') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for If-None-Match string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'If-None-Match';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'If-None-Match: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.27
*/
class IfRange implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'if-range') {
throw new Exception\InvalidArgumentException('Invalid header line for If-Range string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'If-Range';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'If-Range: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* If-Unmodified-Since Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.28
*/
class IfUnmodifiedSince extends AbstractDate
{
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'If-Unmodified-Since';
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @todo Search for RFC for this header
*/
class KeepAlive implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'keep-alive') {
throw new Exception\InvalidArgumentException('Invalid header line for Keep-Alive string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Keep-Alive';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Keep-Alive: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Last-Modified Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.29
*/
class LastModified extends AbstractDate
{
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Last-Modified';
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Location Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.30
*/
class Location extends AbstractLocation
{
/**
* Return header name
*
* @return string
*/
public function getFieldName()
{
return 'Location';
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.31
*/
class MaxForwards implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'max-forwards') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Max-Forwards string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Max-Forwards';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Max-Forwards: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
interface MultipleHeaderInterface extends HeaderInterface
{
public function toStringMultipleHeaders(array $headers);
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Uri\UriFactory;
/**
* @throws Exception\InvalidArgumentException
* @see http://tools.ietf.org/id/draft-abarth-origin-03.html#rfc.section.2
*/
class Origin implements HeaderInterface
{
/**
* @var string
*/
protected $value = '';
public static function fromString($headerLine)
{
list($name, $value) = explode(': ', $headerLine, 2);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'origin') {
throw new Exception\InvalidArgumentException('Invalid header line for Origin string: "' . $name . '"');
}
$uri = UriFactory::factory($value);
if (!$uri->isValid()) {
throw new Exception\InvalidArgumentException('Invalid header value for Origin key: "' . $name . '"');
}
return new static($value);
}
/**
* @param string|null $value
*/
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Origin';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Origin: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32
*/
class Pragma implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'pragma') {
throw new Exception\InvalidArgumentException('Invalid header line for Pragma string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Pragma';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Pragma: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,78 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.33
*/
class ProxyAuthenticate implements MultipleHeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'proxy-authenticate') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Proxy-Authenticate string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Proxy-Authenticate';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Proxy-Authenticate: ' . $this->getFieldValue();
}
public function toStringMultipleHeaders(array $headers)
{
$strings = [$this->toString()];
foreach ($headers as $header) {
if (!$header instanceof ProxyAuthenticate) {
throw new Exception\RuntimeException(
'The ProxyAuthenticate multiple header implementation can only accept'
. ' an array of ProxyAuthenticate headers'
);
}
$strings[] = $header->toString();
}
return implode("\r\n", $strings);
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.34
*/
class ProxyAuthorization implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'proxy-authorization') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Proxy-Authorization string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Proxy-Authorization';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Proxy-Authorization: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.2
*/
class Range implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'range') {
throw new Exception\InvalidArgumentException('Invalid header line for Range string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Range';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Range: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Uri\Http as HttpUri;
/**
* Content-Location Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36
*/
class Referer extends AbstractLocation
{
/**
* Set the URI/URL for this header
* according to RFC Referer URI should not have fragment
*
* @param string|HttpUri $uri
* @return Referer
*/
public function setUri($uri)
{
parent::setUri($uri);
$this->uri->setFragment(null);
return $this;
}
/**
* Return header name
*
* @return string
*/
public function getFieldName()
{
return 'Referer';
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @todo FIND SPEC FOR THIS
*/
class Refresh implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'refresh') {
throw new Exception\InvalidArgumentException('Invalid header line for Refresh string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Refresh';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Refresh: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Retry-After HTTP Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.37
*/
class RetryAfter extends AbstractDate
{
/**
* Value of header in delta-seconds
* By default set to 1 hour
*
* @var int
*/
protected $deltaSeconds = 3600;
/**
* Create Retry-After header from string
*
* @param string $headerLine
* @return RetryAfter
* @throws Exception\InvalidArgumentException
*/
public static function fromString($headerLine)
{
$dateHeader = new static();
list($name, $date) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== strtolower($dateHeader->getFieldName())) {
throw new Exception\InvalidArgumentException(
'Invalid header line for "' . $dateHeader->getFieldName() . '" header string'
);
}
if (is_numeric($date)) {
$dateHeader->setDeltaSeconds($date);
} else {
$dateHeader->setDate($date);
}
return $dateHeader;
}
/**
* Set number of seconds
*
* @param int $delta
* @return RetryAfter
*/
public function setDeltaSeconds($delta)
{
$this->deltaSeconds = (int) $delta;
return $this;
}
/**
* Get number of seconds
*
* @return int
*/
public function getDeltaSeconds()
{
return $this->deltaSeconds;
}
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Retry-After';
}
/**
* Returns date if it's set, or number of seconds
*
* @return int|string
*/
public function getFieldValue()
{
return ($this->date === null) ? $this->deltaSeconds : $this->getDate();
}
/**
* Return header line
*
* @return string
*/
public function toString()
{
return 'Retry-After: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.38
*/
class Server implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'server') {
throw new Exception\InvalidArgumentException('Invalid header line for Server string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Server';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Server: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,674 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use DateTime;
use Zend\Uri\UriFactory;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.ietf.org/rfc/rfc2109.txt
* @see http://www.w3.org/Protocols/rfc2109/rfc2109
*/
class SetCookie implements MultipleHeaderInterface
{
/**
* Cookie name
*
* @var string|null
*/
protected $name = null;
/**
* Cookie value
*
* @var string|null
*/
protected $value = null;
/**
* Version
*
* @var int|null
*/
protected $version = null;
/**
* Max Age
*
* @var int|null
*/
protected $maxAge = null;
/**
* Cookie expiry date
*
* @var int|null
*/
protected $expires = null;
/**
* Cookie domain
*
* @var string|null
*/
protected $domain = null;
/**
* Cookie path
*
* @var string|null
*/
protected $path = null;
/**
* Whether the cookie is secure or not
*
* @var bool|null
*/
protected $secure = null;
/**
* If the value need to be quoted or not
*
* @var bool
*/
protected $quoteFieldValue = false;
/**
* @var bool|null
*/
protected $httponly = null;
/**
* @static
* @throws Exception\InvalidArgumentException
* @param $headerLine
* @param bool $bypassHeaderFieldName
* @return array|SetCookie
*/
public static function fromString($headerLine, $bypassHeaderFieldName = false)
{
static $setCookieProcessor = null;
if ($setCookieProcessor === null) {
$setCookieClass = get_called_class();
$setCookieProcessor = function ($headerLine) use ($setCookieClass) {
$header = new $setCookieClass;
$keyValuePairs = preg_split('#;\s*#', $headerLine);
foreach ($keyValuePairs as $keyValue) {
if (preg_match('#^(?P<headerKey>[^=]+)=\s*("?)(?P<headerValue>[^"]*)\2#', $keyValue, $matches)) {
$headerKey = $matches['headerKey'];
$headerValue= $matches['headerValue'];
} else {
$headerKey = $keyValue;
$headerValue = null;
}
// First K=V pair is always the cookie name and value
if ($header->getName() === null) {
$header->setName($headerKey);
$header->setValue(urldecode($headerValue));
continue;
}
// Process the remaining elements
switch (str_replace(['-', '_'], '', strtolower($headerKey))) {
case 'expires':
$header->setExpires($headerValue);
break;
case 'domain':
$header->setDomain($headerValue);
break;
case 'path':
$header->setPath($headerValue);
break;
case 'secure':
$header->setSecure(true);
break;
case 'httponly':
$header->setHttponly(true);
break;
case 'version':
$header->setVersion((int) $headerValue);
break;
case 'maxage':
$header->setMaxAge((int) $headerValue);
break;
default:
// Intentionally omitted
}
}
return $header;
};
}
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
HeaderValue::assertValid($value);
// some sites return set-cookie::value, this is to get rid of the second :
$name = (strtolower($name) =='set-cookie:') ? 'set-cookie' : $name;
// check to ensure proper header type for this factory
if (strtolower($name) !== 'set-cookie') {
throw new Exception\InvalidArgumentException('Invalid header line for Set-Cookie string: "' . $name . '"');
}
$multipleHeaders = preg_split('#(?<!Sun|Mon|Tue|Wed|Thu|Fri|Sat),\s*#', $value);
if (count($multipleHeaders) <= 1) {
return $setCookieProcessor(array_pop($multipleHeaders));
} else {
$headers = [];
foreach ($multipleHeaders as $headerLine) {
$headers[] = $setCookieProcessor($headerLine);
}
return $headers;
}
}
/**
* Cookie object constructor
*
* @todo Add validation of each one of the parameters (legal domain, etc.)
*
* @param string $name
* @param string $value
* @param int|string|DateTime $expires
* @param string $path
* @param string $domain
* @param bool $secure
* @param bool $httponly
* @param string $maxAge
* @param int $version
*/
public function __construct(
$name = null,
$value = null,
$expires = null,
$path = null,
$domain = null,
$secure = false,
$httponly = false,
$maxAge = null,
$version = null
) {
$this->type = 'Cookie';
$this->setName($name)
->setValue($value)
->setVersion($version)
->setMaxAge($maxAge)
->setDomain($domain)
->setExpires($expires)
->setPath($path)
->setSecure($secure)
->setHttpOnly($httponly);
}
/**
* @return string 'Set-Cookie'
*/
public function getFieldName()
{
return 'Set-Cookie';
}
/**
* @throws Exception\RuntimeException
* @return string
*/
public function getFieldValue()
{
if ($this->getName() == '') {
return '';
}
$value = urlencode($this->getValue());
if ($this->hasQuoteFieldValue()) {
$value = '"'. $value . '"';
}
$fieldValue = $this->getName() . '=' . $value;
$version = $this->getVersion();
if ($version !== null) {
$fieldValue .= '; Version=' . $version;
}
$maxAge = $this->getMaxAge();
if ($maxAge!==null) {
$fieldValue .= '; Max-Age=' . $maxAge;
}
$expires = $this->getExpires();
if ($expires) {
$fieldValue .= '; Expires=' . $expires;
}
$domain = $this->getDomain();
if ($domain) {
$fieldValue .= '; Domain=' . $domain;
}
$path = $this->getPath();
if ($path) {
$fieldValue .= '; Path=' . $path;
}
if ($this->isSecure()) {
$fieldValue .= '; Secure';
}
if ($this->isHttponly()) {
$fieldValue .= '; HttpOnly';
}
return $fieldValue;
}
/**
* @param string $name
* @throws Exception\InvalidArgumentException
* @return SetCookie
*/
public function setName($name)
{
HeaderValue::assertValid($name);
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $value
* @return SetCookie
*/
public function setValue($value)
{
$this->value = $value;
return $this;
}
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* Set version
*
* @param int $version
* @throws Exception\InvalidArgumentException
* @return SetCookie
*/
public function setVersion($version)
{
if ($version !== null && !is_int($version)) {
throw new Exception\InvalidArgumentException('Invalid Version number specified');
}
$this->version = $version;
return $this;
}
/**
* Get version
*
* @return int
*/
public function getVersion()
{
return $this->version;
}
/**
* Set Max-Age
*
* @param int $maxAge
* @throws Exception\InvalidArgumentException
* @return SetCookie
*/
public function setMaxAge($maxAge)
{
if ($maxAge !== null && (!is_int($maxAge) || ($maxAge < 0))) {
throw new Exception\InvalidArgumentException('Invalid Max-Age number specified');
}
$this->maxAge = $maxAge;
return $this;
}
/**
* Get Max-Age
*
* @return int
*/
public function getMaxAge()
{
return $this->maxAge;
}
/**
* Set Expires
*
* @param int|string|DateTime $expires
*
* @return self
*
* @throws Exception\InvalidArgumentException
*/
public function setExpires($expires)
{
if ($expires === null) {
$this->expires = null;
return $this;
}
if ($expires instanceof DateTime) {
$expires = $expires->format(DateTime::COOKIE);
}
$tsExpires = $expires;
if (is_string($expires)) {
$tsExpires = strtotime($expires);
// if $tsExpires is invalid and PHP is compiled as 32bit. Check if it fail reason is the 2038 bug
if (!is_int($tsExpires) && PHP_INT_SIZE === 4) {
$dateTime = new DateTime($expires);
if ($dateTime->format('Y') > 2038) {
$tsExpires = PHP_INT_MAX;
}
}
}
if (!is_int($tsExpires) || $tsExpires < 0) {
throw new Exception\InvalidArgumentException('Invalid expires time specified');
}
$this->expires = $tsExpires;
return $this;
}
/**
* @param bool $inSeconds
* @return int|string
*/
public function getExpires($inSeconds = false)
{
if ($this->expires === null) {
return;
}
if ($inSeconds) {
return $this->expires;
}
return gmdate('D, d-M-Y H:i:s', $this->expires) . ' GMT';
}
/**
* @param string $domain
* @return SetCookie
*/
public function setDomain($domain)
{
HeaderValue::assertValid($domain);
$this->domain = $domain;
return $this;
}
/**
* @return string
*/
public function getDomain()
{
return $this->domain;
}
/**
* @param string $path
* @return SetCookie
*/
public function setPath($path)
{
HeaderValue::assertValid($path);
$this->path = $path;
return $this;
}
/**
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* @param bool $secure
* @return SetCookie
*/
public function setSecure($secure)
{
if (null !== $secure) {
$secure = (bool) $secure;
}
$this->secure = $secure;
return $this;
}
/**
* Set whether the value for this cookie should be quoted
*
* @param bool $quotedValue
* @return SetCookie
*/
public function setQuoteFieldValue($quotedValue)
{
$this->quoteFieldValue = (bool) $quotedValue;
return $this;
}
/**
* @return bool
*/
public function isSecure()
{
return $this->secure;
}
/**
* @param bool $httponly
* @return SetCookie
*/
public function setHttponly($httponly)
{
if (null !== $httponly) {
$httponly = (bool) $httponly;
}
$this->httponly = $httponly;
return $this;
}
/**
* @return bool
*/
public function isHttponly()
{
return $this->httponly;
}
/**
* Check whether the cookie has expired
*
* Always returns false if the cookie is a session cookie (has no expiry time)
*
* @param int $now Timestamp to consider as "now"
* @return bool
*/
public function isExpired($now = null)
{
if ($now === null) {
$now = time();
}
if (is_int($this->expires) && $this->expires < $now) {
return true;
}
return false;
}
/**
* Check whether the cookie is a session cookie (has no expiry time set)
*
* @return bool
*/
public function isSessionCookie()
{
return ($this->expires === null);
}
/**
* Check whether the value for this cookie should be quoted
*
* @return bool
*/
public function hasQuoteFieldValue()
{
return $this->quoteFieldValue;
}
public function isValidForRequest($requestDomain, $path, $isSecure = false)
{
if ($this->getDomain() && (strrpos($requestDomain, $this->getDomain()) === false)) {
return false;
}
if ($this->getPath() && (strpos($path, $this->getPath()) !== 0)) {
return false;
}
if ($this->secure && $this->isSecure()!==$isSecure) {
return false;
}
return true;
}
/**
* Checks whether the cookie should be sent or not in a specific scenario
*
* @param string|\Zend\Uri\Uri $uri URI to check against (secure, domain, path)
* @param bool $matchSessionCookies Whether to send session cookies
* @param int $now Override the current time when checking for expiry time
* @return bool
* @throws Exception\InvalidArgumentException If URI does not have HTTP or HTTPS scheme.
*/
public function match($uri, $matchSessionCookies = true, $now = null)
{
if (is_string($uri)) {
$uri = UriFactory::factory($uri);
}
// Make sure we have a valid Zend_Uri_Http object
if (! ($uri->isValid() && ($uri->getScheme() == 'http' || $uri->getScheme() =='https'))) {
throw new Exception\InvalidArgumentException('Passed URI is not a valid HTTP or HTTPS URI');
}
// Check that the cookie is secure (if required) and not expired
if ($this->secure && $uri->getScheme() != 'https') {
return false;
}
if ($this->isExpired($now)) {
return false;
}
if ($this->isSessionCookie() && ! $matchSessionCookies) {
return false;
}
// Check if the domain matches
if (! self::matchCookieDomain($this->getDomain(), $uri->getHost())) {
return false;
}
// Check that path matches using prefix match
if (! self::matchCookiePath($this->getPath(), $uri->getPath())) {
return false;
}
// If we didn't die until now, return true.
return true;
}
/**
* Check if a cookie's domain matches a host name.
*
* Used by Zend\Http\Cookies for cookie matching
*
* @param string $cookieDomain
* @param string $host
*
* @return bool
*/
public static function matchCookieDomain($cookieDomain, $host)
{
$cookieDomain = strtolower($cookieDomain);
$host = strtolower($host);
// Check for either exact match or suffix match
return ($cookieDomain == $host ||
preg_match('/' . preg_quote($cookieDomain) . '$/', $host));
}
/**
* Check if a cookie's path matches a URL path
*
* Used by Zend\Http\Cookies for cookie matching
*
* @param string $cookiePath
* @param string $path
* @return bool
*/
public static function matchCookiePath($cookiePath, $path)
{
return (strpos($path, $cookiePath) === 0);
}
public function toString()
{
return 'Set-Cookie: ' . $this->getFieldValue();
}
public function toStringMultipleHeaders(array $headers)
{
$headerLine = $this->toString();
/* @var $header SetCookie */
foreach ($headers as $header) {
if (!$header instanceof SetCookie) {
throw new Exception\RuntimeException(
'The SetCookie multiple header implementation can only accept an array of SetCookie headers'
);
}
$headerLine .= "\n" . $header->toString();
}
return $headerLine;
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.39
*/
class TE implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'te') {
throw new Exception\InvalidArgumentException('Invalid header line for TE string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'TE';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'TE: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.40
*/
class Trailer implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'trailer') {
throw new Exception\InvalidArgumentException('Invalid header line for Trailer string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Trailer';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Trailer: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.41
*/
class TransferEncoding implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'transfer-encoding') {
throw new Exception\InvalidArgumentException(
'Invalid header line for Transfer-Encoding string: "' . $name . '"'
);
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Transfer-Encoding';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Transfer-Encoding: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.42
*/
class Upgrade implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'upgrade') {
throw new Exception\InvalidArgumentException('Invalid header line for Upgrade string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Upgrade';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Upgrade: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
*/
class UserAgent implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (str_replace(['_', ' ', '.'], '-', strtolower($name)) !== 'user-agent') {
throw new Exception\InvalidArgumentException('Invalid header line for User-Agent string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'User-Agent';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'User-Agent: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44
*/
class Vary implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'vary') {
throw new Exception\InvalidArgumentException('Invalid header line for Vary string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Vary';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Vary: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.45
*/
class Via implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'via') {
throw new Exception\InvalidArgumentException('Invalid header line for Via string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Via';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Via: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,78 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.47
*/
class WWWAuthenticate implements MultipleHeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'www-authenticate') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for WWW-Authenticate string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'WWW-Authenticate';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'WWW-Authenticate: ' . $this->getFieldValue();
}
public function toStringMultipleHeaders(array $headers)
{
$strings = [$this->toString()];
foreach ($headers as $header) {
if (!$header instanceof WWWAuthenticate) {
throw new Exception\RuntimeException(
'The WWWAuthenticate multiple header implementation can only'
. ' accept an array of WWWAuthenticate headers'
);
}
$strings[] = $header->toString();
}
return implode("\r\n", $strings);
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.46
*/
class Warning implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'warning') {
throw new Exception\InvalidArgumentException('Invalid header line for Warning string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Warning';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Warning: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http;
use Zend\Loader\PluginClassLoader;
/**
* Plugin Class Loader implementation for HTTP headers
*/
class HeaderLoader extends PluginClassLoader
{
/**
* @var array Pre-aliased Header plugins
*/
protected $plugins = [
'accept' => 'Zend\Http\Header\Accept',
'acceptcharset' => 'Zend\Http\Header\AcceptCharset',
'acceptencoding' => 'Zend\Http\Header\AcceptEncoding',
'acceptlanguage' => 'Zend\Http\Header\AcceptLanguage',
'acceptranges' => 'Zend\Http\Header\AcceptRanges',
'age' => 'Zend\Http\Header\Age',
'allow' => 'Zend\Http\Header\Allow',
'authenticationinfo' => 'Zend\Http\Header\AuthenticationInfo',
'authorization' => 'Zend\Http\Header\Authorization',
'cachecontrol' => 'Zend\Http\Header\CacheControl',
'connection' => 'Zend\Http\Header\Connection',
'contentdisposition' => 'Zend\Http\Header\ContentDisposition',
'contentencoding' => 'Zend\Http\Header\ContentEncoding',
'contentlanguage' => 'Zend\Http\Header\ContentLanguage',
'contentlength' => 'Zend\Http\Header\ContentLength',
'contentlocation' => 'Zend\Http\Header\ContentLocation',
'contentmd5' => 'Zend\Http\Header\ContentMD5',
'contentrange' => 'Zend\Http\Header\ContentRange',
'contenttransferencoding' => 'Zend\Http\Header\ContentTransferEncoding',
'contenttype' => 'Zend\Http\Header\ContentType',
'cookie' => 'Zend\Http\Header\Cookie',
'date' => 'Zend\Http\Header\Date',
'etag' => 'Zend\Http\Header\Etag',
'expect' => 'Zend\Http\Header\Expect',
'expires' => 'Zend\Http\Header\Expires',
'from' => 'Zend\Http\Header\From',
'host' => 'Zend\Http\Header\Host',
'ifmatch' => 'Zend\Http\Header\IfMatch',
'ifmodifiedsince' => 'Zend\Http\Header\IfModifiedSince',
'ifnonematch' => 'Zend\Http\Header\IfNoneMatch',
'ifrange' => 'Zend\Http\Header\IfRange',
'ifunmodifiedsince' => 'Zend\Http\Header\IfUnmodifiedSince',
'keepalive' => 'Zend\Http\Header\KeepAlive',
'lastmodified' => 'Zend\Http\Header\LastModified',
'location' => 'Zend\Http\Header\Location',
'maxforwards' => 'Zend\Http\Header\MaxForwards',
'origin' => 'Zend\Http\Header\Origin',
'pragma' => 'Zend\Http\Header\Pragma',
'proxyauthenticate' => 'Zend\Http\Header\ProxyAuthenticate',
'proxyauthorization' => 'Zend\Http\Header\ProxyAuthorization',
'range' => 'Zend\Http\Header\Range',
'referer' => 'Zend\Http\Header\Referer',
'refresh' => 'Zend\Http\Header\Refresh',
'retryafter' => 'Zend\Http\Header\RetryAfter',
'server' => 'Zend\Http\Header\Server',
'setcookie' => 'Zend\Http\Header\SetCookie',
'te' => 'Zend\Http\Header\TE',
'trailer' => 'Zend\Http\Header\Trailer',
'transferencoding' => 'Zend\Http\Header\TransferEncoding',
'upgrade' => 'Zend\Http\Header\Upgrade',
'useragent' => 'Zend\Http\Header\UserAgent',
'vary' => 'Zend\Http\Header\Vary',
'via' => 'Zend\Http\Header\Via',
'warning' => 'Zend\Http\Header\Warning',
'wwwauthenticate' => 'Zend\Http\Header\WWWAuthenticate'
];
}

View File

@@ -0,0 +1,472 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 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 PluginClassLocator
*/
protected $pluginClassLoader = null;
/**
* @var array key names for $headers array
*/
protected $headersKeys = [];
/**
* @var array Array of header array information or Header instances
*/
protected $headers = [];
/**
* 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 = [];
$emptyLine = 0;
// iterate the header lines, some might be continuations
foreach (explode("\r\n", $string) as $line) {
// CRLF*2 is end of headers; an empty line by itself or between header lines
// is an attempt at CRLF injection.
if (preg_match('/^\s*$/', $line)) {
// empty line indicates end of headers
$emptyLine += 1;
if ($emptyLine > 2) {
throw new Exception\RuntimeException('Malformed header detected');
}
continue;
}
if ($emptyLine) {
throw new Exception\RuntimeException('Malformed header detected');
}
// 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 = [
'name' => $matches['name'],
'line' => trim($line)
];
continue;
}
if (preg_match("/^[ \t][^\r\n]*$/", $line, $matches)) {
// continuation: append to current line
$current['line'] .= trim($line);
continue;
}
// 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[] = ['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 = [];
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 = [];
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 = [];
/* @var $header Header\HeaderInterface */
foreach ($this->headers as $header) {
if ($header instanceof Header\MultipleHeaderInterface) {
$name = $header->getFieldName();
if (!isset($headers[$name])) {
$headers[$name] = [];
}
$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(['-', '_', ' ', '.'], '', strtolower($name));
}
}

View File

@@ -0,0 +1,171 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\PhpEnvironment;
/**
* Functionality for determining client IP address.
*/
class RemoteAddress
{
/**
* Whether to use proxy addresses or not.
*
* As default this setting is disabled - IP address is mostly needed to increase
* security. HTTP_* are not reliable since can easily be spoofed. It can be enabled
* just for more flexibility, but if user uses proxy to connect to trusted services
* it's his/her own risk, only reliable field for IP address is $_SERVER['REMOTE_ADDR'].
*
* @var bool
*/
protected $useProxy = false;
/**
* List of trusted proxy IP addresses
*
* @var array
*/
protected $trustedProxies = [];
/**
* HTTP header to introspect for proxies
*
* @var string
*/
protected $proxyHeader = 'HTTP_X_FORWARDED_FOR';
/**
* Changes proxy handling setting.
*
* This must be static method, since validators are recovered automatically
* at session read, so this is the only way to switch setting.
*
* @param bool $useProxy Whether to check also proxied IP addresses.
* @return RemoteAddress
*/
public function setUseProxy($useProxy = true)
{
$this->useProxy = $useProxy;
return $this;
}
/**
* Checks proxy handling setting.
*
* @return bool Current setting value.
*/
public function getUseProxy()
{
return $this->useProxy;
}
/**
* Set list of trusted proxy addresses
*
* @param array $trustedProxies
* @return RemoteAddress
*/
public function setTrustedProxies(array $trustedProxies)
{
$this->trustedProxies = $trustedProxies;
return $this;
}
/**
* Set the header to introspect for proxy IPs
*
* @param string $header
* @return RemoteAddress
*/
public function setProxyHeader($header = 'X-Forwarded-For')
{
$this->proxyHeader = $this->normalizeProxyHeader($header);
return $this;
}
/**
* Returns client IP address.
*
* @return string IP address.
*/
public function getIpAddress()
{
$ip = $this->getIpAddressFromProxy();
if ($ip) {
return $ip;
}
// direct IP address
if (isset($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
}
return '';
}
/**
* Attempt to get the IP address for a proxied client
*
* @see http://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10#section-5.2
* @return false|string
*/
protected function getIpAddressFromProxy()
{
if (!$this->useProxy
|| (isset($_SERVER['REMOTE_ADDR']) && !in_array($_SERVER['REMOTE_ADDR'], $this->trustedProxies))
) {
return false;
}
$header = $this->proxyHeader;
if (!isset($_SERVER[$header]) || empty($_SERVER[$header])) {
return false;
}
// Extract IPs
$ips = explode(',', $_SERVER[$header]);
// trim, so we can compare against trusted proxies properly
$ips = array_map('trim', $ips);
// remove trusted proxy IPs
$ips = array_diff($ips, $this->trustedProxies);
// Any left?
if (empty($ips)) {
return false;
}
// Since we've removed any known, trusted proxy servers, the right-most
// address represents the first IP we do not know about -- i.e., we do
// not know if it is a proxy server, or a client. As such, we treat it
// as the originating IP.
// @see http://en.wikipedia.org/wiki/X-Forwarded-For
$ip = array_pop($ips);
return $ip;
}
/**
* Normalize a header string
*
* Normalizes a header string to a format that is compatible with
* $_SERVER
*
* @param string $header
* @return string
*/
protected function normalizeProxyHeader($header)
{
$header = strtoupper($header);
$header = str_replace('-', '_', $header);
if (0 !== strpos($header, 'HTTP_')) {
$header = 'HTTP_' . $header;
}
return $header;
}
}

Some files were not shown because too many files have changed in this diff Show More