Update documentation and add tests for Request

This commit is contained in:
PJ Dietz 2014-07-13 21:39:01 -04:00
parent 8aa6b91d91
commit 581c3d1351
3 changed files with 620 additions and 175 deletions

View File

@ -27,10 +27,6 @@ abstract class Message
* @var array
*/
protected $headerLookup;
/** @var string Name of the protocol to use. */
protected $protocol = 'HTTP';
/** @var string Version of the protocol to use. */
protected $protocolVersion = '1.1';
// -------------------------------------------------------------------------
@ -66,22 +62,6 @@ abstract class Message
$this->body = $body;
}
/**
* Return if the body is set
*
* @return bool
*/
public function issetBody()
{
return isset($this->body);
}
/** Unset the body property */
public function unsetBody()
{
unset($this->body);
}
/**
* Return an associative array of all set headers.
*
@ -110,23 +90,16 @@ abstract class Message
* Return the value of a given header, or false if it does not exist.
*
* @param string $name
* @return string|bool
* @return string|null
*/
public function getHeader($name)
{
$lowerName = strtolower($name);
if (isset($this->headerLookup[$lowerName])) {
$realName = $this->headerLookup[$lowerName];
if (isset($this->headers[$realName])) {
return $this->headers[$realName];
}
return $this->headers[$realName];
}
return false;
return null;
}
/**
@ -188,90 +161,4 @@ abstract class Message
}
}
/**
* Return the protocol (e.g., HTTP)
*
* @return string
*/
public function getProtocol()
{
return $this->protocol;
}
/**
* Set the protocol for the message.
*
* The value is expected to be the name of the protocol only. If the
* version is included, the version is striped and set as the
* protocolVersion property.
*
* <code>
* $instance->protocol = 'HTTP1/1';
* print $instance->protocol; // 'HTTP';
* print $instance->protocolVersion; // '1.1';
* </code>
*
* @param $protocol
*/
public function setProtocol($protocol)
{
if (strpos($protocol, '/') === false) {
list($this->protocol, $this->protocolVersion) = explode('/', $protocol, 2);
} else {
$this->protocol = $protocol;
}
}
/**
* Return if the protocol property is set.
*
* @return bool
*/
public function issetProtocol()
{
return isset($this->protocol);
}
/** Unset the protocol property. */
public function unsetProtocol()
{
unset($this->protocol);
}
/**
* Return the version portion of the protocol. For HTTP/1.1, this is 1.1
*
* @return string
*/
public function getProtocolVersion()
{
return $this->protocolVersion;
}
/**
* Assign a new protocol version
*
* @param string $protocolVersion
*/
public function setProtocolVersion($protocolVersion)
{
$this->protocolVersion = $protocolVersion;
}
/**
* Return if the version portion of the protocol is set.
*
* @return bool
*/
public function issetProtocolVersion()
{
return isset($this->protocolVersion);
}
/** Unset the version portion of the protocol. */
public function unsetProtocolVersion()
{
unset($this->protocolVersion);
}
}

View File

@ -10,8 +10,10 @@
namespace pjdietz\WellRESTed;
use InvalidArgumentException;
use pjdietz\WellRESTed\Exceptions\CurlException;
use pjdietz\WellRESTed\Interfaces\RequestInterface;
use UnexpectedValueException;
/**
* A Request instance represents an HTTP request. This class has two main uses:
@ -32,20 +34,20 @@ class Request extends Message implements RequestInterface
* @static
*/
static protected $theRequest;
/** @var string The Hostname for the request (e.g., www.google.com) */
private $hostname;
/** @var string HTTP method or verb for the request */
private $method = 'GET';
private $method = "GET";
/** @var string The Hostname for the request (e.g., www.google.com) */
private $hostname = "localhost";
/** @var string Scheme for the request (Must be "http" or "https" */
protected $scheme;
/** @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 */
/** @var int HTTP Port*/
private $port = 80;
/**@var array Associative array of query parameters */
/** @var array Associative array of query parameters */
private $query;
/** @var int internal count of the number of times routers have dispatched this instance */
private $routeDepth = 0;
// -------------------------------------------------------------------------
@ -55,7 +57,7 @@ class Request extends Message implements RequestInterface
* @param string|null $uri
* @param string $method
*/
public function __construct($uri = null, $method = 'GET')
public function __construct($uri = null, $method = "GET")
{
parent::__construct();
if (!is_null($uri)) {
@ -83,7 +85,11 @@ class Request extends Message implements RequestInterface
return self::$theRequest;
}
/** @return array all request headers from the current request. */
/**
* Read and return all request headers from the request issued to the server.
*
* @return array Associative array of headers
*/
public static function getRequestHeaders()
{
if (function_exists('apache_request_headers')) {
@ -138,24 +144,6 @@ class Request extends Message implements RequestInterface
$this->hostname = $hostname;
}
/**
* Return if the hostname portion of the URI is set.
*
* @return bool
*/
public function issetHostName()
{
return isset($this->hostname);
}
/**
* Unset the hostname portion of the URI.
*/
public function unsetHostname()
{
unset($this->hostname);
}
/**
* Return the method (e.g., GET, POST, PUT, DELETE)
*
@ -194,7 +182,11 @@ class Request extends Message implements RequestInterface
public function setPath($path)
{
$this->path = $path;
$this->pathParts = explode('/', substr($path, 1));
if ($path !== "/") {
$this->pathParts = explode("/", substr($path, 1));
} else {
$this->pathParts = array();
}
}
/**
@ -207,15 +199,26 @@ class Request extends Message implements RequestInterface
return $this->pathParts;
}
/** @return int */
/**
* Return the HTTP port
*
* @return int
*/
public function getPort()
{
return $this->port;
}
/** @param int $port */
public function setPort($port)
/**
* Set the HTTP port
*
* @param int $port
*/
public function setPort($port = null)
{
if (is_null($port)) {
$port = $this->getDefaultPort();
}
$this->port = $port;
}
@ -234,22 +237,50 @@ class Request extends Message implements RequestInterface
* joined by ampersands or it can be an associative array.
*
* @param string|array $query
* @throws \InvalidArgumentException
* @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.');
throw new InvalidArgumentException('Unable to parse query string.');
}
}
/**
* 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 full URI includeing protocol, hostname, path, and query.
*
@ -257,18 +288,16 @@ class Request extends Message implements RequestInterface
*/
public function getUri()
{
$uri = strtolower($this->protocol) . '://' . $this->hostname;
if ($this->port !== 80) {
$uri = $this->scheme . "://" . $this->hostname;
if ($this->port !== $this->getDefaultPort()) {
$uri .= ':' . $this->port;
}
$uri .= $this->path;
if ($this->path !== "/") {
$uri .= $this->path;
}
if ($this->query) {
$uri .= '?' . http_build_query($this->query);
}
return $uri;
}
@ -280,37 +309,33 @@ class Request extends Message implements RequestInterface
*/
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);
$host = isset($parsed['host']) ? $parsed['host'] : '';
$scheme = isset($parsed["scheme"]) ? $parsed["scheme"] : "http";
$this->setScheme($scheme);
$host = isset($parsed['host']) ? $parsed['host'] : 'localhost';
$this->setHostname($host);
$path = isset($parsed['path']) ? $parsed['path'] : '';
$this->setPath($path);
$port = isset($parsed['port']) ? (int) $parsed['port'] : 80;
$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 int The number of times a router has dispatched this Routable */
public function getRouteDepth()
{
return $this->routeDepth;
}
/** Increase the instance's internal count of its depth in nested route tables */
public function incrementRouteDepth()
{
$this->routeDepth++;
}
// -------------------------------------------------------------------------
/**
* Make a cURL request out of the instance and return a Response.
*
@ -396,4 +421,13 @@ class Request extends Message implements RequestInterface
return $resp;
}
/**
* Return the default port for the currently set scheme.
*
* @return int;
*/
protected function getDefaultPort()
{
return $this->scheme === "http" ? 80 : 443;
}
}

524
test/RequestTest.php Normal file
View File

