A type-safe HTTP client for PHP.
This is a PHP port of square/retrofit, under Thulium umbrella.
Retrofit requires PHP >=8.2
composer require thulium/retrofit-php-core
Please make sure you also install an HTTP client implementation.
HTTP clients:
composer require thulium/retrofit-php-client-guzzle7
To handle more advanced request and responses install a converter.
Converters:
composer require thulium/retrofit-php-converter-symfony-serializer
Retrofit turns your PHP interface into an HTTP API.
interface GitHubService
{
#[GET('/users/{user}/repos')]
#[ResponseBody('array', 'Repo')]
public function listRepos(#[Path('user')] string $user): Call;
}
The Retrofit
class generates an implementation of the GitHubService
interface.
$retrofit = Retrofit::Builder()
->baseUrl('https://api.github.com')
->client(new Guzzle7HttpClient(new Client()))
->addConverterFactory(new SymfonySerializerConverterFactory(new Serializer()))
->build();
$service = $retrofit->create(GitHubService::class);
Each Call
from the created GitHubService
can make a synchronous or asynchronous HTTP request to the remote
webserver.
$call = $service->listRepos('octocat);
// synchronous request
$call->execute();
// asynchronous request
$callback = new class () implements Callback {
public function onResponse(Call $call, Response $response): void
{
}
public function onFailure(Call $call, Throwable $t): void
{
}
};
$call->enqueue($callback);
$call->wait();
Attributes on the interface methods and its parameters indicate how a request will be handled.
Every method must have an HTTP attribute that provides the request method and path. There are eight built-in
attributes: HTTP
, GET
, POST
, PUT
, PATCH
, DELETE
, OPTIONS
and HEAD
. The path of the resource is specified
in the attribute.
#[GET('/users/list')]
You can also specify query parameters in the URL.
#[GET('/users/list?sort=desc')]
A request URL can be updated dynamically using replacement blocks and parameters on the method. A replacement block is
an alphanumeric string surrounded by {
and }
. A corresponding parameter must hava attribute #[Path]
using the same
string.
#[GET('/group/{id}/users')]
#[ResponseBody('array', 'User')]
public function groupList(#[Path('id')] int $groupId): Call;
Query parameters can also be added.
#[GET('/group/{id}/users')]
#[ResponseBody('array', 'User')]
public function groupList(#[Path('id')] int $groupId, #[Query('sort')] string $sort): Call;
For complex query parameter combinations an array map can be used.
#[GET('/group/{id}/users')]
#[ResponseBody('array', 'User')]
public function groupList(#[Path('id')] int $groupId, #[QueryMap] string $options): Call;
An object can be specified for use as an HTTP request body with the #[Body]
attribute.
#[POST('/users/new')]
#[ResponseBody('User')]
public function createUser(#[Body] User $user): Call;
The object will also be converted using a converter specified on the Retrofit
instance. If no converter is added,
the build-in will be used.
Methods can also be declared to send form-encoded and Multipart data.
Form-encoded data is sent when #[FormUrlEncoded]
is present on the method. Each key-value pair has attribute with
#[Field]
containing the name and the object providing the value.
#[FormUrlEncoded]
#[POST('/user/edit')]
#[ResponseBody('User')]
public function updateUser(#[Field('first_name')] string $first, #[Field('last_name')] string $last): Call;
Multipart requests are used when #[Multipart]
is present on the method. Parts are declared using the #[Part]
attribute.
#[Multipart]
#[PUT('/user/photo')]
#[ResponseBody('User')]
public function updateUser(#[Part] PartInterface $photo, #[Part('description')] string $description): Call;
Multipart parts use one of Retrofit
's converters, or they can implement PartInterface
to handle their own
serialization.
You can set static headers for a method using the #[Headers]
attribute.
#[Headers(['Cache-Control' => 'max-age=640000')]
#[GET('/widget/list')]
#[ResponseBody('array', 'Widget')]
public function widgetList(): Call;
#[Headers([
'Accept' => 'application/vnd.github.v3.full+json',
'User-Agent' => 'Retrofit-Sample-App'
])]
#[GET('/users/{username}')]
#[ResponseBody('User')]
public function getUser(#[Path('username')] string $username): Call;
A request header can be updated dynamically using the #[Header]
attribute. A corresponding parameter must be provided
to the #[Header]
. If the value is null
, the header will be omitted.
#[GET('/user')]
#[ResponseBody('User')]
public function getUser(#[Header('Authorization')] string $authorization): Call;
Similar to query parameters, for complex header combinations, an array map can be used.
#[GET('/user')]
#[ResponseBody('User')]
public function getUser(#[HeaderMap] array $headers): Call;