Add Client class
Move Request::request() to Client::request()
This commit is contained in:
parent
ad1e5a1782
commit
1a21b2b7d0
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
|
||||||
|
</project>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" />
|
||||||
|
</project>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/wellrested.iml" filepath="$PROJECT_DIR$/.idea/wellrested.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
|
|
||||||
17
README.md
17
README.md
|
|
@ -180,7 +180,11 @@ if ($rqst->getMethod() === 'PUT') {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The Request class can also make a request to another server and provide the response as a Response object. (This feature requires [PHP cURL](http://php.net/manual/en/book.curl.php).)
|
### HTTP Client
|
||||||
|
|
||||||
|
You can also use the `Client` class to make a request using cURL.
|
||||||
|
|
||||||
|
(This feature requires [PHP cURL](http://php.net/manual/en/book.curl.php).)
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// Prepare a request.
|
// Prepare a request.
|
||||||
|
|
@ -189,8 +193,9 @@ $rqst->setUri('http://my.api.local/resources/');
|
||||||
$rqst->setMethod('POST');
|
$rqst->setMethod('POST');
|
||||||
$rqst->setBody(json_encode($newResource));
|
$rqst->setBody(json_encode($newResource));
|
||||||
|
|
||||||
// Make the request.
|
// Use a Client to get a Response.
|
||||||
$resp = $rqst->request();
|
$client = new Client();
|
||||||
|
$resp = $client->request($rqst);
|
||||||
|
|
||||||
// Read the response.
|
// Read the response.
|
||||||
if ($resp->getStatusCode() === 201) {
|
if ($resp->getStatusCode() === 201) {
|
||||||
|
|
@ -200,12 +205,6 @@ if ($resp->getStatusCode() === 201) {
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
More Examples
|
|
||||||
---------------
|
|
||||||
|
|
||||||
For more examples, see the project [pjdietz/wellrested-samples](https://github.com/pjdietz/wellrested-samples). **Not yet updated for version 2.0**
|
|
||||||
|
|
||||||
|
|
||||||
Copyright and License
|
Copyright and License
|
||||||
---------------------
|
---------------------
|
||||||
Copyright © 2014 by PJ Dietz
|
Copyright © 2014 by PJ Dietz
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pjdietz\WellRESTed\Client
|
||||||
|
*
|
||||||
|
* @author PJ Dietz <pj@pjdietz.com>
|
||||||
|
* @copyright Copyright 2014 by PJ Dietz
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace pjdietz\WellRESTed;
|
||||||
|
|
||||||
|
use pjdietz\WellRESTed\Exceptions\CurlException;
|
||||||
|
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
||||||
|
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for making HTTP requests using cURL.
|
||||||
|
*/
|
||||||
|
class Client
|
||||||
|
{
|
||||||
|
/** @var array cURL options */
|
||||||
|
private $curlOpts;
|
||||||
|
|
||||||
|
public function __construct(array $curlOpts = null) {
|
||||||
|
if (is_array($curlOpts)) {
|
||||||
|
$this->curlOpts = $curlOpts;
|
||||||
|
} else {
|
||||||
|
$this->curlOpts = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an HTTP request and return a Response.
|
||||||
|
*
|
||||||
|
* @param RequestInterface $rqst
|
||||||
|
* @param array $curlOpts Option array of cURL options
|
||||||
|
* @throws \pjdietz\WellRESTed\Exceptions\CurlException
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function request(RequestInterface $rqst, $curlOpts = null)
|
||||||
|
{
|
||||||
|
$ch = curl_init();
|
||||||
|
|
||||||
|
$headers = array();
|
||||||
|
foreach ($rqst->getHeaders() as $field => $value) {
|
||||||
|
$lines[] = sprintf('%s: %s', $field, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$options = $this->curlOpts;
|
||||||
|
$options[CURLOPT_URL] = $rqst->getUri();
|
||||||
|
$options[CURLOPT_PORT] = $rqst->getPort();
|
||||||
|
$options[CURLOPT_HEADER] = 1;
|
||||||
|
$options[CURLOPT_RETURNTRANSFER] = 1;
|
||||||
|
$options[CURLOPT_HTTPHEADER] = $headers;
|
||||||
|
|
||||||
|
// Set the method. Include the body, if needed.
|
||||||
|
switch ($rqst->getMethod()) {
|
||||||
|
case 'GET':
|
||||||
|
$options[CURLOPT_HTTPGET] = 1;
|
||||||
|
break;
|
||||||
|
case 'POST':
|
||||||
|
$options[CURLOPT_POST] = 1;
|
||||||
|
$options[CURLOPT_POSTFIELDS] = $rqst->getBody();
|
||||||
|
break;
|
||||||
|
case 'PUT':
|
||||||
|
$options[CURLOPT_CUSTOMREQUEST] = 'PUT';
|
||||||
|
$options[CURLOPT_POSTFIELDS] = $rqst->getBody();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$options[CURLOPT_CUSTOMREQUEST] = $rqst->getMethod();
|
||||||
|
$options[CURLOPT_POSTFIELDS] = $rqst->getBody();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override cURL options with the user options passed in.
|
||||||
|
if ($curlOpts) {
|
||||||
|
foreach ($curlOpts as $optKey => $optValue) {
|
||||||
|
$options[$optKey] = $optValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the cURL options.
|
||||||
|
curl_setopt_array($ch, $options);
|
||||||
|
|
||||||
|
// Make the cURL request.
|
||||||
|
$result = curl_exec($ch);
|
||||||
|
|
||||||
|
// Throw an exception in the event of a cURL error.
|
||||||
|
if ($result === false) {
|
||||||
|
$error = curl_error($ch);
|
||||||
|
$errno = curl_errno($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
throw new CurlException($error, $errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a reponse to populate and return with data obtained via cURL.
|
||||||
|
$resp = new Response();
|
||||||
|
|
||||||
|
$resp->setStatusCode(curl_getinfo($ch, CURLINFO_HTTP_CODE));
|
||||||
|
|
||||||
|
// Split the result into headers and body.
|
||||||
|
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
||||||
|
$headers = substr($result, 0, $headerSize);
|
||||||
|
$body = substr($result, $headerSize);
|
||||||
|
|
||||||
|
// Set the body. Do not auto-add the Content-length header.
|
||||||
|
$resp->setBody($body, false);
|
||||||
|
|
||||||
|
// Iterate over the headers line by line and add each one.
|
||||||
|
foreach (explode("\r\n", $headers) as $header) {
|
||||||
|
if (strpos($header, ':')) {
|
||||||
|
list ($headerName, $headerValue) = explode(':', $header, 2);
|
||||||
|
$resp->setHeader($headerName, ltrim($headerValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
return $resp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,20 @@ interface RequestInterface
|
||||||
*/
|
*/
|
||||||
public function getMethod();
|
public function getMethod();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the full URI for the request.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getUri();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the hostname portion of the URI
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getHostname();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return path component of the request URI.
|
* Return path component of the request URI.
|
||||||
*
|
*
|
||||||
|
|
@ -29,6 +43,13 @@ interface RequestInterface
|
||||||
*/
|
*/
|
||||||
public function getPath();
|
public function getPath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the HTTP port
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getPort();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an associative array of query paramters.
|
* Return an associative array of query paramters.
|
||||||
*
|
*
|
||||||
|
|
@ -47,6 +68,13 @@ interface RequestInterface
|
||||||
*/
|
*/
|
||||||
public function getHeader($headerName);
|
public function getHeader($headerName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an associative array of headers.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getHeaders();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the body of the request.
|
* Return the body of the request.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -72,20 +72,6 @@ abstract class Message
|
||||||
return $this->headers;
|
return $this->headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an array containing one string for each header as "field: value"
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getHeaderLines()
|
|
||||||
{
|
|
||||||
$lines = array();
|
|
||||||
foreach ($this->headers as $field => $value) {
|
|
||||||
$lines[] = sprintf('%s: %s', $field, $value);
|
|
||||||
}
|
|
||||||
return $lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the value of a given header, or false if it does not exist.
|
* Return the value of a given header, or false if it does not exist.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -336,91 +336,6 @@ class Request extends Message implements RequestInterface
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* Make a cURL request out of the instance and return a Response.
|
|
||||||
*
|
|
||||||
* @param array|null $curlOpts Associative array of options to set using curl_setopt_array before making the request.
|
|
||||||
* @throws Exceptions\CurlException
|
|
||||||
* @return Response
|
|
||||||
*/
|
|
||||||
public function request($curlOpts = null)
|
|
||||||
{
|
|
||||||
$ch = curl_init();
|
|
||||||
|
|
||||||
$options = array(
|
|
||||||
CURLOPT_URL => $this->getUri(),
|
|
||||||
CURLOPT_PORT => $this->port,
|
|
||||||
CURLOPT_HEADER => 1,
|
|
||||||
CURLOPT_RETURNTRANSFER => 1,
|
|
||||||
CURLOPT_HTTPHEADER => $this->getHeaderLines()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set the method. Include the body, if needed.
|
|
||||||
switch ($this->method) {
|
|
||||||
case 'GET':
|
|
||||||
$options[CURLOPT_HTTPGET] = 1;
|
|
||||||
break;
|
|
||||||
case 'POST':
|
|
||||||
$options[CURLOPT_POST] = 1;
|
|
||||||
$options[CURLOPT_POSTFIELDS] = $this->body;
|
|
||||||
break;
|
|
||||||
case 'PUT':
|
|
||||||
$options[CURLOPT_CUSTOMREQUEST] = 'PUT';
|
|
||||||
$options[CURLOPT_POSTFIELDS] = $this->body;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$options[CURLOPT_CUSTOMREQUEST] = $this->method;
|
|
||||||
$options[CURLOPT_POSTFIELDS] = $this->body;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override cURL options with the user options passed in.
|
|
||||||
if ($curlOpts) {
|
|
||||||
foreach ($curlOpts as $optKey => $optValue) {
|
|
||||||
$options[$optKey] = $optValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the cURL options.
|
|
||||||
curl_setopt_array($ch, $options);
|
|
||||||
|
|
||||||
// Make the cURL request.
|
|
||||||
$result = curl_exec($ch);
|
|
||||||
|
|
||||||
// Throw an exception in the event of a cURL error.
|
|
||||||
if ($result === false) {
|
|
||||||
$error = curl_error($ch);
|
|
||||||
$errno = curl_errno($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
throw new CurlException($error, $errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a reponse to populate and return with data obtained via cURL.
|
|
||||||
$resp = new Response();
|
|
||||||
|
|
||||||
$resp->setStatusCode(curl_getinfo($ch, CURLINFO_HTTP_CODE));
|
|
||||||
|
|
||||||
// Split the result into headers and body.
|
|
||||||
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
|
||||||
$headers = substr($result, 0, $headerSize);
|
|
||||||
$body = substr($result, $headerSize);
|
|
||||||
|
|
||||||
// Set the body. Do not auto-add the Content-length header.
|
|
||||||
$resp->setBody($body, false);
|
|
||||||
|
|
||||||
// Iterate over the headers line by line and add each one.
|
|
||||||
foreach (explode("\r\n", $headers) as $header) {
|
|
||||||
if (strpos($header, ':')) {
|
|
||||||
list ($headerName, $headerValue) = explode(':', $header, 2);
|
|
||||||
$resp->setHeader($headerName, ltrim($headerValue));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
return $resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the default port for the currently set scheme.
|
* Return the default port for the currently set scheme.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use pjdietz\WellRESTed\Client;
|
||||||
|
use pjdietz\WellRESTed\Request;
|
||||||
|
use pjdietz\WellRESTed\Test;
|
||||||
|
|
||||||
|
class ClientTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testFake()
|
||||||
|
{
|
||||||
|
$this->assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider curlProvider
|
||||||
|
*/
|
||||||
|
public function testCurl($method, $uri, $opts, $code)
|
||||||
|
{
|
||||||
|
$rqst = $this->getMockBuilder('pjdietz\WellRESTed\Request')->getMock();
|
||||||
|
$rqst->expects($this->any())
|
||||||
|
->method("getUri")
|
||||||
|
->will($this->returnValue($uri));
|
||||||
|
$rqst->expects($this->any())
|
||||||
|
->method("getMethod")
|
||||||
|
->will($this->returnValue($method));
|
||||||
|
$rqst->expects($this->any())
|
||||||
|
->method("getPort")
|
||||||
|
->will($this->returnValue(80));
|
||||||
|
$rqst->expects($this->any())
|
||||||
|
->method("getHeaders")
|
||||||
|
->will($this->returnValue(array(
|
||||||
|
"Cache-control" => "max-age=0"
|
||||||
|
)));
|
||||||
|
|
||||||
|
$client = new Client(array(CURLOPT_HTTPHEADER => array("Cache-control" => "max-age=0")));
|
||||||
|
$resp = $client->request($rqst, $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 = $this->getMockBuilder('pjdietz\WellRESTed\Request')->getMock();
|
||||||
|
$rqst->expects($this->any())
|
||||||
|
->method("getUri")
|
||||||
|
->will($this->returnValue($uri));
|
||||||
|
$rqst->expects($this->any())
|
||||||
|
->method("getHeaders")
|
||||||
|
->will($this->returnValue(array(
|
||||||
|
"Cache-control" => "max-age=0"
|
||||||
|
)));
|
||||||
|
|
||||||
|
$rqst = new Request($uri);
|
||||||
|
$client = new Client();
|
||||||
|
$client->request($rqst, $opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function curlErrorProvider()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
["http://localhost:9991", [
|
||||||
|
CURLOPT_FAILONERROR, true,
|
||||||
|
CURLOPT_TIMEOUT_MS, 10
|
||||||
|
]],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -45,15 +45,6 @@ class RequestBuilderTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertEquals(3, count($this->request->getHeaders()));
|
$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
|
* @dataProvider headerProvider
|
||||||
*/
|
*/
|
||||||
|
|
@ -498,46 +489,4 @@ class RequestBuilderTest extends \PHPUnit_Framework_TestCase
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @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,
|
|
||||||
CURLOPT_TIMEOUT_MS, 10
|
|
||||||
]],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,4 @@ class ResponseBuilderTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertEquals($body, $captured);
|
$this->assertEquals($body, $captured);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue