Skip to content
This repository has been archived by the owner on Aug 9, 2021. It is now read-only.

Commit

Permalink
Merge pull request #3 from Rebilly/client-class
Browse files Browse the repository at this point in the history
Implement HTTP Client
  • Loading branch information
slavcodev committed Oct 14, 2015
2 parents 075d850 + 10f4871 commit 13b2467
Show file tree
Hide file tree
Showing 2 changed files with 317 additions and 0 deletions.
187 changes: 187 additions & 0 deletions src/Client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<?php
/**
* This file is part of the HTTP Client package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @see http://rebilly.com
*/

namespace Rebilly\HttpClient;

use BadMethodCallException;
use Psr\Http\Message\RequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use GuzzleHttp\Psr7\Request as GuzzleRequest;
use GuzzleHttp\Psr7\Response as GuzzleResponse;
use Rebilly\HttpClient\Adapter\Curl\CurlAdapter;
use RuntimeException;

/**
* Class Client.
*
* Magic facades for HTTP methods:
*
* @see Client::__call()
* @see Client::send()
*
* @method Response get($path, $params = [], $headers = [], $options = [])
* @method Response head($path, $params = [], $headers = [], $options = [])
* @method Response post($payload, $path, $params = [], $headers = [], $options = [])
* @method Response put($payload, $path, $params = [], $headers = [], $options = [])
* @method Response delete($path, $params = [], $headers = [], $options = [])
*
* @author Veaceslav Medvedev <veaceslav.medvedev@rebilly.com>
* @version 0.1
*/
final class Client
{
/**
* @var Middleware
*/
private $middleware;

/**
* @var Adapter
*/
private $adapter;

/**
* @var array
*/
private $requestOptions = [];

/**
* @var Request
*/
private $lastRequest;

/**
* @var Response
*/
private $lastResponse;

/**
* @param Adapter|callable|null $adapter
* @param Middleware[]|callable[] $middleware
*/
public function __construct($adapter = null, array $middleware = [])
{
$this->middleware = empty($middleware) ? $this : new Middleware\Stack($middleware);
$this->adapter = $adapter ?: new CurlAdapter();
}

/**
* Application (Client) should be a final layer in middleware stack.
*
* @param Request $request
* @param Response $response
*
* @return Response
*/
public function __invoke(Request $request, Response $response)
{
// Send response to the adapter as a prototype
$response = call_user_func($this->adapter, $request, $response, $this->requestOptions) ?: $response;

if (!($response instanceof Response)) {
throw new RuntimeException('Wrong response');
}

return $response;
}

/**
* Magic methods support.
*
* @see Client::send()
*
* @param string $name
* @param array $arguments
*
* @throws RuntimeException
* @throws BadMethodCallException
*
* @return mixed
*/
public function __call($name, $arguments)
{
switch (strtoupper($name)) {
case 'HEAD':
case 'GET':
case 'DELETE':
array_unshift($arguments, null);
array_unshift($arguments, $name);
return call_user_func_array([$this, 'send'], $arguments);
case 'POST':
case 'PUT':
array_unshift($arguments, $name);
return call_user_func_array([$this, 'send'], $arguments);
}

throw new BadMethodCallException(sprintf('Call unknown method %s::%s', __CLASS__, $name));
}

/**
* Send request.
*
* @param string $method The request method.
* @param mixed $payload The request body.
* @param string $uri The URL path or URL Template
* @param array $headers The headers specific for request.
* @param array $options The request options.
*
* @return Response
*/
public function send($method, $payload, $uri, array $headers = [], array $options = [])
{
$this->requestOptions = $options;
$this->lastRequest = $this->createRequest($method, $uri, $payload, $headers);
$this->lastResponse = $this->createResponse();
$this->lastResponse = call_user_func($this->middleware, $this->lastRequest, $this->lastResponse, $this);

return $this->lastResponse;
}

/**
* @return Request
*/
public function getLastRequest()
{
return $this->lastRequest;
}

/**
* @return Response
*/
public function getLastResponse()
{
return $this->lastResponse;
}

/**
* Factory method to create a new Request object.
*
* @param string $method
* @param mixed $uri
* @param mixed $payload
* @param array $headers
*
* @return Request
*/
private function createRequest($method, $uri, $payload, array $headers = [])
{
return new GuzzleRequest($method, $uri, $headers, $payload);
}

/**
* Factory method to create a new Response object.
*
* @return Response
*/
private function createResponse()
{
return new GuzzleResponse();
}
}
130 changes: 130 additions & 0 deletions tests/ClientTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<?php
/**
* This file is part of the HTTP Client package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @see http://rebilly.com
*/

namespace Rebilly\HttpClient;

use BadMethodCallException;
use Psr\Http\Message\RequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use RuntimeException;

/**
* Class ClientTest.
*
* @author Veaceslav Medvedev <veaceslav.medvedev@rebilly.com>
*/
class ClientTest extends TestCase
{
/**
* @test
*/
public function useInvalidAdapter()
{
$this->setExpectedException(RuntimeException::class);

$handler = $this->getMockForAbstractClass(Adapter::class);
$handler->expects($this->any())->method('__invoke')->willReturn('response');

$client = new Client($handler);
$client->get('http://localhost');
}

/**
* @test
* @dataProvider provideHttpMethods
*
* @param string $method
*/
public function sendRequest($method)
{
$handler = $this->getMockForAbstractClass(Adapter::class);
$client = new Client($handler);

$response = $client->send($method, [], 'http://localhost');
$this->assertInstanceOf(Response::class, $response);

$this->assertInstanceOf(Request::class, $client->getLastRequest());
$this->assertInstanceOf(Response::class, $client->getLastResponse());
}

/**
* @test
*/
public function sendRequestUsingFacades()
{
$handler = $this->getMockForAbstractClass(Adapter::class);

$client = new Client($handler);

$response = $client->get('http://localhost');
$this->assertInstanceOf(Response::class, $response);

$response = $client->post([], 'http://localhost');
$this->assertInstanceOf(Response::class, $response);

$response = $client->put([], 'http://localhost');
$this->assertInstanceOf(Response::class, $response);

$response = $client->delete('http://localhost');
$this->assertInstanceOf(Response::class, $response);
}

/**
* @test
*/
public function callUnknownFacade()
{
$this->setExpectedException(BadMethodCallException::class);

/** @var mixed $client */
$client = new Client();
$client->show('http://localhost');
}

/**
* @todo
* @test
* @dataProvider provideHttpExceptionCodes
*
* @param int $code
*/
public function sendClientExceptions($code)
{
}

/**
* @return array
*/
public function provideHttpExceptionCodes()
{
return [
[404],
[410],
[422],
[400],
[500],
];
}

/**
* @return array
*/
public function provideHttpMethods()
{
return [
['OPTIONS'],
['HEAD'],
['GET'],
['POST'],
['PUT'],
['DELETE'],
];
}
}

0 comments on commit 13b2467

Please sign in to comment.