@ -0,0 +1,524 @@
<?php
use pjdietz\WellRESTed\Request;
use pjdietz\WellRESTed\Test;
class RequestBuilderTest extends \PHPUnit_Framework_TestCase
{
/** @var Request */
private $request;
public function setUp()
{
$this->request = new Request();
foreach ($this->headerProvider() as $item) {
$name = $item[0];
$value = $item[1];
$this->request->setHeader($name, $value);
}
}
public function headerProvider()
{
return array(
array("Accept-Charset", "utf-8", "accept-charset"),
array("Accept-Encoding", "gzip, deflate", "ACCEPT-ENCODING"),
array("Cache-Control", "no-cache", "Cache-Control"),
);
}
public function testSetBody()
{
$body = "This is the body";
$rqst = new Request();
$rqst->setBody($body);
$this->assertEquals($body, $rqst->getBody());
}
public function testNullBody()
{
$this->assertNull($this->request->getBody());
}
public function testHeaders()
{
$this->assertEquals(3, count($this->request->getHeaders()));
}
/**
* @dataProvider headerProvider
*/
public function testHeaderLines($name, $value, $testName)
{
$line = "$name: $value";
$this->assertTrue(in_array($line, $this->request->getHeaderLines()));
}
/**
* @dataProvider headerProvider
*/
public function testHeaderValue($name, $value, $testName)
{
$this->assertEquals($value, $this->request->getHeader($testName));
}
/**
* @dataProvider headerProvider
*/
public function testNonsetHeader()
{
$this->assertNull($this->request->getHeader("no-header"));
}
/**
* @dataProvider headerProvider
*/
public function testUnsetHeader($name, $value, $testName)
{
$this->request->unsetHeader($testName);
$this->assertNull($this->request->getHeader($testName));
}
/**
* @dataProvider headerProvider
*/
public function testUpdateHeader($name, $value, $testName)
{
$newvalue = "newvalue";
$this->request->setHeader($testName, "newvalue");
$this->assertEquals($newvalue, $this->request->getHeader($testName));
}
/**
* @dataProvider headerProvider
*/
public function testIssetHeader($name, $value, $testName)
{
$this->assertTrue($this->request->issetHeader($testName));
}
/**
* @dataProvider headerProvider
*/
public function testNotIssetHeader($name, $value, $testName)
{
$this->request->unsetHeader($testName);
$this->assertFalse($this->request->issetHeader($testName));
}
/**
* @dataProvider uriProvider
*/
public function testUri($uri, $data)
{
$rqst = new Request($uri);
$this->assertEquals($data->uri, $rqst->getUri());
}
/**
* @dataProvider uriProvider
*/
public function testScheme($uri, $data)
{
$rqst = new Request($uri);
$this->assertEquals($data->scheme, $rqst->getScheme());
}
/**
* @dataProvider uriProvider
*/
public function testHostname($uri, $data)
{
$rqst = new Request($uri);
$this->assertEquals($data->hostname, $rqst->getHostname());
}
/**
* @dataProvider uriProvider
*/
public function testPort($uri, $data)
{
$rqst = new Request($uri);
$this->assertEquals($data->port, $rqst->getPort());
}
/**
* @dataProvider uriProvider
*/
public function testPath($uri, $data)
{
$rqst = new Request($uri);
$this->assertEquals($data->path, $rqst->getPath());
}
/**
* @dataProvider uriProvider
*/
public function testPathParts($uri, $data)
{
$rqst = new Request($uri);
$this->assertEquals($data->parts, $rqst->getPathParts());
}
/**
* @dataProvider uriProvider
*/
public function testQuery($uri, $data)
{
$rqst = new Request($uri);
$this->assertEquals($data->query, $rqst->getQuery());
}
public function uriProvider()
{
return array(
array(
"http://www.google.com",
(object) [
"uri" => "http://www.google.com",
"scheme" => "http",
"hostname" => "www.google.com",
"port" => 80,
"path" => "/",
"query" => [],
"parts" => []
]
),
array(
"https://www.google.com",
(object) [
"uri" => "https://www.google.com",
"scheme" => "https",
"hostname" => "www.google.com",
"port" => 443,
"path" => "/",
"query" => [],
"parts" => []
]
),
array(
"localhost:8080/my/path/with/parts",
(object) [
"uri" => "http://localhost:8080/my/path/with/parts",
"scheme" => "http",
"hostname" => "localhost",
"port" => 8080,
"path" => "/my/path/with/parts",
"query" => [],
"parts" => ["my", "path", "with", "parts"]
]
),
array(
"localhost?dog=bear&cat=molly",
(object) [
"uri" => "http://localhost?cat=molly&dog=bear",
"scheme" => "http",
"hostname" => "localhost",
"port" => 80,
"path" => "/",
"query" => [
"cat" => "molly",
"dog" => "bear"
],
"parts" => []
]
),
array(
"/my-page?id=2",
(object) [
"uri" => "http://localhost/my-page?id=2",
"scheme" => "http",
"hostname" => "localhost",
"port" => 80,
"path" => "/my-page",
"query" => [
"id" => "2"
],
"parts" => ["my-page"]
]
)
);
}
/**
* @dataProvider invalidSchemeProvider
* @expectedException \UnexpectedValueException
*/
public function testInvalidScheme($scheme)
{
$this->request->setScheme($scheme);
}
public function invalidSchemeProvider()
{
return [
[""],
["ftp"],
["ssh"],
[null],
[0]
];
}
/**
* @dataProvider queryProvider
*/
public function testSetQuery($input, $expected)
{
$this->request->setQuery($input);
$this->assertEquals($expected, $this->request->getQuery());
}
public function queryProvider()
{
return [
[
"cat=molly&dog=bear",
[
"cat" => "molly",
"dog" => "bear"
]
],
[
["id" => "1"],
["id" => "1"]
],
[
(object)["dog" => "bear"],
["dog" => "bear"]
],
["", []],
[[], []],
];
}
/**
* @dataProvider invalidQueryProvider
* @expectedException \InvalidArgumentException
*/
public function testInvalidQuery($query)
{
$this->request->setQuery($query);
}
public function invalidQueryProvider()
{
return [
[11],
[false],
[true],
[null]
];
}
/**
* @dataProvider methodProvider
*/
public function testMethod($method)
{
$this->request->setMethod($method);
$this->assertEquals($method, $this->request->getMethod());
}
public function methodProvider()
{
return array(
array("GET"),
array("POST"),
array("PUT"),
array("DELETE"),
array("OPTIONS"),
array("HEAD")
);
}
/**
* @dataProvider serverProvider
*/
public function testServerRequestMethod($serverVars, $expected)
{
$original = $_SERVER;
$_SERVER = array_merge($_SERVER, $serverVars);
$rqst = new Request();
$rqst->readHttpRequest();
$this->assertEquals($expected->method, $rqst->getMethod());
$_SERVER = $original;
}
/**
* @dataProvider serverProvider
*/
public function testServerRequestHost($serverVars, $expected)
{
$original = $_SERVER;
$_SERVER = array_merge($_SERVER, $serverVars);
$rqst = new Request();
$rqst->readHttpRequest();
$this->assertEquals($expected->host, $rqst->getHostname());
$_SERVER = $original;
}
/**
* @dataProvider serverProvider
*/
public function testServerRequestPath($serverVars, $expected)
{
$original = $_SERVER;
$_SERVER = array_merge($_SERVER, $serverVars);
$rqst = new Request();
$rqst->readHttpRequest();
$this->assertEquals($expected->path, $rqst->getPath());
$_SERVER = $original;
}
/**
* @dataProvider serverProvider
*/
public function testServerRequestHeaders($serverVars, $expected)
{
$original = $_SERVER;
$_SERVER = array_merge($_SERVER, $serverVars);
$rqst = new Request();
$rqst->readHttpRequest();
foreach ($expected->headers as $name => $value) {
$this->assertEquals($value, $rqst->getHeader($name));
}
$_SERVER = $original;
}
/**
* @dataProvider serverProvider
*/
public function testHasApacheHeaders($serverVars, $expected)
{
if (!function_exists('apache_request_headers')) {
function apache_request_headers() {
$headers = '';
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) === 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
$original = $_SERVER;
$_SERVER = array_merge($_SERVER, $serverVars);
$rqst = new Request();
$rqst->readHttpRequest();
foreach ($expected->headers as $name => $value) {
$this->assertEquals($value, $rqst->getHeader($name));
}
$_SERVER = $original;
}
/**
* We can only test the static member once, so no need for dataProvider.
*/
public function testStaticRequest()
{
$data = $this->serverProvider();
$serverVars = $data[0][0];
$expected = $data[0][1];
$original = $_SERVER;
$_SERVER = array_merge($_SERVER, $serverVars);
$rqst = Request::getRequest();
$this->assertEquals($expected->host, $rqst->getHostname());
$_SERVER = $original;
return $rqst;
}
/**
* @depends testStaticRequest
*/
public function testStaticRequestAgain($previousRequest)
{
$rqst = Request::getRequest();
$this->assertSame($previousRequest, $rqst);
}
public function serverProvider()
{
return [
[
[
"REQUEST_METHOD" => "GET",
"REQUEST_URI" => "/",
"HTTP_ACCEPT_CHARSET" => "utf-8",
"HTTP_HOST" => "localhost"
],
(object) [
"method" => "GET",
"host" => "localhost",
"path" => "/",
"headers" => [
"Accept-charset" => "utf-8"
]
]
],
[
[
"REQUEST_METHOD" => "POST",
"REQUEST_URI" => "/my/page",
"HTTP_ACCEPT_CHARSET" => "utf-8",
"HTTP_HOST" => "mysite.com"
],
(object) [
"method" => "POST",
"host" => "mysite.com",
"path" => "/my/page",
"headers" => [
"Accept-charset" => "utf-8"
]
]
]
];
}
/**
* @dataProvider curlProvider
*/
public function testCurl($method, $uri, $opts, $code)
{
$rqst = new Request($uri);
$rqst->setMethod($method);
$resp = $rqst->request($opts);
$this->assertEquals($code, $resp->getStatusCode());
}
public function curlProvider()
{
return [
["GET", "http://icanhasip.com", [
[CURLOPT_MAXREDIRS => 2]
], 200],
["POST", "http://icanhasip.com", [], 200],
["PUT", "http://icanhasip.com", [], 405],
["DELETE", "http://icanhasip.com", [], 405]
];
}
/**
* @dataProvider curlErrorProvider
* @expectedException \pjdietz\WellRESTed\Exceptions\CurlException
*/
public function testErrorCurl($uri, $opts)
{
$rqst = new Request($uri);
$resp = $rqst->request($opts);
}
public function curlErrorProvider()
{
return [
["http://localhost:9991", [
CURLOPT_FAILONERROR, true
]],
];
}
}