Building a PHP SDK with HTTPlug


@nathanjdunn
How would you go about building an SDK?
- Which abstraction would you use?
- How would you test it?
- What happens if you want to switch from Guzzle to cURL?
How does HTTPlug help?
- HTTPlug decouples packages from the implementation
- Comes with a 'mock client' which allows you to send fake HTTP requests
- Plugins system to handle custom logic
- Authentication support for common auth types
- PSR-7 Compatible (🤷♀️)

HTTPlug makes certain decisions so you don't have to
The Chargebee API
- Subscription Billing Platform
- REST API
- Official, but old-school PHP SDK
- https://github.com/nathanjdunn/chargebee-php-sdk
- Modelled from https://github.com/m4tthumphrey/php-gitlab-api
Chargebee SDK Usage
use NathanDunn\Chargebee\Client;
$client = new Client('nathandunn-test', 'test_bv6UzHREYaCNJQLqzdNzILQ0450o2dyS');
$sub = $client->subscription()->list();
// Response
array(1) {
'list' =>
array(1) {
[0] =>
array(2) {
'subscription' =>
array(22) {
...
}
'customer' =>
array(21) {
...
}
}
}
}Swapping Out Instances
use NathanDunn\Chargebee\HttpClient\Builder;
use Http\Mock\Client as MockClient;
use NathanDunn\Chargebee\Client;
$builder = new Builder(
'test_bv6UzHREYaCNJQLqzdNzILQ0450o2dyS', new MockClient
);
$client = new Client(
'nathandunn-test', 'test_bv6UzHREYaCNJQLqzdNzILQ0450o2dyS', $builder
);
$sub = $client->subscription()->list();Plugins
- Allows you to execute logic before/after a request
- You can also manipulate a request before it gets sent
- HTTPlug have 'official' plugins and can be installed via Composer
- If you want to do something really custom, you can create your own. Just create a class and implement the Http\Client\Common\Plugin interface.
Plugins
- Allows you to execute logic before/after a request
- You can also manipulate a request before it gets sent
- HTTPlug have 'official' plugins and can be installed via Composer
- If you want to do something really custom, you can create your own. Just create a class and implement the Http\Client\Common\Plugin interface.
Plugins - ErrorPlugin
- The ErrorPlugin transforms responses with HTTP error status codes into exceptions:
- 400-499 status code are transformed into Http\Client\Common\Exception\ClientErrorException;
- 500-599 status code are transformed into Http\Client\Common\Exception\ServerErrorException
Plugins - Usage
// Builder.php
/**
* @return HttpMethodsClient
*/
public function getHttpClient(): HttpMethodsClient
{
$this->httpMethodsClient = new HttpMethodsClient(
new PluginClient($this->httpClient, [new ErrorPlugin]),
$this->requestFactory
);
return $this->httpMethodsClient;
}Authentication
- Authentication works in exactly the same way as a plugin
- HTTPlug provides several out-of-box auth types
- Basic Auth
- Bearer
- WSSE
Authentication - Usage
// Builder.php
/**
* @return AuthenticationPlugin
*/
protected function getAuthenticationPlugin(): AuthenticationPlugin
{
$authentication = new BasicAuth($this->key, null);
return new AuthenticationPlugin($authentication);
}Authentication - Usage
// Builder.php
/**
* @return HttpMethodsClient
*/
public function getHttpClient(): HttpMethodsClient
{
$authenticator = $this->getAuthenticationPlugin();
$this->httpMethodsClient = new HttpMethodsClient(
new PluginClient($this->httpClient, [$authenticator]),
$this->requestFactory
);
return $this->httpMethodsClient;
}Testing
// tests/Unit/Api/Addons/AddonTest.php
/** @test */
public function should_list_addons()
{
$expected = $this->getContent(
sprintf('%s/data/responses/addon_list.json', __DIR__)
);
$addon = $this->getApiMock();
$addon->expects($this->once())
->method('get')
->with('https://123456789.chargebee.com/api/v2/addons', [])
->will($this->returnValue($expected));
$this->assertEquals($expected, $addon->list());
}Testing
// Unit/Api/TestCase.php
protected function getApiMock(array $methods = [])
{
$httpClient = $this->getMockBuilder(HttpClient::class)
->setMethods(['sendRequest'])
->getMock();
$httpClient
->expects($this->any())
->method('sendRequest');
$builder = new Builder(self::$key, $httpClient);
$client = new Client(self::$site, self::$key, $builder);
return $this->getMockBuilder(Addon::class)
->setMethods(['get', 'post'])
->setConstructorArgs([$client])
->getMock();
}Any questions?

Building a PHP SDK with HTTPlug
By Nathan Dunn
Building a PHP SDK with HTTPlug
- 88