diff --git a/.travis.yml b/.travis.yml index da349cf..9f6261c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,6 @@ env: - deps="" php: - - 5.4 - 5.5 - 5.6 - hhvm @@ -19,7 +18,7 @@ php: matrix: fast_finish: true include: - - php: 5.3 + - php: 5.4 env: deps="low" - php: 7.0 env: xdebug="yes" @@ -29,7 +28,7 @@ before_install: - composer self-update install: - - if [ "$deps" = "low" ]; then composer update --prefer-lowest --prefer-stable; fi + - if [ "$deps" = "low" ]; then composer update --prefer-lowest --prefer-stable --ignore-platform-reqs; fi - if [ "$deps" = "" ]; then composer install; fi script: diff --git a/CHANGELOG.md b/CHANGELOG.md index c1857f4..e2ea2b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,42 @@ CHANGELOG 0.5.0 ----- +* **CAUTION**: This release drops support for PHP 5.3 due to the introduced + dependency on `php-http/httplug` (see below). + +* The client now depends on the [HTTPlug library](http://httplug.io/) to + perform HTTP requests. This means that the package now depends the virtual + `php-http/client-implementation`. To satisfy this dependency you have to + pick [an implementation](https://packagist.org/providers/php-http/client-implementation) + and install it together with `php-xapi/client`. + + For example, if you prefer to use [Guzzle 6](http://docs.guzzlephp.org/en/latest/) + you would do the following: + + ```bash + $ composer require --no-update php-http/guzzle6-adapter + $ composer require php-xapi/client + ``` + +* The `setHttpClient()` and `setRequestFactory()` method have been added + to the `XApiClientBuilderInterface` and must be used to configure the + `HttpClient` and `RequestFactory` instances you intend to use. + + To use [Guzzle 6](http://docs.guzzlephp.org/en/latest/), for example, + this will look like this: + + ```php + use Http\Adapter\Guzzle6\Client; + use Http\Message\MessageFactory\GuzzleMessageFactory; + use Xabbuh\XApi\Client\XApiClientBuilder; + + $builder = new XApiClientBuilder(); + $client = $builder->setHttpClient(new Client()) + ->setRequestFactory(new GuzzleMessageFactory()) + ->setBaseUrl('http://example.com/xapi/') + ->build(); + ``` + * Bumped the required versions of all `php-xapi` packages to the `1.x` release series. diff --git a/UPGRADE.md b/UPGRADE.md index 26e22f9..0add0d1 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,6 +1,45 @@ UPGRADE ======= +Upgrading from 0.4 to 0.5 +------------------------- + +* **CAUTION**: This release drops support for PHP 5.3 due to the introduced + dependency on `php-http/httplug` (see below). + +* The client now depends on the [HTTPlug library](http://httplug.io/) to + perform HTTP requests. This means that the package now depends the virtual + `php-http/client-implementation`. To satisfy this dependency you have to + pick [an implementation](https://packagist.org/providers/php-http/client-implementation) + and install it together with `php-xapi/client`. + + For example, if you prefer to use [Guzzle 6](http://docs.guzzlephp.org/en/latest/) + you would do the following: + + ```bash + $ composer require --no-update php-http/guzzle6-adapter + $ composer require php-xapi/client + ``` + +* The `setHttpClient()` and `setRequestFactory()` method have been added + to the `XApiClientBuilderInterface` and must be used to configure the + `HttpClient` and `RequestFactory` instances you intend to use. + + To use [Guzzle 6](http://docs.guzzlephp.org/en/latest/), for example, + this will look like this: + + ```php + use Http\Adapter\Guzzle6\Client; + use Http\Message\MessageFactory\GuzzleMessageFactory; + use Xabbuh\XApi\Client\XApiClientBuilder; + + $builder = new XApiClientBuilder(); + $client = $builder->setHttpClient(new Client()) + ->setRequestFactory(new GuzzleMessageFactory()) + ->setBaseUrl('http://example.com/xapi/') + ->build(); + ``` + Upgrading from 0.2 to 0.3 ------------------------- diff --git a/composer.json b/composer.json index f7fefea..a7e17ee 100644 --- a/composer.json +++ b/composer.json @@ -12,16 +12,22 @@ } ], "require": { - "php": ">=5.3.0", - "guzzle/guzzle": "~3.7", + "php": ">=5.4.0", + "php-http/client-common": "^1.0", + "php-http/client-implementation": "^1.0", + "php-http/httplug": "^1.0", + "php-http/message": "^1.0", + "php-http/message-factory": "^1.0", "php-xapi/exception": "^0.1.0", "php-xapi/model": "^1.0", "php-xapi/serializer": "^1.0", "php-xapi/serializer-implementation": "^1.0", - "php-xapi/symfony-serializer": "^1.0" + "php-xapi/symfony-serializer": "^1.0", + "psr/http-message": "^1.0" }, "require-dev": { "phpspec/phpspec": "^2.3", + "php-http/mock-client": "^0.3", "php-xapi/test-fixtures": "^1.0" }, "conflict": { diff --git a/spec/Request/HandlerSpec.php b/spec/Request/HandlerSpec.php index 5b33870..0925da9 100644 --- a/spec/Request/HandlerSpec.php +++ b/spec/Request/HandlerSpec.php @@ -2,16 +2,17 @@ namespace spec\Xabbuh\XApi\Client\Request; -use Guzzle\Http\Message\RequestInterface; -use Guzzle\Http\Message\Response; -use Guzzle\Service\ClientInterface; +use Http\Client\HttpClient; +use Http\Message\RequestFactory; use PhpSpec\ObjectBehavior; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; class HandlerSpec extends ObjectBehavior { - function let(ClientInterface $client) + function let(HttpClient $client, RequestFactory $requestFactory) { - $this->beConstructedWith($client, '1.0.1'); + $this->beConstructedWith($client, $requestFactory, 'http://example.com/xapi/', '1.0.1'); } function it_throws_an_exception_if_a_request_is_created_with_an_invalid_method() @@ -19,109 +20,100 @@ function it_throws_an_exception_if_a_request_is_created_with_an_invalid_method() $this->shouldThrow('\InvalidArgumentException')->during('createRequest', array('options', '/xapi/statements')); } - function it_sets_the_experience_api_version_header_and_the_content_type_header_when_creating_a_request(ClientInterface $client, RequestInterface $request) + function it_returns_get_request_created_by_the_http_client(RequestFactory $requestFactory, RequestInterface $request) { - $client->get('/xapi/statements')->willReturn($request); - $request->addHeader('X-Experience-API-Version', '1.0.1')->shouldBeCalled(); - $request->addHeader('Content-Type', 'application/json')->shouldBeCalled(); + $requestFactory->createRequest('GET', 'http://example.com/xapi/statements', array( + 'X-Experience-API-Version' => '1.0.1', + 'Content-Type' => 'application/json', + ))->willReturn($request); - $this->createRequest('get', '/xapi/statements'); + $this->createRequest('get', '/statements')->shouldReturn($request); + $this->createRequest('GET', '/statements')->shouldReturn($request); } - function it_sets_basic_auth_data_when_credentials_are_configured_when_creating_a_request(ClientInterface $client, RequestInterface $request) + function it_returns_post_request_created_by_the_http_client(RequestFactory $requestFactory, RequestInterface $request) { - $this->beConstructedWith($client, '1.0.1', 'username', 'password'); + $requestFactory->createRequest('POST', 'http://example.com/xapi/statements', array( + 'X-Experience-API-Version' => '1.0.1', + 'Content-Type' => 'application/json', + ))->willReturn($request); - $client->get('/xapi/statements')->willReturn($request); - $request->addHeader('X-Experience-API-Version', '1.0.1')->shouldBeCalled(); - $request->addHeader('Content-Type', 'application/json')->shouldBeCalled(); - $request->setAuth('username', 'password')->shouldBeCalled(); - - $this->createRequest('get', '/xapi/statements'); - } - - function it_returns_get_request_created_by_the_http_client(ClientInterface $client, RequestInterface $request) - { - $client->get('/xapi/statements')->willReturn($request); - - $this->createRequest('get', '/xapi/statements')->shouldReturn($request); - $this->createRequest('GET', '/xapi/statements')->shouldReturn($request); - } - - function it_returns_post_request_created_by_the_http_client(ClientInterface $client, RequestInterface $request) - { - $client->post('/xapi/statements', null, 'body')->willReturn($request); - - $this->createRequest('post', '/xapi/statements', array(), 'body')->shouldReturn($request); - $this->createRequest('POST', '/xapi/statements', array(), 'body')->shouldReturn($request); + $this->createRequest('post', '/statements', array(), 'body')->shouldReturn($request); + $this->createRequest('POST', '/statements', array(), 'body')->shouldReturn($request); } - function it_returns_put_request_created_by_the_http_client(ClientInterface $client, RequestInterface $request) + function it_returns_put_request_created_by_the_http_client(RequestFactory $requestFactory, RequestInterface $request) { - $client->put('/xapi/statements', null, 'body')->willReturn($request); + $requestFactory->createRequest('PUT', 'http://example.com/xapi/statements', array( + 'X-Experience-API-Version' => '1.0.1', + 'Content-Type' => 'application/json', + ))->willReturn($request); - $this->createRequest('put', '/xapi/statements', array(), 'body')->shouldReturn($request); - $this->createRequest('PUT', '/xapi/statements', array(), 'body')->shouldReturn($request); + $this->createRequest('put', '/statements', array(), 'body')->shouldReturn($request); + $this->createRequest('PUT', '/statements', array(), 'body')->shouldReturn($request); } - function it_returns_delete_request_created_by_the_http_client(ClientInterface $client, RequestInterface $request) + function it_returns_delete_request_created_by_the_http_client(RequestFactory $requestFactory, RequestInterface $request) { - $client->delete('/xapi/statements')->willReturn($request); + $requestFactory->createRequest('DELETE', 'http://example.com/xapi/statements', array( + 'X-Experience-API-Version' => '1.0.1', + 'Content-Type' => 'application/json', + ))->willReturn($request); - $this->createRequest('delete', '/xapi/statements')->shouldReturn($request); - $this->createRequest('DELETE', '/xapi/statements')->shouldReturn($request); + $this->createRequest('delete', '/statements')->shouldReturn($request); + $this->createRequest('DELETE', '/statements')->shouldReturn($request); } - function it_throws_an_access_denied_exception_when_a_401_status_code_is_returned(RequestInterface $request, Response $response) + function it_throws_an_access_denied_exception_when_a_401_status_code_is_returned(HttpClient $client, RequestInterface $request, ResponseInterface $response) { - $request->send()->willReturn($response); + $client->sendRequest($request)->willReturn($response); $response->getStatusCode()->willReturn(401); - $response->getBody(true)->willReturn('body'); + $response->getBody()->willReturn('body'); $this->shouldThrow('Xabbuh\XApi\Common\Exception\AccessDeniedException')->during('executeRequest', array($request, array(200))); } - function it_throws_an_access_denied_exception_when_a_403_status_code_is_returned(RequestInterface $request, Response $response) + function it_throws_an_access_denied_exception_when_a_403_status_code_is_returned(HttpClient $client, RequestInterface $request, ResponseInterface $response) { - $request->send()->willReturn($response); + $client->sendRequest($request)->willReturn($response); $response->getStatusCode()->willReturn(403); - $response->getBody(true)->willReturn('body'); + $response->getBody()->willReturn('body'); $this->shouldThrow('Xabbuh\XApi\Common\Exception\AccessDeniedException')->during('executeRequest', array($request, array(200))); } - function it_throws_a_not_found_exception_when_a_404_status_code_is_returned(RequestInterface $request, Response $response) + function it_throws_a_not_found_exception_when_a_404_status_code_is_returned(HttpClient $client, RequestInterface $request, ResponseInterface $response) { - $request->send()->willReturn($response); + $client->sendRequest($request)->willReturn($response); $response->getStatusCode()->willReturn(404); - $response->getBody(true)->willReturn('body'); + $response->getBody()->willReturn('body'); $this->shouldThrow('Xabbuh\XApi\Common\Exception\NotFoundException')->during('executeRequest', array($request, array(200))); } - function it_throws_a_conflict_exception_when_a_409_status_code_is_returned(RequestInterface $request, Response $response) + function it_throws_a_conflict_exception_when_a_409_status_code_is_returned(HttpClient $client, RequestInterface $request, ResponseInterface $response) { - $request->send()->willReturn($response); + $client->sendRequest($request)->willReturn($response); $response->getStatusCode()->willReturn(409); - $response->getBody(true)->willReturn('body'); + $response->getBody()->willReturn('body'); $this->shouldThrow('Xabbuh\XApi\Common\Exception\ConflictException')->during('executeRequest', array($request, array(200))); } - function it_throws_an_xapi_exception_when_an_unexpected_status_code_is_returned(RequestInterface $request, Response $response) + function it_throws_an_xapi_exception_when_an_unexpected_status_code_is_returned(HttpClient $client, RequestInterface $request, ResponseInterface $response) { - $request->send()->willReturn($response); + $client->sendRequest($request)->willReturn($response); $response->getStatusCode()->willReturn(204); - $response->getBody(true)->willReturn('body'); + $response->getBody()->willReturn('body'); $this->shouldThrow('Xabbuh\XApi\Common\Exception\XApiException')->during('executeRequest', array($request, array(200))); } - function it_returns_the_response_on_success(RequestInterface $request, Response $response) + function it_returns_the_response_on_success(HttpClient $client, RequestInterface $request, ResponseInterface $response) { - $request->send()->willReturn($response); + $client->sendRequest($request)->willReturn($response); $response->getStatusCode()->willReturn(200); - $response->getBody(true)->willReturn('body'); + $response->getBody()->willReturn('body'); $this->executeRequest($request, array(200))->shouldReturn($response); } diff --git a/spec/XApiClientBuilderSpec.php b/spec/XApiClientBuilderSpec.php index e6a014d..7a001e0 100644 --- a/spec/XApiClientBuilderSpec.php +++ b/spec/XApiClientBuilderSpec.php @@ -2,6 +2,8 @@ namespace spec\Xabbuh\XApi\Client; +use Http\Client\HttpClient; +use Http\Message\RequestFactory; use PhpSpec\ObjectBehavior; class XApiClientBuilderSpec extends ObjectBehavior @@ -11,22 +13,45 @@ function it_is_an_xapi_client_builder() $this->shouldHaveType('Xabbuh\XApi\Client\XApiClientBuilderInterface'); } - function it_creates_an_xapi_client() + function it_creates_an_xapi_client(HttpClient $httpClient, RequestFactory $requestFactory) { + $this->setHttpClient($httpClient); + $this->setRequestFactory($requestFactory); $this->setBaseUrl('http://example.com/xapi/'); $this->build()->shouldHaveType('Xabbuh\XApi\Client\XApiClientInterface'); } - function its_methods_can_be_chained() + function its_methods_can_be_chained(HttpClient $httpClient, RequestFactory $requestFactory) { + $this->setHttpClient($httpClient)->shouldReturn($this); + $this->setRequestFactory($requestFactory)->shouldReturn($this); $this->setBaseUrl('http://example.com/xapi/')->shouldReturn($this); $this->setVersion('1.0.0')->shouldReturn($this); $this->setAuth('foo', 'bar')->shouldReturn($this); $this->setOAuthCredentials('consumer key', 'consumer secret', 'token', 'token secret')->shouldReturn($this); } - function it_throws_an_exception_if_the_base_uri_is_not_configured() + function it_throws_an_exception_if_the_http_client_is_not_configured(RequestFactory $requestFactory) { + $this->setRequestFactory($requestFactory); + $this->setBaseUrl('http://example.com/xapi/'); + + $this->shouldThrow('\LogicException')->during('build'); + } + + function it_throws_an_exception_if_the_request_factory_is_not_configured(HttpClient $httpClient) + { + $this->setHttpClient($httpClient); + $this->setBaseUrl('http://example.com/xapi/'); + + $this->shouldThrow('\LogicException')->during('build'); + } + + function it_throws_an_exception_if_the_base_uri_is_not_configured(HttpClient $httpClient, RequestFactory $requestFactory) + { + $this->setHttpClient($httpClient); + $this->setRequestFactory($requestFactory); + $this->shouldThrow('\LogicException')->during('build'); } } diff --git a/src/Api/DocumentApiClient.php b/src/Api/DocumentApiClient.php index ca4c597..c931f82 100644 --- a/src/Api/DocumentApiClient.php +++ b/src/Api/DocumentApiClient.php @@ -82,7 +82,7 @@ protected function doGetDocument($uri, array $urlParameters) { $request = $this->requestHandler->createRequest('get', $uri, $urlParameters); $response = $this->requestHandler->executeRequest($request, array(200)); - $document = $this->deserializeDocument($response->getBody(true)); + $document = $this->deserializeDocument((string) $response->getBody()); return $document; } diff --git a/src/Api/StatementsApiClient.php b/src/Api/StatementsApiClient.php index 738c9ba..a83476a 100644 --- a/src/Api/StatementsApiClient.php +++ b/src/Api/StatementsApiClient.php @@ -165,7 +165,7 @@ private function doStoreStatements($statements, $method = 'post', $parameters = $serializedStatements ); $response = $this->requestHandler->executeRequest($request, array($validStatusCode)); - $statementIds = json_decode($response->getBody(true)); + $statementIds = json_decode((string) $response->getBody()); if (is_array($statements)) { /** @var Statement[] $statements */ @@ -201,9 +201,9 @@ private function doGetStatements($url, array $urlParameters = array()) $response = $this->requestHandler->executeRequest($request, array(200)); if (isset($urlParameters['statementId']) || isset($urlParameters['voidedStatementId'])) { - return $this->statementSerializer->deserializeStatement($response->getBody(true)); + return $this->statementSerializer->deserializeStatement((string) $response->getBody()); } else { - return $this->statementResultSerializer->deserializeStatementResult($response->getBody(true)); + return $this->statementResultSerializer->deserializeStatementResult((string) $response->getBody()); } } } diff --git a/src/Request/Handler.php b/src/Request/Handler.php index 61777f1..b6bbb7f 100644 --- a/src/Request/Handler.php +++ b/src/Request/Handler.php @@ -11,41 +11,39 @@ namespace Xabbuh\XApi\Client\Request; -use Guzzle\Http\ClientInterface; -use Guzzle\Http\Exception\ClientErrorResponseException; -use Guzzle\Http\Message\RequestInterface; +use Http\Client\Exception; +use Http\Client\HttpClient; +use Http\Message\RequestFactory; +use Psr\Http\Message\RequestInterface; use Xabbuh\XApi\Common\Exception\AccessDeniedException; use Xabbuh\XApi\Common\Exception\ConflictException; use Xabbuh\XApi\Common\Exception\NotFoundException; use Xabbuh\XApi\Common\Exception\XApiException; /** - * Prepare and execute xAPI HTTP requests. + * Prepares and executes xAPI HTTP requests. * * @author Christian Flothmann */ final class Handler implements HandlerInterface { private $httpClient; - + private $requestFactory; + private $baseUri; private $version; - private $username; - - private $password; - /** - * @param ClientInterface $httpClient The HTTP client - * @param string $version The xAPI version - * @param string $username The optional HTTP auth username - * @param string $password The optional HTTP auth password + * @param HttpClient $httpClient The HTTP client sending requests to the remote LRS + * @param RequestFactory $requestFactory The factory used to create PSR-7 HTTP requests + * @param string $baseUri The APIs base URI (all end points will be created relatively to this URI) + * @param string $version The xAPI version */ - public function __construct(ClientInterface $httpClient, $version, $username = null, $password = null) + public function __construct(HttpClient $httpClient, RequestFactory $requestFactory, $baseUri, $version) { $this->httpClient = $httpClient; + $this->requestFactory = $requestFactory; + $this->baseUri = $baseUri; $this->version = $version; - $this->username = $username; - $this->password = $password; } /** @@ -53,36 +51,21 @@ public function __construct(ClientInterface $httpClient, $version, $username = n */ public function createRequest($method, $uri, array $urlParameters = array(), $body = null) { - if (count($urlParameters) > 0) { - $uri .= '?'.http_build_query($urlParameters); + if (!in_array(strtoupper($method), array('GET', 'POST', 'PUT', 'DELETE'))) { + throw new \InvalidArgumentException(sprintf('"%s" is no valid HTTP method (expected one of [GET, POST, PUT, DELETE]) in an xAPI context.', $method)); } - switch (strtolower($method)) { - case 'get': - $request = $this->httpClient->get($uri); - break; - case 'post': - $request = $this->httpClient->post($uri, null, $body); - break; - case 'put': - $request = $this->httpClient->put($uri, null, $body); - break; - case 'delete': - $request = $this->httpClient->delete($uri); - break; - default: - throw new \InvalidArgumentException( - $method.' is no valid HTTP method' - ); - } - - $request->addHeader('X-Experience-API-Version', $this->version); - $request->addHeader('Content-Type', 'application/json'); + $uri = rtrim($this->baseUri, '/').'/'.ltrim($uri, '/'); - if (null !== $this->username && null !== $this->password) { - $request->setAuth($this->username, $this->password); + if (count($urlParameters) > 0) { + $uri .= '?'.http_build_query($urlParameters); } + $request = $this->requestFactory->createRequest(strtoupper($method), $uri, array( + 'X-Experience-API-Version' => $this->version, + 'Content-Type' => 'application/json', + )); + return $request; } @@ -92,25 +75,25 @@ public function createRequest($method, $uri, array $urlParameters = array(), $bo public function executeRequest(RequestInterface $request, array $validStatusCodes) { try { - $response = $request->send(); - } catch (ClientErrorResponseException $e) { - $response = $e->getResponse(); + $response = $this->httpClient->sendRequest($request); + } catch (Exception $e) { + throw new XApiException($e->getMessage(), $e->getCode(), $e); } // catch some common errors if (in_array($response->getStatusCode(), array(401, 403))) { throw new AccessDeniedException( - $response->getBody(true), + (string) $response->getBody(), $response->getStatusCode() ); } elseif (404 === $response->getStatusCode()) { - throw new NotFoundException($response->getBody(true)); + throw new NotFoundException((string) $response->getBody()); } elseif (409 === $response->getStatusCode()) { - throw new ConflictException($response->getBody(true)); + throw new ConflictException((string) $response->getBody()); } if (!in_array($response->getStatusCode(), $validStatusCodes)) { - throw new XApiException($response->getBody(true), $response->getStatusCode()); + throw new XApiException((string) $response->getBody(), $response->getStatusCode()); } return $response; diff --git a/src/Request/HandlerInterface.php b/src/Request/HandlerInterface.php index ba9874a..f07ea6f 100644 --- a/src/Request/HandlerInterface.php +++ b/src/Request/HandlerInterface.php @@ -11,7 +11,8 @@ namespace Xabbuh\XApi\Client\Request; -use Guzzle\Http\Message\RequestInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; use Xabbuh\XApi\Common\Exception\XApiException; /** @@ -41,7 +42,7 @@ public function createRequest($method, $uri, array $urlParameters = array(), $bo * the calling method is able to * handle * - * @return \Guzzle\Http\Message\Response The remote server's response + * @return ResponseInterface The remote server's response * * @throws XApiException when the request fails */ diff --git a/src/XApiClientBuilder.php b/src/XApiClientBuilder.php index 21467ae..5da8cf8 100644 --- a/src/XApiClientBuilder.php +++ b/src/XApiClientBuilder.php @@ -11,8 +11,11 @@ namespace Xabbuh\XApi\Client; -use Guzzle\Http\Client; -use Guzzle\Plugin\Oauth\OauthPlugin; +use Http\Client\Common\Plugin\AuthenticationPlugin; +use Http\Client\Common\PluginClient; +use Http\Client\HttpClient; +use Http\Message\Authentication\BasicAuth; +use Http\Message\RequestFactory; use Xabbuh\XApi\Client\Request\Handler; use Xabbuh\XApi\Serializer\SerializerFactoryInterface; use Xabbuh\XApi\Serializer\SerializerRegistry; @@ -26,6 +29,17 @@ final class XApiClientBuilder implements XApiClientBuilderInterface { private $serializerFactory; + + /** + * @var HttpClient|null + */ + private $httpClient; + + /** + * @var RequestFactory|null + */ + private $requestFactory; + private $baseUrl; private $version; private $username; @@ -37,6 +51,26 @@ public function __construct(SerializerFactoryInterface $serializerFactory = null $this->serializerFactory = $serializerFactory ?: new SerializerFactory(); } + /** + * {@inheritdoc} + */ + public function setHttpClient(HttpClient $httpClient) + { + $this->httpClient = $httpClient; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setRequestFactory(RequestFactory $requestFactory) + { + $this->requestFactory = $requestFactory; + + return $this; + } + /** * {@inheritDoc} */ @@ -88,14 +122,16 @@ public function setOAuthCredentials($consumerKey, $consumerSecret, $token, $toke */ public function build() { - if (null === $this->baseUrl) { - throw new \LogicException('Base URI value was not configured.'); + if (null === $httpClient = $this->httpClient) { + throw new \LogicException('No HTTP client was configured.'); } - $httpClient = new Client($this->baseUrl); + if (null === $this->requestFactory) { + throw new \LogicException('No request factory was configured.'); + } - if (is_array($this->oAuthCredentials)) { - $httpClient->addSubscriber(new OauthPlugin($this->oAuthCredentials)); + if (null === $this->baseUrl) { + throw new \LogicException('Base URI value was not configured.'); } $serializerRegistry = new SerializerRegistry(); @@ -104,10 +140,13 @@ public function build() $serializerRegistry->setActorSerializer($this->serializerFactory->createActorSerializer()); $serializerRegistry->setDocumentDataSerializer($this->serializerFactory->createDocumentDataSerializer()); + if (null !== $this->username && null !== $this->password) { + $httpClient = new PluginClient($httpClient, array(new AuthenticationPlugin(new BasicAuth($this->username, $this->password)))); + } + $version = null === $this->version ? '1.0.1' : $this->version; - $requestHandler = new Handler($httpClient, $version, $this->username, $this->password); - $xApiClient = new XApiClient($requestHandler, $serializerRegistry, $this->version); + $requestHandler = new Handler($httpClient, $this->requestFactory, $this->baseUrl, $version); - return $xApiClient; + return new XApiClient($requestHandler, $serializerRegistry, $this->version); } } diff --git a/src/XApiClientBuilderInterface.php b/src/XApiClientBuilderInterface.php index 0e5f08b..18b859a 100644 --- a/src/XApiClientBuilderInterface.php +++ b/src/XApiClientBuilderInterface.php @@ -11,6 +11,9 @@ namespace Xabbuh\XApi\Client; +use Http\Client\HttpClient; +use Http\Message\RequestFactory; + /** * xAPI client builder. * @@ -18,6 +21,24 @@ */ interface XApiClientBuilderInterface { + /** + * Sets the HTTP client implementation that will be used to issue HTTP requests. + * + * @param HttpClient $httpClient The HTTP client implementation + * + * @return XApiClientBuilderInterface The builder + */ + public function setHttpClient(HttpClient $httpClient); + + /** + * Sets the requests factory which creates requests that are then handled by the HTTP client. + * + * @param RequestFactory $requestFactory The request factory + * + * @return XApiClientBuilderInterface The builder + */ + public function setRequestFactory(RequestFactory $requestFactory); + /** * Sets the LRS base URL. * diff --git a/tests/Api/ActivityProfileApiClientTest.php b/tests/Api/ActivityProfileApiClientTest.php index 2cf61df..48320cc 100644 --- a/tests/Api/ActivityProfileApiClientTest.php +++ b/tests/Api/ActivityProfileApiClientTest.php @@ -87,8 +87,7 @@ public function testDeleteDocument() 'activityId' => 'activity-id', 'profileId' => 'profile-id', ), - '', - $this->createResponseMock(204, '') + '' ); $this->validateSerializer(array()); diff --git a/tests/Api/AgentProfileApiClientTest.php b/tests/Api/AgentProfileApiClientTest.php index afb2116..1dfecdf 100644 --- a/tests/Api/AgentProfileApiClientTest.php +++ b/tests/Api/AgentProfileApiClientTest.php @@ -94,8 +94,7 @@ public function testDeleteDocument() 'agent' => 'agent-as-json', 'profileId' => 'profile-id', ), - '', - $this->createResponseMock(204, '') + '' ); $this->validateSerializer(array(array('data' => $profile->getAgent(), 'result' => 'agent-as-json'))); diff --git a/tests/Api/ApiClientTest.php b/tests/Api/ApiClientTest.php index fbd6fcd..9632eb1 100644 --- a/tests/Api/ApiClientTest.php +++ b/tests/Api/ApiClientTest.php @@ -75,31 +75,9 @@ protected function validateSerializer(array $serializerMap) }); } - protected function createResponseMock($statusCode, $body) + protected function validateRequest($method, $uri, array $urlParameters, $body = null) { - $response = $this->getMockBuilder('\Guzzle\Http\Message\Response') - ->disableOriginalConstructor() - ->getMock(); - $response->expects($this->any()) - ->method('getStatusCode') - ->willReturn($statusCode); - $response->expects($this->any()) - ->method('getBody') - ->willReturn($body); - - return $response; - } - - protected function validateRequest($method, $uri, array $urlParameters, $body = null, $response = null) - { - $request = $this->getMockBuilder('\Guzzle\Http\Message\RequestInterface')->getMock(); - - if (null !== $response) { - $request->expects($this->any()) - ->method('send') - ->willReturn($response); - } - + $request = $this->getMockBuilder('\Psr\Http\Message\RequestInterface')->getMock(); $this ->requestHandler ->expects($this->once()) @@ -113,8 +91,10 @@ protected function validateRequest($method, $uri, array $urlParameters, $body = protected function validateRetrieveApiCall($method, $uri, array $urlParameters, $statusCode, $type, $transformedResult, array $serializerMap = array()) { $rawResponse = 'the-server-response'; - $response = $this->createResponseMock($statusCode, $rawResponse); - $request = $this->validateRequest($method, $uri, $urlParameters, null, $response); + $response = $this->getMockBuilder('\Psr\Http\Message\ResponseInterface')->getMock(); + $response->expects($this->any())->method('getStatusCode')->willReturn($statusCode); + $response->expects($this->any())->method('getBody')->willReturn($rawResponse); + $request = $this->validateRequest($method, $uri, $urlParameters); if (404 === $statusCode) { $this @@ -146,8 +126,10 @@ protected function validateRetrieveApiCall($method, $uri, array $urlParameters, protected function validateStoreApiCall($method, $uri, array $urlParameters, $statusCode, $rawResponse, $object, array $serializerMap = array()) { $rawRequest = 'the-request-body'; - $response = $this->createResponseMock($statusCode, $rawResponse); - $request = $this->validateRequest($method, $uri, $urlParameters, $rawRequest, $response); + $response = $this->getMockBuilder('\Psr\Http\Message\ResponseInterface')->getMock(); + $response->expects($this->any())->method('getStatusCode')->willReturn($statusCode); + $response->expects($this->any())->method('getBody')->willReturn($rawResponse); + $request = $this->validateRequest($method, $uri, $urlParameters, $rawRequest); $this ->requestHandler ->expects($this->once()) diff --git a/tests/Api/StateApiClientTest.php b/tests/Api/StateApiClientTest.php index 5a0ea2b..61d5968 100644 --- a/tests/Api/StateApiClientTest.php +++ b/tests/Api/StateApiClientTest.php @@ -96,8 +96,7 @@ public function testDeleteDocument() 'agent' => 'agent-as-json', 'stateId' => 'state-id', ), - '', - $this->createResponseMock(204, '') + '' ); $this->validateSerializer(array(array('data' => $state->getActor(), 'result' => 'agent-as-json')));