Writing HTTP clients
in PHP
David Buchmann
@dbu
@dbu
Márk Sági-Kazár
@sagikazarmark
@sagikazarmark
Introduction
- HTTPlug
- Client Library
- Symfony Integration
HTTP
Request / Response
- HTTP Server: Get request, reply with response.
- HTTP Client: Send request, get response.
PSR-7
Interfaces for Request & Response
Immutable, withX methods return a copy
$request = new GuzzleHttp\Psr7\Request();
$request = $request
->withMethod('POST')
->withUri($uri)
->withBody($body)
;
var_dump(json_decode($response->getBody()));
?
?
Server
PSR-7 flow
create request
send request
HTTP Message Factory
Interface for message factory
=> decouple from message implementation
$request = $requestFactory->createRequest(
$method,
$url,
$headers,
$body
);
$request->withHeader(...);
?
Server
PSR-7 flow
create request
send request
Message Factory
HTTPlug
Interface for HTTP clients
=> decouple from client implementation
$response = $client
->sendRequest($request);
Server
PSR-7 flow
create request
send request
HTTPlug
TODO API
Simplistic API for the tutorial
- List TODO items
- CRUD TODO items
- Swagger documentation at api.httplug.websc
Exercise: Simplistic Client
- In clients/trivial-skeleton/bin/client
- Bootstrap HTTPlug
- Create an item (hardcoded content)
- Then list all existing items
HTTPlug
Why?
- Do not couple code to message/client implementation
- Use guzzle 5 or guzzle 6
- Or something else
- Shared libraries: Do not force implementation on user
HTTPlug Plugins
- Client independent plugin system
- Only depends on PSR-7 request/response
- Plugins provided for:
- authentication
- caching
- logging
- generic header manipulations
- etc
HTTPlug Asynchronous
- AsyncClient
- Returns promise
$promise = $client->sendAsync($request);
$promise->then(
[$this, 'onSuccess'],
[$this, 'onFailure']
);
...
$promise->wait();
HTTPlug Discovery
- Find implementation for HTTPlug client, message factory and stream factory implementations
- Please allow to inject instance!
public function __construct(
HttpClient $client = null,
MessageFactory $messageFactory = null
) {
$this->client = $client ?:
HttpClientDiscovery::find();
$this->messageFactory = $messageFactory ?:
MessageFactoryDiscovery::find();
}
API Client Library
API client library
- API over API
- Custom or exactly the same domain model
API client library + HTTPlug
- Use HTTP as transport layer
- Don't force HTTP client implementation
- If you need plugins, provide a client factory
Transport: HTTP
Serialization: JSON/XML/etc
Error handling (HTTP Status)
Domain model
Error handling (Domain)
How does it work?
Composer in a library
- Require php-http/client-implementation
Virtual package provided by all HTTPlug compliant clients - Recommended php-http/message factory-implementation
Virtual package provided by php-http/message - Optionally php-http/discovery
- require-dev e.g. php-http/guzzle6-adapter to test the library
Composer in an application
- Require a client implementation (e.g. php-http/guzzle6-adapter)
- Require a message factory implementation (php-http/message)
Exercise 1: Configuration
- client/library-skeleton:
- src/HttpClientFactory.php
- src/TodoClient.php::__construct
Exercise 2: Get methods
- client/library-skeleton/src/TodoClient.php:
- getTodo
- getTodoPage
- Try using the command bin/get and bin/list
Exercise 3: Modify methods
- client/library-skeleton/src/TodoClient.php:
- createTodo
- updateTodo
- deleteTodo
- Try using the commands bin/
Testing
What to test?
- Domain operations
- Domain exceptions
- HTTP calls behind operations
- Domain models
Exercise 1: Test get methods
- client/library-skeleton/tests/TodoClientTest.php
Exercise 2: Test modify methods
- client/library-skeleton/tests/TodoClientTest.php
Symfony is a trademark of Fabien Potencier. All rights reserved.
Bundle for Library
- Configure services
- Integration code (form types, validation, ...)
HttplugBundle
- Configure clients
- Configure plugins
- Debug toolbar integration
Exercise 1: Symfony Bundle
- clients/symfony-skeleton
- src/Wsc/TodoBundle
- Resources/config/services.xml
- DependencyInjection/Configuration.php
- DependencyInjection/WscTodoExtension.php
Exercise 2: Symfony Application
- clients/symfony-skeleton
- src/AppBundle/Controller/DefaultController
- app/Resources/views/default/*
Outlook
PSR-15
HTTP factories
- Upcoming FIG standard for HTTP factories
- request, response, uri and stream body
<?php
$request = $factory->createReques('GET', '/api/user')
->withHeader('Authentication', 'Bearer token')
->WithHeader()
//...
;
PSR-?
HTTP Client
- Eventually, the client interface should be FIG standard
- Guzzle et al could simply implement that interface
API Client Generator
OpenAPI spec to PHP code
https://github.com/jolicode/jane-openapi
Puli
- Can be used as discovery strategy
- Allows to have your custom client discovered
{
"version": "1.0",
"name": "php-http/guzzle6-adapter",
"bindings": {
"04b5a002-71a8-473d-a8df-75671551b84a": {
"_class": "Puli\\Discovery\\Binding\\ClassBinding",
"class": "Http\\Adapter\\Guzzle6\\Client",
"type": "Http\\Client\\HttpClient"
}
}
}
Get help
- Homepage: http://httplug.io
- Documentation: http://docs.php-http.org
- Slack: php-http
- Twitter: @
They already use HTTPlug
Thank you!
We are glad for any feedback!
Thank you!
Writing HTTP clients in PHP
By David Buchmann
Writing HTTP clients in PHP
PSR-7 defines common interfaces for HTTP requests and responses but the libraries that need to send HTTP requests need more than that. HTTPlug defines an interface for HTTP clients and provides adapters for existing clients like Guzzle, as well as a plugin system operating directly on PSR-7. This tool chain allows building API clients that do not depend on a specific HTTP client at all.
- 5,172