wellrested/src/pjdietz/WellRESTed/Request.php

366 lines
9.0 KiB
PHP

<?php
/**
* pjdietz\WellRESTed\Request
*
* @author PJ Dietz <pj@pjdietz.com>
* @copyright Copyright 2015 by PJ Dietz
* @license MIT
*/
namespace pjdietz\WellRESTed;
use InvalidArgumentException;
use pjdietz\WellRESTed\Interfaces\RequestInterface;
use UnexpectedValueException;
/**
* A Request instance represents an HTTP request.
*/
class Request extends Message implements RequestInterface
{
/**
* Singleton instance derived from reading the request sent to the server.
*
* @var Request
* @static
*/
static protected $theRequest;
/** @var string HTTP method or verb for the request */
private $method = "GET";
/** @var string Scheme for the request (Must be "http" or "https" */
private $scheme;
/** @var string The Hostname for the request */
private $hostname = "localhost";
/** @var string Path component of the URI for the request */
private $path = "/";
/** @var array Array of fragments of the path, delimited by slashes */
private $pathParts;
/** @var int HTTP Port */
private $port = 80;
/** @var array Associative array of query parameters */
private $query;
// -------------------------------------------------------------------------
/**
* Create a new Request instance.
*
* @param string|null $uri
* @param string $method
*/
public function __construct($uri = null, $method = "GET")
{
parent::__construct();
if (!is_null($uri)) {
$this->setUri($uri);
}
$this->method = $method;
}
// -------------------------------------------------------------------------
/**
* Return a reference to the singleton instance of the Request derived
* from the server's information about the request sent to the server.
*
* @return Request
* @static
*/
public static function getRequest()
{
if (!isset(self::$theRequest)) {
$request = new Request();
$request->readHttpRequest();
self::$theRequest = $request;
}
return self::$theRequest;
}
/**
* Read and return all request headers from the request issued to the server.
*
* @return array Associative array of headers
*/
public static function getRequestHeaders()
{
// Prefer apache_request_headers is available.
if (function_exists('apache_request_headers')) {
return apache_request_headers();
}
// http://www.php.net/manual/en/function.getallheaders.php#84262
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) === "HTTP_") {
$headers[str_replace(" ", "-", ucwords(strtolower(str_replace("_", " ", substr($name, 5)))))] = $value;
}
}
return $headers;
}
/**
* Set instance members based on the HTTP request sent to the server.
*/
public function readHttpRequest()
{
$this->setBody(file_get_contents("php://input"), false);
$this->headers = self::getRequestHeaders();
// Add case insensitive headers to the lookup table.
foreach ($this->headers as $key => $value) {
$this->headerLookup[strtolower($key)] = $key;
}
$this->setMethod($_SERVER["REQUEST_METHOD"]);
$this->setUri($_SERVER["REQUEST_URI"]);
$this->setHostname($_SERVER["HTTP_HOST"]);
}
/**
* Return the method (e.g., GET, POST, PUT, DELETE)
*
* @return string
*/
public function getMethod()
{
return $this->method;
}
/**
* Assign the method (e.g., GET, POST, PUT, DELETE)
*
* @param string $method
*/
public function setMethod($method)
{
$this->method = $method;
}
/**
* Return the full URI includeing protocol, hostname, path, and query.
*
* @return array
*/
public function getUri()
{
$uri = $this->scheme . "://" . $this->hostname;
if ($this->port !== $this->getDefaultPort()) {
$uri .= ":" . $this->port;
}
if ($this->path !== "/") {
$uri .= $this->path;
}
if ($this->query) {
$uri .= "?" . http_build_query($this->query);
}
return $uri;
}
/**
* Set the URI for the Request. This sets the other members: hostname,
* path, port, and query.
*
* @param string $uri
*/
public function setUri($uri)
{
// Provide http and localhost if missing.
if ($uri[0] === "/") {
$uri = "http://localhost" . $uri;
} elseif (strpos($uri, "://") === false) {
$uri = "http://" . $uri;
}
$parsed = parse_url($uri);
$scheme = isset($parsed["scheme"]) ? $parsed["scheme"] : "http";
$this->setScheme($scheme);
$host = isset($parsed["host"]) ? $parsed["host"] : "localhost";
$this->setHostname($host);
$port = isset($parsed["port"]) ? (int) $parsed["port"] : $this->getDefaultPort();
$this->setPort($port);
$path = isset($parsed["path"]) ? $parsed["path"] : "/";
$this->setPath($path);
$query = isset($parsed["query"]) ? $parsed["query"] : "";
$this->setQuery($query);
}
/**
* Return the hostname portion of the URI
*
* @return string
*/
public function getHostname()
{
return $this->hostname;
}
/**
* Assign the hostname portion of the URI
*
* @param string $hostname
*/
public function setHostname($hostname)
{
$this->hostname = $hostname;
}
/**
* Set the scheme for the request (either "http" or "https")
*
* @param string $scheme
* @throws \UnexpectedValueException
*/
public function setScheme($scheme)
{
$scheme = strtolower($scheme);
if (!in_array($scheme, array("http", "https"))) {
throw new UnexpectedValueException('Scheme must be "http" or "https".');
}
$this->scheme = $scheme;
}
/**
* Return the scheme for the request (either "http" or "https")
*
* @return string
*/
public function getScheme()
{
return $this->scheme;
}
/**
* Return the path part of the URI as a string.
*
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* Set the path and pathParts members.
*
* @param string $path
*/
public function setPath($path)
{
$this->path = $path;
if ($path !== "/") {
$this->pathParts = explode("/", substr($path, 1));
} else {
$this->pathParts = array();
}
}
/**
* Return an array of the sections of the path delimited by slashes.
*
* @return array
*/
public function getPathParts()
{
return $this->pathParts;
}
/**
* Return the HTTP port
*
* @return int
*/
public function getPort()
{
return $this->port;
}
/**
* Set the HTTP port
*
* @param int $port
*/
public function setPort($port = null)
{
if (is_null($port)) {
$port = $this->getDefaultPort();
}
$this->port = $port;
}
/**
* Return an associative array representing the query.
*
* @return array
*/
public function getQuery()
{
return $this->query;
}
/**
* Set the query. The value passed can be a query string of key-value pairs
* joined by ampersands or it can be an associative array.
*
* @param string|array $query
* @throws InvalidArgumentException
*/
public function setQuery($query)
{
if (is_string($query)) {
$qs = $query;
parse_str($qs, $query);
} elseif (is_object($query)) {
$query = (array) $query;
}
if (is_array($query)) {
ksort($query);
$this->query = $query;
} else {
throw new InvalidArgumentException("Unable to parse query string.");
}
}
/**
* Return the form fields for this request.
*
* @return array
*/
public function getFormFields()
{
parse_str($this->getBody(), $fields);
return $fields;
}
/**
* Set the body by supplying an associative array of form fields.
*
* In addition, add a "Content-type: application/x-www-form-urlencoded" header
*
* @param array $fields
*/
public function setFormFields(array $fields)
{
$this->setBody(http_build_query($fields));
$this->setHeader("Content-type", "application/x-www-form-urlencoded");
}
// -------------------------------------------------------------------------
/**
* Return the default port for the currently set scheme.
*
* @return int;
*/
protected function getDefaultPort()
{
return $this->scheme === "http" ? 80 : 443;
}
}